このページの目次
故障モデル
ノードはクラッシュする可能性があり、通信不能になる可能性があり、嘘をつく可能性があります。Crash-Stop から Byzantine へ、故障モデルの每一次のアップグレードはプロトコル設計の難易度を2倍にします——そして、エンジニアリングにおいて「どのくらいのタイムアウト時間が故障とみなされるか」自体が正解のないトレードオフです:短すぎると生きているノードを誤検知し、長すぎるとシステムが空待ちになります。
故障の分類
Crash-Stop (Fail-Stop)
ノードの実行停止後、再起動しません。これは最も単純な故障モデルです——一度ノードがクラッシュすると、新しい出力を生成することは永久にありません。コンセンサスプロトコル(Paxos/Raft)はこのモデル下で簡潔な証明を持っています。
特徴:永続化は不要です(クラッシュ後、ノードはログの回復を必要としません)、状態はメモリに置くことができます。しかし、実務では真の crash-stop システムは極めて稀です——多くのシステムは crash-recovery を必要とします。
Crash-Recovery
ノードは停止しますが、ある期間(任意の時間)後に再起動して実行を再開します。回復には、永続ストレージ(WAL/journal)からクラッシュ前の状態をリプレイする必要があります。これは最も現実的な故障モデルです——Raft/etcd は crash-recovery を対象として設計されています。
回復後の重要な課題:再起動したノードは、ログが失われていないことを証明できなければなりません——つまり、ログが永続的に書き込まれ、クラッシュ前に確認されている必要があります。これが Raft が fsync をログエントリに対して実行してからリーダーに応答する理由です。
Byzantine (ビザンチン) 故障
ノードは任意の動作を行うことができます——偽メッセージの送信、選択的な応答、意図的な破壊を含みます。悪意のある攻撃だけでなく、メモリビットフリップ(宇宙線)、ファームウェアのバグ、ネットワークデバイスの誤った転送なども Byzantine 動作を引き起こす可能性があります。
Byzantine は crash よりもはるかに困難です:Paxos/Raft は f 個の crash を許容するために 2f+1 個のノードで十分ですが、PBFT(Practical Byzantine Fault Tolerance)は f 個の Byzantine ノードを許容するために 3f+1 個のノードを必要とします。
理由:Byzantine ノードは誠実なノードに対して異なる情報を伝えることができます——誠実なノードは「相手が嘘をついている」と「ネットワークがパケットをドロップした」ことを区別できません。PBFT の 3f+1 の下限は必要であることが証明されています。
実践的な分布
実際のシステムでは Byzantine 耐性はほとんど必要ありません——閉じたデータセンター/クラスター内では、crash-recovery がほとんどの故障をカバーします。Byzantine は主に以下の場合に現れます:
- ブロックチェーン/暗号通貨(ノードが互いを信頼しない)
- 組織間コラボレーション(共通の信頼ルートがない)
- 極めて重要なインフラストラクチャ(Spanner は TrueTime を使用して、特定の Byzantine 的な時刻攻撃を防いでいます)
ネットワークパーティション
パーティションはクラッシュとは異なります——ノードは稼働しており、クライアントリクエストを受け付けることができますが、他のノードとは通信できません。パーティションは CAP 定理の中核的な脅威です:パーティション発生時、あなたは整合性か可用性かのどちらかを選ばなければなりません。
対称パーティション vs 非対称パーティション
- 対称パーティション:クラスターが等しい2つの部分に分割され、それぞれがノードの過半数を持つ → (Raft を使用して) 過半数を持つ方の半分だけがリーダーを選出し、サービスを提供し続けることができる
- 非対称パーティション:単一のノードが他のすべてのノードから切断される → そのノードはリーダーになれず、ブレインシュラウド(脳分裂)も引き起こさない
Split-Brain (ブレインシュラウド)
マルチリーダーシステム + パーティション = 2つのパーティションがそれぞれリーダーを選出 → 両方が同時に書き込みを受け入れる → パーティション回復時に競合が発生する。古典的な解決策:
- Raft/Paxos: 常に過半数のパーティションのみがリーダーを持つことができる(数学的保証)
- システムレベルの防御:STONITH (Shoot The Other Node In The Head) → 古いマスターをフェンスするか、直接電源を切る
タイムアウト
分散システムの核心的な緊張関係:タイムアウトが短すぎると crash の誤検知(フォールスポジティブ)になり、タイムアウトが長すぎると故障検出が遅れ(利用不可のウィンドウが増加する)。
Raft の選挙タイムアウトはその典型です:150-300ms のランダム化により、異なる候補者が異なるタイムアウトを持つことを保証し(分割票を避けるため)、通常の RPC レイテンシをカバーするのに十分長い必要があります。
なぜ「正しい」タイムアウトがないのか
ネットワークレイテンシはガウス分布ではありません——長い裾(テールレイテンシ)があります。p99 レイテンシが 10ms でも、p99.9 は 100ms に跳ね上がる可能性があります。timeout=100ms に設定すると、0.1% のリクエストがタイムアウトと誤検知されます。timeout=1s に設定すると、故障検出の遅延は 1s になり、その間システムは利用できません。
この問題には理論的な解決策はなく、エンジニアリングのトレードオフのみが存在します。実務では、φ-accrual failure detector(05章参照)を使用して、二値判断ではなく疑念レベルを出力し——アプリケーション層に「どの程度の疑念がフェイルオーバーに値するか」を決定させます。
参考
- 論文: "Impossibility of Distributed Consensus with One Faulty Process" (FLP, 1985)
- 論文: "Practical Byzantine Fault Tolerance" (Castro & Liskov, 1999)
- 論文: "The φ Accrual Failure Detector" (Hayashibara et al, 2004)
Keywords: crash-stop, crash-recovery, Byzantine fault, network partition, split-brain, FLP, timeout, φ-accrual