Dev Study
← 出して、戻して、直す — 変更と復旧の実務
レッスン 8 / 12·約 9 分·第24期 出して、戻して、直す

RPO は、最後のコピーが届いた地点で決まる

リクエストがアプリケーションに届くまで
  1. ブラウザURL入力
  2. DNS名前→IP
  3. ルータ経路選択
  4. ロードバランサ振り分け
  5. Nginx受け口
  6. アプリ処理
  7. DB保存今ここ

このコースの芯は、壊れたときに打てる手は壊れる前に用意しておいた分しかない、です。今回は、戻せる地点が壊れる前のどの操作で決まっているかを見ます。

まず結論

ベースバックアップと WAL アーカイブを組めば、誤った DELETE の直前へ戻せます。ただし戻れるのは、閉じて外へ届いた WAL の末尾までです。

PostgreSQL のアーカイブは閉じたセグメントの単位でしか走りません。失いうるデータの幅は、ベースバックアップの間隔ではなく、最後のコピーが外へ届いた地点で決まります。

? 読む前に予測

WAL アーカイブ(archive_timeout=60)を設定した PostgreSQL 18 で、ベースバックアップ後に 50 行を足し、その時刻を目標にした直後に全行を DELETE しました。DELETE の約3秒後、目標時刻を指定して別のコンテナに復元すると、どうなったでしょうか。

当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。

抽象原理

戻せる地点は、外へ届いた WAL の末尾

第6期で見た WAL は、同じ機械のクラッシュ回復に使うログでした。これを別の場所へ運んでおくと、別の実体の上で好きな時刻まで再生できます。PostgreSQL 文書 §25.3 はこう書きます。

Thus, this technique supports point-in-time recovery: it is possible to restore the database to its state at any time since your base backup was taken.

条件は、ベースバックアップの開始まで遡る continuous sequence of archived WAL files があることです。問題は、WAL がいつ外へ出るかです。§19.5 は次のように書きます。

The archive_command or archive_library is only invoked for completed WAL segments.

WAL は既定で 16MB ずつのセグメントに分かれ(normally 16MB apiece)、アーカイブは閉じたセグメントにしか走りません。 書き込みが少なければ、コミット済みの変更が長くプライマリの中にしか無い状態が続きます。§25.3 は there could be a long delay between the completion of a transaction and its safe recording in archive storage と書いています。

戻せる地点の上限は、閉じて外へ届いた WAL の末尾
ベースバックアップ100 行の時点
閉じたセグメントだけが写される
アーカイブ済み WAL…01〜…03(外にある)
まだ閉じていない
書き込み中の WAL…04: 50 行追加と DELETE(プライマリの中だけ)
  • ・復元は外にあるものしか使えない。…04 が閉じて届くまで、50 行の追加には戻れない
  • ・セグメント名は本教材の実演のもの
具体実物

本教材の実演の出力(スクリプト非掲載)

PostgreSQL 18.6(Docker の postgres:18)で、archive_timeout=60 と cp で写す archive_command を設定しました。100 行を入れてベースバックアップ、50 行を足して時刻 T を記録し、1秒後に全行を DELETE。別のコンテナでベースバックアップとアーカイブから、T を目標に復元します。

DELETE の直後に pg_switch_wal() で書き込み中のセグメントを閉じた版では、2回とも 150 行が戻り、ログは recovery stopping before commit of transaction 755 でした。誤った DELETE のコミットの手前で止まっています。

では pg_switch_wal() を外すとどうなるか。約3秒後に同じ手順で復元した出力です(抜粋、各2回で同じ結果)。

MODE A: pg_switch_wal() なし・約3秒後・目標時刻つき(run 1 の抜粋)コマンド出力
restore target: 2026-09-26 00:32:20.478867+00
current WAL on primary: 000000010000000000000004
archive: 000000010000000000000001 000000010000000000000002 000000010000000000000003 000000010000000000000003.00000028.backup
pg_stat_archiver.last_archived_wal: 000000010000000000000003.00000028.backup
pg_ctl: could not start server
pg_ctl start exit=1
2026-09-26 00:32:25.676 UTC [33] FATAL: recovery ended before configured recovery target was reached

T の後の書き込みは …04 にあり、アーカイブの最新は …03.00000028.backup です。戻せる地点が少しずれたのではなく、復元が起動しませんでした。 §19.5 の記述どおりです(if a recovery target is configured but the archive recovery ends before the target is reached, the server will shut down with a fatal error.)。

pg_switch_wal() は実演を短くするための操作です。実運用の既定の流れではありません。

✓ ここまでの確認

実演の MODE A で、目標時刻つきの復元が起動しなかった直接の理由はどれですか。

抽象原理

失いうる幅を決めるのは、最後に外へ届いたコピー

NIST SP 800-34 Rev.1 は RPO をこう定義します。

The RPO represents the point in time, prior to a disruption or system outage, to which mission/business process data can be recovered (given the most recent backup copy of the data) after an outage.

NIST が前提にしているのは「最新のバックアップのコピー」までで、WAL アーカイブを論じているわけではありません。ここから先は PostgreSQL 文書と実演で支えます。WAL アーカイブでは、そのコピーが閉じたセグメントの単位で届きます。

