大きな画面での利用をおすすめします — アニメーションと操作がより快適になります。
入力

メッセージから分散合意へ

クライアントのタイムアウトは、書き込み成功の後にも起こる。三サーバーの Raft サービスでは、リーダーが二つの永続コピーの証拠を得れば、三台目が遅れていても現在任期の命令をコミットして適用できる。その後に応答経路が壊れると、クライアントは成功を確認できない。バイト到着、永続化、コミット、適用、結果受信は別の出来事だ。

受領証は対応する段階だけを証明する。配送と保存、保存と合意による実行許可、許可と通知の到着は同じではない。どの確認も要求全体の完了だと読んではいけない。

保存とリーダーの認識は別

クライアント C は set x=7 を求める m42 を送る。S1 は選出済みリーダー、S2 と S3 はフォロワー。三台は索引 2 まで同じログを持ち、適用済みで x=0 から始める。

要求は確立済み TCP で進む。TCP ACK はバイト受信を示し、アプリの解析やディスク書き込みは示さない。その停止点では全永続ログがまだ 2 で終わる。TCP はアプリの下にある信頼できるバイト流だ。RFC 9293

S1 は命令を受け、現在任期 7 の索引 3 に 3/t7 を保存する。3 は位置、7 は選挙の任期で、サーバー番号や秒数ではない。AppendEntries でこの項目と一致する前置ログ 2/t6 を S2 に送り、S2 は永続化してから応答する。

二つのディスクに保存されても、S1 はまだ自分の一つしか知らない。応答が届いて初めて S2 を数えられる。現在任期の項目なら、既知の二永続コピーが多数となり、commitIndex を 3 に進められる。その後ログ順に適用し、lastApplied=3、x=7 にする。永続状態、コミット、適用は別々だ。Raft 論文の図 2 と §5.3

本サービスはリーダー適用後に成功を返す。S2 は 3/t7 を持っても commitIndex=2 の場合があり、後のハートビートで新境界を知ってからローカルでコミット、適用する。全フォロワーの適用を待つ必要はない。Raft Apply のライフサイクル

メッセージから合意までのステップ

既定は応答喪失、値 7 で、S3 は全体を通して遅れている。

  1. C が m42 を送り、S1 の TCP が受信して ACK を返す。C は転送の受領だけを知り、全ログはまだ 2。
  2. S1 が解析して 3/t7 を保存する。既知コピーは 1/3、commitIndex=2、lastApplied=2、x=0 はそのまま。
  3. AppendEntries は 3/t7、前置 2/t6、旧コミット境界 2 を運ぶ。S2 が保存し、二コピーになるが、S1 の認識は一つのまま。
  4. S2 の AppendEntries OK が届き、S1 は {S1, S2} の永続化を知る。二台の閾値を満たし、項目の任期も現在と一致する。S3 は不要だ。
  5. S1 は先に commitIndex=3 とし、アプリはまだ x=0。次の場面で lastApplied=3、x=7 にする。
  6. S1 は m42 の成功結果を作るが、配送前に接続が使えなくなり、期限まで戻らない。C はタイムアウトして結果不明。S1 のコミットと x=7 は残る。
  7. クライアント期限とは独立に、S1 が leaderCommit=3 を送る。S2 は commitIndex、lastApplied、x=7 の順に進む。S3 は古い前置ログと x=0 のまま。
  8. 最後に不確かなクライアントと、コミット・適用済みのサービスを並べる。C には成功を確認するアプリ応答がない。

七項目の成果物列では、保存項目が複製へ、多数の証拠がコミットへ、コミット済み項目が適用へ渡る。最後には結果が実際にクライアントへ届く必要がある。最後の受け渡しが壊れても、前の成功は消えない。

タイムアウトで結果が不明になる理由

値 7 を保ち、シナリオだけを変える。

シナリオ今回観測できる最終サービス状態クライアントの証拠
正常応答S1、S2 が索引 3 を保存・適用し、S3 は遅れる成功受信、x=7
応答喪失正常時と同じサーバー状態履歴TCP ACK 後に期限切れ、アプリ結果不明
多数なしS1 だけが索引 3 を保存、全アプリは 2、x=0TCP ACK 後に期限切れ、アプリ結果不明

多数なしでは S2 への複製経路も使えず、S1 は二つ目を得られない。未コミット項目で終わることは観測者には見えるが、クライアントが知るのは応答待ちの期限切れだけ。応答喪失でも同じ観測なのに、命令は実行済みだ。

タイムアウトは呼び出し側の待機方針で、サーバーへのロールバック指示ではない。未コミット項目は後でコミットされることも、復旧で置き換わることもあり、本レッスンは将来を決めない。

再試行にはアプリ契約が必要だ。ここは set x=7 だが、increment x なら、最初が実行され応答を失った後に新しいログ項目を送ると二回増えうる。相関 ID だけでは重複排除にならない。Raft はログを順序付け、重複抑止には追加の要求 ID と結果追跡が必要になる。Raft 論文 §8

呼び出し側は結果不明を保ち、API の状態照会や冪等な再試行契約に従う。合意だけで新しい送信が前の操作と同一になるわけではない。

境界ケース

  • 入力 0 なら set x=0 で見た目の値は変わらないが、lastApplied は 3 へ進む。命令は処理されている。
  • S3 は全シナリオで 2 に残る。多数の進行は同時適用を要しない。遅れた前置ログと、同じ索引に競合命令を適用することは違う。
  • S2 の応答が移動中なら、二ログに 3/t7 があっても S1 の証拠は一つ。未到着の返答を使えない。
  • 直接の多数コミットは現在リーダー任期の項目に関する。旧任期には追加の推論が必要で、条件を落として数だけコピーできない。
  • 応答経路は期限まで接続不能にする。一 TCP セグメントの損失を永久喪失と扱ってはいない。通常の損失には再送がある。
  • 入力は −99〜99 の整数で、ラベルの表示制限であり Raft の限界ではない。

よくある間違い

  • 全確認を同じに呼ぶ。TCP ACK はバイト受信、AppendEntries OK は対応ログの永続保存、アプリ応答はクライアントへの結果確認だ。
  • クライアントをレプリカに数える。多数は S1、S2、S3 の中で数え、C の ACK はログコピーを増やさない。
  • 一ローカル追加で成功を返す。多数なしは実ディスクにあっても未コミットの例だ。
  • 保存しただけで適用する。S2 はコミット境界を知るまで待つ。
  • 全サーバーを同時に完了表示する。各台が独立に知り、適用する。後の通知には最初の RPC になかったコミット情報がある。
  • タイムアウトを未実行と読む。応答喪失でも S1 の lastApplied=3 で、返信不足は実行を取り消さない。

モデルの範囲

三台の非ビザンチンサーバー、選出済みリーダー、一致した初期ログ、一回の現在任期書き込みを固定する。接続と TCP 回復はまとめ、選挙、通常心拍、永続化遅延、バッチ、並べ替え、リーダー交代、旧任期コミット、読み取り、再試行・重複排除、スナップショット、構成変更は省く。メッセージは順に配置し、再生秒数は通信測定ではない。応答はリーダー適用を待つが、別 API は異なる完了契約を持てる。選挙とログ修復は Raft 合意、接続は TCP ハンドシェイク へ続く。