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

複製は誤りも複製する — レプリカはバックアップの代わりにならない

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

本番のデータを複数の場所に持つ仕組みが、何から守り、何から守らないのかを分けて見ます。

まず結論

レプリカは主の変更記録をそのまま再生するので、誤った DELETE も同じように再生します。守れるのは機械や拠点を失う故障で、誤操作や不具合で消えた行は守れません。

適用をわざと遅らせる遅延スタンバイは猶予を作りますが、本教材の実演で救えたのは、猶予の間に適用を止めて読み出したときだけでした。そのまま昇格させると、誤りごと本番になりました。

? 読む前に予測

主で誤って全行を DELETE しました。適用を30秒遅らせたスタンバイにはまだ100件残っています。このスタンバイを数秒後にそのまま昇格させて新しい主にすると、昇格直後の件数はどうなったでしょうか(PostgreSQL 18.6 の実演)。

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

抽象原理

レプリカは、主が何をしたかを区別せずに再生する

冗長化で単一障害点をなくし、壊れたら予備へ引き継がせる。この考え方は正しく、主の機械が止まってもレプリカがあればサービスを続けられます。

問題は、予備が「何を」引き継ぐかです。PostgreSQL の文書はスタンバイの動きを次のように書きます。

By default, a standby server restores WAL records from the sending server as soon as possible.

WAL は第6期で扱ったとおり、何をどう書き換えたかの記録です。そこには「正しい更新」と「誤った DELETE」の区別がありません。スタンバイは届いた記録を順に再生するだけなので、誤りも正しい変更と同じ速さで複製されます。

SRE Book 第26章はこれを一文で言います。

In designing a data integrity program, it’s important to recognize that replication and redundancy are not recoverability.

少し後の段落は、自動で同期するデータストアでは壊れた行や誤った削除が全ての複製へ押し出される(likely before you can isolate the problem)と書いています。

複製が守る故障と、守らない故障
主で起きたこと正しい更新も誤った DELETE も同じ WAL
WAL を受け取り、順に再生する
即時スタンバイ数秒以内に同じ状態
遅延スタンバイ猶予の後に同じ状態
守れるかどうか
主の機械・拠点を失う予備に引き継げる
誤操作・不具合で消える予備にも同じく届く
  • ・遅延は「いつ届くか」を変えるだけで、「届くかどうか」は変えない
具体実物

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

Docker 上の PostgreSQL 18.6 で、主1台とスタンバイ2台を立てました。1台は既定のまま、もう1台は recovery_min_apply_delay を30秒にしています(複製の認証は実演用に省いており、設定例ではありません)。主に100行入れてから全行を DELETE し、数秒後に各台で件数を数えました。

主で DELETE した後の件数(1回目。2回目も同じ出力)コマンド出力
=== replica.sh run 1 2026-09-26 09:29:15
primary: 0
standby(即時): 0
standby(30s遅延): 100
30秒後 standby(30s遅延): 0

即時のスタンバイは、数えた時点ですでに0件です。届くまでの時間は測っていないので、言えるのは「この環境で試した回はいずれも数秒以内に届いた」までです。SRE Book も、DB の層では1つのトランザクションの複製は on the order of seconds かかりうると書いています。

遅延スタンバイには100件残っています。ただし最後の行(実際は32秒待ってから数えた行)で0件になりました。猶予が過ぎれば、遅延スタンバイも即時と同じです。

✓ ここまでの確認

「主の障害に備えてレプリカを置いてあるので、誤って消した行もレプリカから取り戻せる」という考えは妥当ですか。

抽象原理

遅延は猶予を作るだけで、使い方は人が決める

第12期では複製の遅れを、書いた直後に読めない困りごととして扱いました。ここでは遅れをわざと作ります。文書はこの設定の目的をこう書きます。

It may be useful to have a time-delayed copy of the data, offering opportunities to correct data loss errors.

opportunities(機会)であって保証ではありません。遅延には条件が付いています。

  • 既定は0(The default is zero, adding no delay.)。設定しなければ猶予は無い。
  • 遅らせるのはコミットのレコードだけ(The delay occurs only on WAL records for transaction commits.)。
  • 昇格すると待たない(until the standby is promoted or triggered. After that the standby will end recovery without further waiting.)。

3つ目が落とし穴です。猶予の内に誤りに気づいても、スタンバイを主に切り替えるいつもの復旧手順を当てはめると、猶予は消えます。 救うには、昇格ではなく適用を止めることです。文書は停止関数について While recovery is paused, no further database changes are applied. と書き、同時に A request doesn't mean that recovery stops right away. と、止まったことは状態で確かめるよう求めています。

