
Summary
この文書の要点
- 論理削除中はファイルを残し、閲覧権限だけを止める。
- 物理削除予定時刻をDBに持たせる。
- DBのdeleted_at、保持期限、GCSオブジェクト状態を別々に持ち、同期ジョブで整合させる。
- 復元可能期間と物理削除期間を分け、利用者操作と運用ジョブの責務を分離する。
どこが設計の難所か
添付ファイル付きデータを論理削除した時、ファイルをすぐ消すと復元できません。残し続けると不要ファイルが増え、権限不備があると削除済みデータにアクセスできてしまいます。
ファイルはDBトランザクションに含められません。DB更新とGCS操作のどちらかが成功し、もう片方が失敗する可能性があります。これを前提に整合性を回復する仕組みが必要です。
アプリ上で削除したファイルがGCSに残ることも、GCSから消えたファイルがDB上では存在することもあります。ネットワーク失敗、手動操作、バッチ中断がある限り、DBとストレージの完全な同時更新は前提にできません。
境界をどう切るか
論理削除時には、DBの状態を削除済みにし、物理削除予定時刻を設定します。アプリは削除済み状態のファイルを配信しません。予定時刻を過ぎたものをジョブが物理削除し、結果を記録します。
設計では、利用者の削除操作はDB上の論理削除として確定し、物理削除は保持期間後のジョブに任せます。GCSオブジェクトにはprefixやmetadataで用途を持たせ、DBレコードと照合できるようにします。
実装で効く細部
ファイルメタデータには、GCSパス、所有レコード、状態、削除予定時刻、削除完了時刻を持たせます。孤児ファイル検出では、GCS一覧とDBメタデータを突合し、DBにないファイルを候補として出します。
PostgreSQLにはfilesテーブルでdeleted_at、purge_after、storage_key、storage_generationを持ちます。Cloud Run Jobsがpurge対象を取得し、GCS削除後にpurged_atを更新します。GCS側のgenerationを保存すると、同じkeyに別ファイルが再作成された場合の誤削除を防げます。
- 削除操作は即GCS削除ではなく、復元可能期間を持つ論理削除にする。
- GCS object generationを保存し、古い削除ジョブが新しいファイルを消さないようにする。
- DBにあるがGCSにない、GCSにあるがDBにない状態を検出する棚卸しジョブを用意する。
壊れ方を観測する
検証では、削除済みデータの閲覧禁止、復元時のファイル参照回復、物理削除後の復元不可、GCS削除失敗の再試行を確認します。大量ファイルでの一覧コストも見ます。
検証では、削除後復元、保持期限到達、GCS削除失敗、DB更新失敗、同じkeyの再利用を確認します。棚卸しジョブの結果をレポート化し、手動対応が必要な不整合を見えるようにします。
捨てた選択肢とトレードオフ
GCS側のライフサイクルルールだけで削除する方法もありますが、業務状態と理由を残すにはアプリ側の削除キューが有効です。コスト削減だけならGCSルール、業務監査が必要ならDB管理を併用します。
復元可能期間を長くすると安全ですが、ストレージコストと情報保持リスクが増えます。短くすると誤削除から戻しにくくなります。保持ポリシーは機能別、ファイル種別別に分けるのが現実的です。
現場に残す判断軸
GCSファイルとDBの削除は同時に成功するとは限りません。論理削除、配信停止、物理削除、孤児検出を分けることで、復元性と保管コストを両立できます。
GCSとDBの整合性は、単一トランザクションでは守れません。論理削除、物理削除、棚卸しを分けることで、ずれを前提にした堅い運用ができます。


