現在テスト中です: 完了後にGitHubのコードを公開する予定です。

Raftパラメータ

項目
ハートビート間隔500ms(cluster_heartbeat_ms)
無応答判定時間2000ms(cluster_election_timeout_ms) — ハートビートがこの時間途絶えたらリーダーが死亡したと判断する
実際に選挙が始まる区間1750〜2000ms — ノードごとにランダムにばらして同時立候補(スプリットボート)を防ぐ。最悪値が、設定した無応答判定時間そのものになる
リーダー障害時のクライアント停止実測で無応答判定時間 + 0.3〜0.5秒(デフォルトなら約2.3〜2.5秒) — 検知とリダイレクト1回分
ノード数3または5(設定読み込み時に強制 — それ以外は起動を拒否)
ノードIDcluster_peersリスト内での(cluster_selfの)位置 — そのためリストの順序はすべてのノードで同一である必要がある
ブートストラップ起動から500ms後、すべてのノードが同じメンバーシップで初期化される — すでに初期化済みのクラスタに参加する場合は無視される
ログストレージインメモリ(揮発性) — 再起動したノードは複製/スナップショットで復旧する
lease失効の検知リーダーが100msごとにチェックし、合意によってExpireをコミットする
waitタイムアウトの粒度待機タイムアウト(T)も同じ100ms周期で決定される — 最大100ms遅れて届くことがある
リーダー交代の通知リーダーが変わると、接続中のすべてのクライアントへLを即座に送信する

ハートビートと無応答判定時間の選び方

この2つは設定cluster_heartbeat_mscluster_election_timeout_msで調整します。無応答判定時間(T)は、そのままリーダー障害時に サービスが停止する時間です。逆に短すぎると、生きているリーダーを死亡と誤判定して不要な 選挙が起こります。

ハートビート / T検知区間リーダーkill時の最悪遅延(実測)推奨環境
100ms / 600ms450〜600ms約0.9秒同一ラック・同一AZで遅延が非常に安定している
100ms / 1000ms750〜1000ms約1.2秒同一AZ
250ms / 2500ms1875〜2500ms約2.8秒マルチAZ
500ms / 2000ms1750〜2000ms約2.4秒デフォルト — マルチAZでのバランス
500ms / 5000ms3750〜5000ms約5.3秒クロスリージョン・遅延が大きく振れる環境
  • 平常時の処理遅延はこれらの値と無関係です(実測でp50に変化なし) — 障害時の回復時間だけを 左右します。
  • フォロワーが死ぬ場合は、どの値でもクライアントへの影響はありません。上記の遅延が発生するのは リーダーが死んだときだけです。
  • 制約: cluster_election_timeout_msは最低600ms、かつハートビートの4倍以上である必要が あります。(負荷中のスケジューリング遅延1回がハートビート2回分をまるごと飲み込むことが 実測されています。)

なぜ3または5ノードなのか

コミットには過半数が必要です。偶数構成はコストが増えるだけで耐障害性は向上しない ため、サーバーはこれを拒否します。

ノード数過半数同時障害耐性
220 — 1台の障害でクラスタが停止する。シングルモードと変わらない
321
431 — 3ノードと同じで、コストだけが増える
532

障害時の動作まとめ

状況動作
リーダーでないノードへのクライアントリクエストM(リーダーアドレス)、リーダー不明ならE no_leader
リーダー障害無応答判定時間以内(デフォルトなら1750〜2000ms)に新リーダーを選出。その間のリクエストはE no_leaderを受け、クライアントが再試行する
リーダー交代中保留中の獲得リクエストはM/E no_leaderでクリアされ、クライアントは新リーダーへ再試行する
ノード再起動空の状態で起動 → ピアのログ複製/スナップショットで追いつく
過半数を喪失コミットが不可能になる → 書き込みが停止(安全優先)、過半数が復旧すれば自動的に再開

用語

  • クォーラム — 全ノードの半数を超える数(3台中2台、または5台中3台)。あらゆる決定に クォーラムの合意が必要なため、分断された2つのグループが互いに矛盾する決定を同時に コミットすることは決してありません。
  • 無応答判定時間(選挙タイムアウト) — フォロワーがハートビートを受け取らないまま リーダーの死亡と判断し、選挙を開始するまでの待機時間。同時に立候補が起きにくいよう、 ノードごとにランダム化されています。