これで両向きの誤りが見えます。

  • 「PITR なら任意の時点へ損失ゼロで戻れる」は誤り。届いていない WAL には戻れません(MODE A・B)。
  • 「ベースバックアップの間隔がそのまま損失の幅」も誤り。ベースバックアップは1回のまま、70秒待って …04 がアーカイブされた後は、DELETE の直前へ 150 行で戻れました(MODE C、2回)。

既存クイズの解説「RPOが短いほど頻繁なバックアップが必要になります」は正しい文です。WAL を使う構成では、その「頻繁さ」に WAL を外へ出す間隔が入ります。その上限を決めるのが archive_timeout です。

To limit how old unarchived data can be, you can set archive_timeout to force the server to switch to a new WAL segment file periodically.

§25.3 は archive_timeout settings of a minute or so are usually reasonable とし、短すぎるとアーカイブが膨らむと書きます。それより速く外へ出したいなら、§19.5 は You should consider using streaming replication, instead of archiving と続けます。

具体実物

マネージドの PITR では、届く間隔が文書に書いてある

Amazon RDS の文書は、ログを外へ送る間隔を明記しています。

RDS uploads transaction logs for DB instances to Amazon S3 every five minutes.

続けて、最新の戻せる時刻は AWS CLI の describe-db-instances が返す LatestRestorableTime で見るよう案内します。仕組みは同じで、戻せる地点の上限は、最後に S3 へ届いたログです。

違いは2つあります。送る間隔を利用者が archive_timeout で決めるのではなく、文書に書かれた値として受け取ること。復元が creating a new DB instance without modifying the source DB instance であること、つまり戻す先が新しい実体になることです(Restored DB instances are automatically associated with the default DB parameter and option groups.)。

「5分ごと」が実際の遅れとどう対応するか、エンジンによって違うかは確かめていません。「RDS の RPO は5分」とは書けません。 言えるのは、文書が5分ごとと書いていることと、実際の値は LatestRestorableTime で見られることまでです。

✓ ここまでの確認

Amazon RDS の PITR の文書から、確かに言えることはどれですか。

⇄ 2つを見比べる
具体例 A

自前の PostgreSQL の WAL アーカイブ

閉じたセグメントだけがアーカイブされます。書き込みが少なければ閉じるまで待つので、archive_timeout が届くまでの時間の上限を決めます。設定するのは利用者です。

何が上限を決めているか表
外へ出る単位 閉じた WAL セグメント(既定 16MB)
間隔の上限 archive_timeout(文書の目安は1分程度)
戻す先 実演では別のコンテナ
具体例 B

Amazon RDS の PITR

トランザクションログは5分ごとに S3 へ送られると文書にあります。戻せる最新時刻は LatestRestorableTime で確かめます。復元は新しいインスタンスを作ります。

何が上限を決めているか表
外へ出る単位 トランザクションログ
間隔の上限 文書の記載は5分ごと
戻す先 新しい DB インスタンス

設定を自分で持つ構成と、提供者に任せる構成。戻せる地点を決めているものは同じでしょうか。

「最後に外へ届いたのはいつか」を、それぞれどこで確かめられるかを考えてください。

この章のまとめ

  • PITR はベースバックアップに途切れない WAL の列を積んで戻す。pg_dump は起点にならない。
  • アーカイブは閉じたセグメントにしか走らない。閉じる前の変更はプライマリの中だけにある。
  • 届く前に目標時刻つきで戻すと FATAL で起動しない。目標なしなら黙ってベースバックアップの時点で起動する。
  • 損失の幅はベースバックアップの間隔ではなく、最後に外へ届いた WALで決まる。上限を決めるのは archive_timeout(目安1分程度)やストリーミング複製。
  • RDS は文書上ログを5分ごとに S3 へ送り、復元は新しいインスタンスを作る。RPO の値として断定はしない。

自分の現場で確かめる

読んだだけでは身につきません。手元の環境で実際に見てみてください。

理解度チェック

読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。

問題 1 / 3正解: 0

「毎晩の pg_dump を起点に WAL アーカイブを再生し、ダンプ以降の任意の時点へ戻す」という案は成り立ちますか。

用語集(4語)
WAL アーカイブ
閉じた WAL セグメントを別の場所へ写す仕組み。PostgreSQL ではセグメントは既定で 16MB で、アーカイブは閉じたセグメントに対してだけ走る。
archive_timeout
最後のセグメント切り替えから指定時間が経ち、何らかの活動があれば、新しいセグメントへ切り替える設定。公式文書は1分程度を妥当な目安とし、短すぎるとアーカイブが膨らむと書く。
PITR(ポイント・イン・タイム・リカバリ)
ベースバックアップに、そこから途切れない WAL の列を再生して、任意の時刻の状態へ戻す復元。戻せるのはクラスタ全体の単位。
RPO(NIST SP 800-34 Rev.1)
障害の前の、データを戻せる時点。本文の定義には `(given the most recent backup copy of the data)` の括弧書きがある。