PR

PostgreSQLバックアップが空・別DBでも成功扱い?XServer VPSで自動restore-test

【PR】この記事にはA8.netのアフィリエイト広告を含みます。

先に結論:バックアップjobpg_dumpが成功しても、そのfileから必要なデータが戻るとは限りません。XServer VPSで正常・空DB・別DB・50%切断の4種類を実際にrestoreすると、空DBと別DBpg_restore終了コード0でした。それでもmarker・行数・内容hashまで検査する自動restore-testなら、正常だけを通して4/4件を正しく判定できました。

バックアップが成功ならrestoreできる?

成功終了だけでは判断できません。backup commandの成功は「そのprocessがエラー終了しなかった」という確認です。「対象DBが正しい」「必要なtabledataが入っている」「archive末尾まで壊れていない」という確認ではありません。

悩みの起点にしたRedditDoes anyone actually test their restores?」では、cron logCompletedなのにPostgreSQL dumpが実質空だった体験と、使い捨てcontainerへ復元して内容を検査する方法が議論されています。

PostgreSQL公式ではpg_dumpとpg_restoreをどう説明している?

PostgreSQL 17のpg_dump公式docsによると、pg_dumpは1つのdatabasebackupするutilityです。custom format-Fcpg_restoreで調査・選択・復元でき、圧縮も標準で有効です。

pg_restore公式docs--exit-on-errorは、SQL送信中のerrorで終了させるoptionです。正常終了しても、そのdumpが読者の期待するdatabaseかどうかまでは判定しません。

また公式docsは、restore先でdump sourcesuperuserが選んだcodeを実行し得ると警告しています。自動restore-testへ入れるのは、自分のsystemが生成した信頼済みdumpに限定します。

XServer VPSでどんな実験をした?

項目 実測条件
VPS XServer VPS、4 vCPU4GB 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なし、DBtmpfs
上限 1 CPU384MiB RAMPID 160

使い捨てPostgreSQL内へ3つのsource databaseを作りました。正常DBは既知data、空DBobjectなし、別DB正常と同じschema・同じ1000行・同じsequenceですが、中身だけを変えています。

[PR]
PostgreSQLの自動検証を試せるVPSなら『XServer VPS』

自動restore-testは何を確認した?

  1. pg_restore --listarchiveTOCを読めるか。
  2. 毎回新しい使い捨てDBを作る。
  3. pg_restore --exit-on-errorで実際に復元する。
  4. 必須tablesequenceSQLを実行する。
  5. dataset markermanifest行数、schema versionを照合する。
  6. 実行row数、金額合計、全行MD5sequence値を照合する。
  7. 全項目が一致した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件が期待判定と一致し、異常dumpfalse acceptは0件、正常dumpfalse rejectも0件でした。

空のバックアップはsizeexit codeで見抜けた?

見抜けませんでした。DBcustom archiveにも857 byteあり、SHA-256を計算できました。pg_restore --listは終了コード0、空のrestore先へのpg_restoreも終了コード0です。

backup_manifestresearch_jobsSQLを実行したところ、relationが存在せず拒否できました。「restore終了コード0」を最終成功条件にすると、このcaseは誤って合格します。

DBdumpは行数だけで見抜けた?

行数だけでは見抜けません。DBにも同名table、1000行、schema version 1sequence 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 --listTOC 11件を表示して終了コード0でした。実restoreを行った段階で終了コード1になりました。

この実体験から、pipelinearchive preflightは高速な早期rejectには使えますが、復元testの代わりにはなりません。

正常dumpは何を基準に合格した?

正常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にします。

実験中に分かりにくかった点は?

最も紛らわしかったのは、DBrestoreが成功することです。空のdatabaseを空のdatabaseへ正しく再現したため、utilityとしては成功です。しかしbackup利用者が求めていた「1000行のsystem dataを戻す」という目的には失敗しています。

もう1点は、50%切断でもTOCを読めたことです。custom archiveは先頭側に一覧情報を持つため、list成功だけでdata section全体を読めたとは限りません。検査stageごとの意味を混同しないことが重要でした。

CPURAM・ストレージへの影響は?

これは1000行の小さな合成DBです。本番sizeindexextensionI/O速度によりrestore時間と必要容量は増えます。4GB planで大きなdumpを検査する場合は、productionmemory余白とdisk容量からtest上限を先に決めます。

本番サービスと後片付けは?

公開/health/readyは開始前、実験中、終了後の3回すべてHTTP 200で、database=okartifact_storage=okを維持しました。

本番CaddyFastAPIPostgreSQLは前後restart 0で状態一致です。test containertmpfs 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の自動restorecode実行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、暗号化、roletablespaceextensionPITR、大容量DBRTOは別に検証します。restore drillの最後に「何を検証していないか」も出力すると、合格判定の過大解釈を防げます。

よくある質問

Q. pg_dumpが終了コード0ならバックアップ成功ですか?

A. file生成jobとしては成功でも、対象DB・必要tabledata内容が正しいとは限りません。復元後のSQL検査が必要です。

Q. バックアップsizeが0 byteでなければ十分ですか?

A. 不十分です。今回の空DB dumpは857 byteあり、listrestoreの両方が終了コード0でした。

Q. pg_restore –listが通れば破損していませんか?

A. 断定できません。50%に切ったdumpでもlistは終了コード0でしたが、実restoreは終了コード1でした。

Q. row countを確認すれば別DBを検知できますか?

A. 同じ1000行の別DBは通りました。dataset markeraggregatecontent hash等も組み合わせます。

Q. restore-testは本番DBで行ってよいですか?

A. 避けます。毎回新しい一時DBまたは使い捨てcontainerrestoreし、検査後に削除します。

Q. XServer VPS 4GBで定期実行できますか?

A. 今回の1000行ではpeak 144.14MiB、総時間4.565秒でした。実DB sizeに合わせたresource limitproduction余白が必要です。

[PR]
XServer VPSの公式情報を確認する

実験日:2026年9月2日。数値は筆者が契約中のXServer VPS 4GB環境で、1000行の合成dataを使った一次実測です。PostgreSQL versionDB sizestorageextension、負荷により結果は変わります。

コメント

タイトルとURLをコピーしました