具体実物

止めた場合と、昇格させた場合

同じ構成で、DELETE の数秒後に遅延スタンバイの適用を止めました。

適用を止めた遅延スタンバイ(1回目。2回目も同じ出力)コマンド出力
=== run 1 2026-09-26 09:45:24
primary: 0
standby(即時): 0
standby(30s遅延): 100
standby(30s遅延) で pg_wal_replay_pause() を実行
32秒後 standby(30s遅延・停止中): 100 state=paused
32秒後 standby(即時): 0
pg_wal_replay_resume() を実行
再開3秒後 standby(30s遅延): 0

止めている間は、猶予を過ぎても100件です。この間に行を読み出せば取り戻せます。再開すると、3秒後には0件でした。止めている間に読み出さなければ、何も救えません。

次は、止めて paused を確かめてから昇格させた場合です。

paused を待ってから pg_promote() した遅延スタンバイ(1回目)追跡ログ
=== MODE paused-promote run 1 2026-09-26 09:49:34
primary: 0
standby(即時): 0
standby(30s遅延): 100
pause state after 26s: paused rows=100
pg_promote(): t
昇格後 standby(30s遅延だったもの): 0 in_recovery=f
2026-09-26 00:50:16.072 UTC [25] LOG: recovery has paused
2026-09-26 00:50:17.075 UTC [25] LOG: received promote request
2026-09-26 00:50:17.076 UTC [25] LOG: redo done at 0/4057288 system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 33.20 s
2026-09-26 00:50:17.080 UTC [25] LOG: selected new timeline ID: 2
2026-09-26 00:50:17.108 UTC [25] LOG: archive recovery complete

昇格の直前は100件、直後は0件です。redo done の位置まで適用を進めてから新しいタイムラインに移っています。昇格の仕方を「そのまま」「停止を要求した1秒後」「paused を待ってから」の3通り、各2回試して、6回とも0件でした。

文書には If a promotion is triggered while recovery is paused, the paused state ends and promotion continues. とあります。「昇格すると残りの WAL をすべて適用する」と明記した一文は確認できた範囲では見つかっておらず、ここは実測として受け取ってください。目標時点を指定して回復し直す手順は、この遅延スタンバイでは試していません。

✓ ここまでの確認

30秒遅延のスタンバイがあり、誤った DELETE に猶予の内に気づきました。本教材の実演で100件のまま残せた対処はどれですか。

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

即時スタンバイ

受け取りしだい適用します。実演では DELETE の数秒後に0件でした。主の機械を失ったときの引き継ぎ先としては働きますが、誤った削除から行を取り戻す場所にはなりません。

実演で数えた件数表
時点 件数
DELETE の数秒後 0
その32秒後 0
具体例 B

30秒の遅延スタンバイ

コミットの適用を30秒遅らせます。数秒後は100件でしたが、待てば0件、昇格させても0件でした。止めた間だけ100件が残り、そこから読み出せます。

実演で数えた件数表
時点・操作 件数
DELETE の数秒後 100
その32秒後 0
適用を止めて32秒後 100
昇格の直後 0

即時と遅延。守れる時間は違って見えますが、最後に行き着く状態に共通するものは何でしょうか。

「何もしなかったとき」に、それぞれ最終的に何件になったかを比べてください。

この章のまとめ

  • スタンバイは主の WAL を再生する。誤った DELETE も同じく届き、実演では数秒以内に0件。
  • 複製は機械や拠点の喪失から守る。replication and redundancy are not recoverability。誤操作からは守らない。
  • 遅延スタンバイは opportunities to correct data loss errors。既定は0で、猶予が過ぎれば即時と同じ。
  • 昇格すると遅延は効かない。実演では、止めてから昇格しても6回とも0件。
  • 猶予の内にやることは、適用を止め、paused を確かめ、読み出すこと。

自分の現場で確かめる

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

理解度チェック

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

問題 1 / 3正解: 0

SRE Book 第26章は、複製(replication)が守れるものとして何を挙げていますか。

用語集(3語)
スタンバイ(レプリカ)
主から WAL レコードを受け取って再生し、同じ内容を持ち続ける DB。既定では受け取りしだい適用する。
recovery_min_apply_delay
スタンバイがコミットの適用を指定時間だけ遅らせる PostgreSQL の設定。既定は0。遅らせたスタンバイを本教材では遅延スタンバイと呼ぶ。
昇格(promote)
スタンバイが回復を終え、書き込みを受け付ける主になること。遅延はこの時点で効かなくなる。