【PR】この記事にはA8.netのアフィリエイト広告を含みます。
先に結論:バックアップjobやpg_dumpが成功しても、そのfileから必要なデータが戻るとは限りません。XServer VPSで正常・空DB・別DB・50%切断の4種類を実際にrestoreすると、空DBと別DBはpg_restore終了コード0でした。それでもmarker・行数・内容hashまで検査する自動restore-testなら、正常だけを通して4/4件を正しく判定できました。
- バックアップが成功ならrestoreできる?
- PostgreSQL公式ではpg_dumpとpg_restoreをどう説明している?
- XServer VPSでどんな実験をした?
- 自動restore-testは何を確認した?
- 正常・空・別DB・切断の結果は?
- 空のバックアップはsizeとexit codeで見抜けた?
- 別DBのdumpは行数だけで見抜けた?
- 途中で切れたdumpはpg_restore –listで分かった?
- 正常dumpは何を基準に合格した?
- cronへ組み込むならどんな構成がよい?
- 実験中に分かりにくかった点は?
- CPU・RAM・ストレージへの影響は?
- 本番サービスと後片付けは?
- 初心者が迷いそうなポイントは?
- XServer VPSでこの悩みはどこまで解決した?
- よくある質問
- 最新条件の確認先
バックアップが成功ならrestoreできる?
成功終了だけでは判断できません。backup commandの成功は「そのprocessがエラー終了しなかった」という確認です。「対象DBが正しい」「必要なtableとdataが入っている」「archive末尾まで壊れていない」という確認ではありません。
悩みの起点にしたReddit「Does anyone actually test their restores?」では、cron logはCompletedなのにPostgreSQL dumpが実質空だった体験と、使い捨てcontainerへ復元して内容を検査する方法が議論されています。
PostgreSQL公式ではpg_dumpとpg_restoreをどう説明している?
PostgreSQL 17のpg_dump公式docsによると、pg_dumpは1つのdatabaseをbackupするutilityです。custom formatの-Fcはpg_restoreで調査・選択・復元でき、圧縮も標準で有効です。
pg_restore公式docsの--exit-on-errorは、SQL送信中のerrorで終了させるoptionです。正常終了しても、そのdumpが読者の期待するdatabaseかどうかまでは判定しません。
また公式docsは、restore先でdump sourceのsuperuserが選んだcodeを実行し得ると警告しています。自動restore-testへ入れるのは、自分のsystemが生成した信頼済みdumpに限定します。
XServer VPSでどんな実験をした?
| 項目 | 実測条件 |
|---|---|
| VPS | XServer VPS、4 vCPU、4GB RAM、150GB |
| OS | Ubuntu 26.04 |
| DB | postgres:17.10-alpine |
| dump | pg_dump -Fc custom format |
| case | 正常、空DB、別DB、正常dumpの50%切断 |
| data | 合成ジョブ1000行、本番data不使用 |
| 隔離 | networkなし、公開portなし、DBはtmpfs |
| 上限 | 1 CPU、384MiB RAM、PID 160 |
使い捨てPostgreSQL内へ3つのsource databaseを作りました。正常DBは既知data、空DBはobjectなし、別DBは正常と同じschema・同じ1000行・同じsequence値ですが、中身だけを変えています。
[PR]
PostgreSQLの自動検証を試せるVPSなら『XServer VPS』![]()
自動restore-testは何を確認した?
pg_restore --listでarchiveのTOCを読めるか。- 毎回新しい使い捨てDBを作る。
pg_restore --exit-on-errorで実際に復元する。- 必須tableとsequenceへSQLを実行する。
- dataset marker、manifest行数、schema versionを照合する。
- 実行row数、金額合計、全行MD5、sequence値を照合する。
- 全項目が一致したarchiveだけ受理する。
file存在やsizeだけで判定せず、復元後の意味を問い合わせる構成にしました。
正常・空・別DB・切断の結果は?
| case | 容量 | list | restore | 内容 | 判定 | 時間 |
|---|---|---|---|---|---|---|
| 正常 | 10,924 byte | 0 | 0 | 全一致 | 受理 | 559.179ms |
| 空DB | 857 byte | 0 | 0 | 必須tableなし | 拒否 | 522.191ms |
| 別DB | 10,947 byte | 0 | 0 | marker・合計・hash不一致 | 拒否 | 535.197ms |
| 50%切断 | 5,462 byte | 0 | 1 | restore失敗 | 拒否 | 334.423ms |
4/4件が期待判定と一致し、異常dumpのfalse acceptは0件、正常dumpのfalse rejectも0件でした。
空のバックアップはsizeとexit codeで見抜けた?
見抜けませんでした。空DBのcustom archiveにも857 byteあり、SHA-256を計算できました。pg_restore --listは終了コード0、空のrestore先へのpg_restoreも終了コード0です。
backup_manifestとresearch_jobsへSQLを実行したところ、relationが存在せず拒否できました。「restore終了コード0」を最終成功条件にすると、このcaseは誤って合格します。
別DBのdumpは行数だけで見抜けた?
行数だけでは見抜けません。別DBにも同名table、1000行、schema version 1、sequence last value 1000を用意しました。TOCも正常と同じ11 entryで、archive容量差は23 byteだけです。
| 検査 | 正常DB | 別DB | 判別 |
|---|---|---|---|
| row count | 1000 | 1000 | 不可 |
| sequence | 1000 | 1000 | 不可 |
| schema version | 1 | 1 | 不可 |
| dataset marker | exp32-primary-v1 | exp32-wrong-db-v1 | 可能 |
| price sum | 5,388,500 | 3,737,500 | 可能 |
| content MD5 | 4f53e0…c340 | f28c1a…d786 | 可能 |
接続先名を取り違える事故に備えるなら、row countの下限だけでなく、database固有markerや代表的なaggregate、再現可能なcontent hashを組み合わせます。
途中で切れたdumpはpg_restore –listで分かった?
一覧確認だけでは分かりませんでした。正常10,924 byteを50%の5,462 byteへ切ると、pg_restore --listはTOC 11件を表示して終了コード0でした。実restoreを行った段階で終了コード1になりました。
この実体験から、pipelineのarchive preflightは高速な早期rejectには使えますが、復元testの代わりにはなりません。
正常dumpは何を基準に合格した?
- dataset marker:
exp32-primary-v1 - manifest expected rows:1000
- schema version:1
- actual rows:1000
- price sum:5,388,500
- content MD5:
4f53e0a31503a1deed689b1abe13c340 - sequence last value:1000
正常caseだけが全指標へ一致しました。hashは暗号学的な改ざん証明ではなく、今回の合成datasetが同一かを比較するfingerprintです。
cronへ組み込むならどんな構成がよい?
backup job
└─ pg_dump -Fc
└─ archive size・checksum記録
└─ throwaway PostgreSQLを起動
└─ pg_restore --exit-on-error
└─ manifest・row・aggregate・hashをSQL検査
├─ 全一致 → backup accepted
└─ 1項目でも不一致 → alert + backup rejected
restore-test先には本番DB名を使わず、毎回一意なdatabaseまたは使い捨てcontainerを使います。終了後に削除し、productionへ上書きしないことをhard ruleにします。
実験中に分かりにくかった点は?
最も紛らわしかったのは、空DBのrestoreが成功することです。空のdatabaseを空のdatabaseへ正しく再現したため、utilityとしては成功です。しかしbackup利用者が求めていた「1000行のsystem dataを戻す」という目的には失敗しています。
もう1点は、50%切断でもTOCを読めたことです。custom archiveは先頭側に一覧情報を持つため、list成功だけでdata section全体を読めたとは限りません。検査stageごとの意味を混同しないことが重要でした。
CPU・RAM・ストレージへの影響は?
- PostgreSQL peak RAM:144.14MiB
- 終了直前RAM:136.58MiB
- CPU usage:2.774558秒
- host MemAvailable:約3270.60MiBから3205.17MiB
- root disk差:40,960 byte増
- PostgreSQL起動:1.068742秒
- 実験総時間:4.564568秒
これは1000行の小さな合成DBです。本番size、index、extension、I/O速度によりrestore時間と必要容量は増えます。4GB planで大きなdumpを検査する場合は、productionのmemory余白とdisk容量からtest上限を先に決めます。
本番サービスと後片付けは?
公開/health/readyは開始前、実験中、終了後の3回すべてHTTP 200で、database=ok、artifact_storage=okを維持しました。
本番Caddy、FastAPI、PostgreSQLは前後restart 0で状態一致です。test container、tmpfs DB、4種類の合成dumpを削除し、公開portと新規dangling volumeは0件でした。
初心者が迷いそうなポイントは?
pg_dump成功と、期待dataを含むことは別。- 0 byte検査だけでは「有効だが空のDB dump」を通す。
pg_restore --list成功だけでは末尾dataの完全性を証明しない。pg_restore成功だけでは別DBの取り違えを証明できない。- row countだけでは同じ規模の別DBを見逃す。
- restore-testは本番へ上書きせず、使い捨て先で行う。
- untrusted dumpの自動restoreはcode実行riskがある。
- 同一VPS内の成功をoff-site災害復旧成功と読み替えない。
XServer VPSでこの悩みはどこまで解決した?
VPS内の自動restore-testで解決可能です。正常・空DB・別DB・50%切断を同じXServer VPS上の使い捨てPostgreSQLへ復元し、内容まで確認することで正常だけを4/4件正しく受理できました。1000行規模なら実験全体は4.565秒、peak RAMは144.14MiBでした。
ただし、同一host内の論理backup検査です。別region消失、外部object storage、暗号化、role、tablespace、extension、PITR、大容量DBのRTOは別に検証します。restore drillの最後に「何を検証していないか」も出力すると、合格判定の過大解釈を防げます。
よくある質問
Q. pg_dumpが終了コード0ならバックアップ成功ですか?
A. file生成jobとしては成功でも、対象DB・必要table・data内容が正しいとは限りません。復元後のSQL検査が必要です。
Q. バックアップsizeが0 byteでなければ十分ですか?
A. 不十分です。今回の空DB dumpは857 byteあり、listとrestoreの両方が終了コード0でした。
Q. pg_restore –listが通れば破損していませんか?
A. 断定できません。50%に切ったdumpでもlistは終了コード0でしたが、実restoreは終了コード1でした。
Q. row countを確認すれば別DBを検知できますか?
A. 同じ1000行の別DBは通りました。dataset marker、aggregate、content hash等も組み合わせます。
Q. restore-testは本番DBで行ってよいですか?
A. 避けます。毎回新しい一時DBまたは使い捨てcontainerへrestoreし、検査後に削除します。
Q. XServer VPS 4GBで定期実行できますか?
A. 今回の1000行ではpeak 144.14MiB、総時間4.565秒でした。実DB sizeに合わせたresource limitとproduction余白が必要です。
実験日:2026年9月2日。数値は筆者が契約中のXServer VPS 4GB環境で、1000行の合成dataを使った一次実測です。PostgreSQL version、DB size、storage、extension、負荷により結果は変わります。

コメント