ゴシッププロトコルによる分散状態の伝播
1. 概要
A. 定義
ゴシッププロトコル(Gossip Protocol)とは、ノードが知っている状態・イベント・メンバーシップを少数のピアと交換し、受信したピアがさらに他のピアへ伝えることを繰り返して、分散システム全体へ情報を確率的に広げる通信方式である。
人が噂を数人に伝え、聞いた人がまた周囲へ伝える仕組みに似ているため、この名前が付いている。 完全なブロードキャストでなくても、ラウンドを重ねれば大部分のノードが同じ事実を知るようになる。 障害検知、クラスタメンバーシップ、設定メタデータ、キャッシュ無効化、イベント伝播に利用できる。
中央コーディネータや全ノード間の専用ブロードキャスト木を必要としないため、単一障害点と中央ボトルネックを避けやすい。 一方、すべてのノードが直ちに受信したことや、同じ順序で受信したことは保証しない。 したがって技術士は、収束時間、通信量、一時的な不一致、障害分離を一体として設計する。
B. 背景と必要性
中央状態サービスは小規模クラスタでは単純だが、その停止や処理能力が全体の上限になる。 全ノードが相互に送る方式も、ノード数の増加とともに接続数とメッセージ数が膨らむ。 ゴシップでは各ノードが少数のピアだけを選ぶため、負荷を分散できる。
分散状態は、ノードの参加・離脱、ネットワーク分断、異なる順序で到着する更新によって常に変化する。 すべての瞬間に同じ状態を要求するより、通信が回復した後に最終的に収束する設計が現実的な場合が多い。 ゴシップは最終収束のための伝播方式であり、トランザクション合意そのものの代替ではない。
C. 特徴
特徴は、分散性、確率的な拡散、冗長性、部分観測、最終収束である。 複数経路で同じ情報を送るため、一部のパケットが失われても別の経路が残る。 同じイベントが何度も届くことを前提に、状態更新は冪等であり、イベントIDまたはバージョンを持つ必要がある。
2. 動作モデル
A. 構成要素
必要な要素は、ノード識別子、ピア選択器、伝播する状態、イベントまたはバージョン、送信周期である。 再起動したプロセスが古いプロセスになりすまさないよう、識別子に世代番号を組み合わせる。 ピア選択器は健全なノードからfanout分を選び、ランダムまたはトポロジーを考慮した方式を使う。
Lamport時計、ベクトル時計、ハイブリッド論理時計、revision番号などをバージョンとして使える。 同時更新を検出する必要があるか、マージ関数があるかによって選択は変わる。 Last-Write-Winsが安全か、複数バージョンを保持するか、最終的な正本がどこにあるかを先に定義する。
B. 1ラウンド
ノードはローカルの変更を伝播キューへ入れる。 タイマーが発火すると、少数のピアを選択して状態要約またはイベント一覧を交換する。 受信側は処理済みバージョンを除外し、新情報を保存し、次ラウンドの候補へ入れる。
flowchart LR
A[ノードAの状態変更] --> Q[キュー化とバージョン付与]
Q --> S[fanout分のピア選択]
S --> B[ノードBと交換]
S --> C[ノードCと交換]
B --> M[重複除去・マージ]
C --> M
M --> R[ローカル状態更新]
R --> N[次ラウンド候補]
N --> S
小さなメンバーシップは全体を送ってもよいが、大きなイベント集合ではdigestやversion vectorで差分を先に確認する。 要約が一致しない場合だけ不足項目を取得すれば、通常時の通信量を減らせる。 要約には衝突やfalse positiveの可能性があるため、最終同期では正確な検証を行う。
C. Push・Pull・Push-Pull
Pushは新情報を持つノードが能動的に送る方式で、遅延が小さい反面、重複送信が増える。 Pullはピアの要約を問い合わせて不足情報を取得する方式で、大きな状態に適するがポーリング遅延がある。 Push-Pullは要約を交換し、双方の不足分を補う方式である。
| 方式 | 長所 | 短所 | 適用 |
|---|---|---|---|
| Push | 検知遅延が小さい | 重複と受信負荷 | 小さい緊急イベント |
| Pull | 不足分だけ取得 | ポーリング遅延 | 大きな状態 |
| Push-Pull | 速度と修復の均衡 | 実装が複雑 | メンバーシップ |
3. ゴシップ型障害検知
A. 直接検知と間接検知
直接probeはpingを送り、応答がなければピアを障害とみなす。 ネットワーク遅延、GC停止、対象プロセスの高負荷も同じ症状を生むため、1回のtimeoutで直ちに除外してはいけない。 まずsuspect状態にし、別経路から再確認する。
間接probeでは、AがBへ到達できないとき、CへBの確認を依頼する。 CがBへ到達できれば、AとBの間の経路障害である可能性が高い。 複数の独立経路で失敗すれば、疑いの信頼度が上がる。
B. SWIM型の状態遷移
SWIM型は周期的probe、間接probe、suspect情報のゴシップ伝播を組み合わせる。 対象を直接確認し、失敗した場合は他ノードに間接確認を依頼する。 確認できなければsuspectを伝播し、期限内にalive証明を受ければ疑いを取り消す。
sequenceDiagram
participant A as 監視A
participant B as 対象B
participant C as 間接確認C
participant G as クラスタ
A->>B: 直接probe
B--xA: 応答遅延または損失
A->>C: Bの確認を依頼
C->>B: 間接probe
C-->>A: 確認結果
A->>G: suspect(B)を伝播
G-->>B: 状態通知
B-->>G: alive証明またはdead確定
timeoutを短くすればよいとは限らない。 短いtimeoutは検知を速めるが誤検知を増やし、長いtimeoutは誤検知を減らすが障害ノードへの送信を長く許す。 RTT分布、再試行、GC停止、ネットワーク区間を計測して調整する。
C. 世代番号
メンバーシップはalive、suspect、deadなどの状態で表現する。 dead後に再起動したプロセスへ古い遅延メッセージが届くと、新しい状態を壊す可能性がある。 incarnationまたはgeneration番号を使い、受信側は古い世代の情報を無視する。 deadを即時削除するか、復旧猶予期間を設けるかも運用方針として定義する。
4. 一貫性と競合処理
同じイベント集合を受けたノードが同じ結果になるよう、マージ関数は決定的でなければならない。 revision番号は単純な設定に有効だが、同時に行われた業務変更の意味を表せないことがある。 フィールド単位のマージ、複数バージョン保持、利用者確認、合意系ストレージが必要になる場合がある。 ゴシップは状態を運ぶ方式であり、万能の競合解決器ではない。
重複を避けるためイベントIDとdeduplication cacheを使う。
activeに設定は冪等だが、残高を1増加は再処理で結果が変わる。
非冪等処理にはイベント台帳、sequence検査、CRDTカウンタ、トランザクション正本を組み合わせる。
ネットワーク分断中に双方が更新した場合、復旧後にイベントを交換してマージする。 到着した最後のイベントだけを採用すると、同時更新を失う可能性がある。 メンバーシップは一時的不一致を許容できても、残高や在庫はquorumや線形化可能な経路を必要とする。
5. 他方式との比較
中央ブロードキャストは順序を管理しやすいが、容量と復旧が中央部品に依存する。 合意ログは強い順序を提供するが、quorumとリーダ障害処理のコストを負う。 メッセージキューは永続化、再処理、producer-consumer間の配送を明確にする。 業務イベントをキュー、ヘルスとディスカバリをゴシップで扱うように共存させることもできる。
| 観点 | ゴシップ | 中央放送 | 合意ログ | メッセージキュー |
|---|---|---|---|---|
| 拡張 | ピア分散 | 中央容量に依存 | リーダとquorum | ブローカ群 |
| 一貫性 | 最終収束 | 方針依存 | 強い順序 | 配送方針 |
| 用途 | 状態とメンバーシップ | 設定通知 | 台帳・状態機械 | 業務イベント |
6. 適用事例
マイクロサービスのサービスディスカバリでは、アドレス、ポート、ゾーン、healthを伝播する。 クライアントはaliveとsuspectを区別し、suspectへの新規要求を控える。 キャッシュ無効化では全データではなく、商品IDとrevisionを持つinvalidateイベントを送る方法がある。 価格や在庫は即時性が重要なため、最終的に正本を再確認する経路を残す。
運用では、発生時刻、世代、hop数、最終転送時刻をイベントに記録する。 p50・p95伝播遅延、未到達率、重複率、suspect誤検知率を監視する。 hop数の増加や特定ピアへの集中は、fanout、ピア選択、周期の再調整を示す。
7. 発展とチューニング
fanoutを増やすと1ラウンドの到達率は上がるが、CPU、ネットワーク、重複も増える。 小さすぎるfanoutは収束の裾を長くする。 ノード数、損失率、通信予算ごとにシミュレーションや負荷試験を行う。
同じラックやゾーンだけからピアを選ぶと、共通障害で情報経路が消える。 ゾーンやリージョンを分散させつつ、コストの高い遠隔通信は一定割合に抑える。 大きな状態にはdigest、差分圧縮、anti-entropy、tombstone保持を使う。 削除マーカーを早く消すと、古いノードが削除済みデータを復活させる危険がある。
8. 考慮事項と示唆
A. 一貫性要求を分類する
許容遅延、損失許容度、競合復旧可能性をデータごとに整理する。 サービス一覧は最終収束でよくても、決済認可や在庫引当は強い経路を必要とする。
B. 障害検知を推定値として扱う
ネットワーク遅延で正常ノードがsuspectになる。 間接probe、再確認、quorum、猶予期間を破壊的自動化の前に置き、誤検知と見逃しを両方測定する。
C. 信頼境界を保護する
アドレス、バージョン、health情報を含むため、ピア認証、相互TLS、署名、再送防止、最小開示を適用する。 悪意あるノードに対して評判値だけに依存しない。
D. 可観測性と復旧性を確保する
イベントID、世代、hop、時刻、ピア選択結果を保存し、障害時の伝播経路を再構成できるようにする。 payloadをすべて保存できなければ、ハッシュ、要約、サンプルを残す。
E. 正本と境界を明確にする
状態伝播用ゴシップとトランザクション処理用経路を分離する。 再起動後の状態再構築、古いメッセージの廃棄、tombstoneの保持基準を運用文書に書く。
F. 最悪条件を試験する
平均遅延だけでなく、分断、再起動集中、パケット損失、バージョン不一致、リージョン障害を組み合わせて試験する。 「5秒以内に99%のノード」など、確率とパーセンタイルを含む目標で定義する。
9. 一言まとめ
ゴシップは少数ピアとの反復・冗長交換で中央ボトルネックなしに状態を広げるが、収束、重複、誤検知、競合を明示的に設計しなければならない。
一言まとめ: ゴシップは「少しずつ何度も伝え、最終的に全体へ届かせる」分散状態伝播であり、強い一貫性のデータとは経路を分ける。