Aurora PostgreSQLで30万件のレコードを誤削除してしまい、PITRで完全復旧した話と再発防止策
自己紹介
Kazu Tomiと申します
普段は、IT企業で、26歳クラウドエンジニア?開発の部署で主にAWSやAI部分を担当しております
下記記事は、Geminiで作成しております
はじめに
本番環境でのパッチ作業中に、クエリミスによって約30万件の重要なレコードを削除してしまうインシデントが発生しました。絶望的な状況から、Amazon Aurora PostgreSQLの**ポイントインタイムリカバリ(PITR)**を活用して、データ欠損もなく復旧させたプロセスと、そこから得た教訓を共有します。
当時の環境
決まったサーバーから決まったpgadminを使用することで、本番のDBにアクセスをしていました
ただ、この時の環境が本番DBに書き込みや読み取り両方とも実施できる環境であったことから、発生しました
インシデントの状況
発生したこと: 本番DBでの調査作業中、DELETEクエリの条件指定ミスにより、想定外のレコード(約30万件)が削除された。
環境: Amazon Aurora PostgreSQL
検知: 作業直後の件数確認、およびアプリケーションのエラー監視。
復旧戦略:なぜ「スナップショット」ではなく「PITR」か
復旧にあたり、以下の3つのステップを検討・実施しました。
-
定期スナップショット(前夜分)からの復元
- 結果: 復元は早いが、当日朝〜事故発生時までのデータが消失するため、完全復旧にならない。
-
事故直後のPITR
- 結果: 削除された後の状態を確認しただけで、当然データはない。
-
事故「直前」のPITR
- 結果: 削除が実行される直前の状態でDBを別インスタンスとして復元。これが成功し、完全復旧に至った。
復旧の鍵となった「クエリログ」の特定
PITRを成功させる最大のポイントは、「何時何分何秒に事故が起きたか」を正確に特定することです。 今回は、Auroraのクエリログを有効にしていたため、実行されたDELETE文のタイムスタンプをミリ秒単位で特定できました。これがなければ、復旧ポイントを探り当てるために膨大な試行錯誤が必要でした。
技術的な学びと再発防止策
今回のインシデントを経て、以下の仕組みを導入・徹底しました。
技術的な対策
PITRの有効期間設定: BackupRetentionPeriodが十分に設定されているか再確認。
クエリログの常時出力: 事故後の「何が起きたか」の特定には、詳細なログが不可欠。
トランザクションの徹底:
BEGIN;
-- 1. まずはSELECTで影響範囲(件数)を確認
SELECT count(*) FROM テーブル名 WHERE ...;
-- 2. 件数が正しいことを確認してから実行
DELETE FROM テーブル名 WHERE ...;
-- 3. 問題なければ確定、不安なら戻す
COMMIT; -- OR ROLLBACK;
プロセス・組織的な対策
-
本番作業のペアレビュー: 作業者と確認者の二名体制
- 「SELECT COUNT(*)」の義務化: UPDATE/DELETEを実行する前に、必ず同じWHERE句で対象件数を確認する。
-
リード専用権限の活用
- 調査目的であれば、更新権限のないユーザーを使用する
- AuroraPostgeSQLでライター・リーダー構成だったため、調査時は、リーダーのみのクラスターエンドポイントのみに接続したユーザーの作成
まとめ
DBの誤操作はエンジニアにとって最大の悪夢の一つですが、AWS AuroraのPITRは非常に強力な守護神になります。しかし、それに甘んじることなく、「事故を起こさせないプロセス」と「起きた時に即座に特定できるログ」の両輪を整えておくことの重要性を痛感しました。
最後に
最後に ここまで読んでくださりありがとうございました。
こちらの内容は、実際に発生してしまったインシデントを、機密に触れない範囲でぼかして記述しています。
私自身、本番環境に触れる機会が多い身として、この日のことは一生忘れられません。
本来であれば、復旧作業が深夜まで及んだ壮絶な舞台裏も共有したいところですが、それはまた別の機会に(笑)。 皆さんも、本番DBを触る際は「明日は我が身」と思って、二重三重の策を講じてください!