Mímisbrunnr知恵の泉

← 分散システム 一覧

🎓 レベル:発展 | 重要度:B(重要)

📎 前提:レプリケーション方式(同期/非同期・リーダー/リーダーレス) | 関連:PACELC・結果整合性障害モデル(クラッシュ・オミッション・ビザンチン)

要点(BLUF)

問題設定 ── 中央なしで「揃える・見つける」

クラスタの「誰が生きているか」「最新の設定は何か」を全ノードで共有したい。中央のレジストリに全員が問い合わせると、そこがボトルネック&単一障害点透過性とスケーラビリティ)。中央を置かずに、ノード同士のうわさ話だけで全体を揃え、故障も見つけたい——これがゴシップの動機です。

アルゴリズム ── ゴシップ拡散

flowchart LR
    A["ノードA(新情報を持つ)"] -->|"ランダムな相手へ"| B["ノードB"]
    B -->|"次のラウンドで拡散"| C["ノードC"]
    B -->|"並行に"| D["ノードD"]
    C --> E["…指数的に倍々で広がる"]

各ラウンドで、各ノードはランダムに選んだ数ノードに自分の知る情報を送る(push)/もらう(pull)。新情報を持つノードが毎ラウンドおよそ倍増し、感染症の流行のように全体へ広がります。

具体例 ── log N で全体収束(実機)

ラボ 06-05_gossip.py。1ノードが持つ新情報が、各ラウンドでランダムな相手に伝播。収束ラウンド数とノード数の関係:

== ゴシップ拡散の収束ラウンド数 (fanout=1) ==
 ノード数 N    収束ラウンド   log2(N) 参考
      10          6.5          3.3
     100         12.8          6.6
    1000         18.0         10.0
   10000         23.1         13.3

N=1000 の1試行での感染数の推移(ラウンドごと):
  [1, 2, 4, 8, 16, 32, 62, 117, 218, 368, 560, 754, 884, 948, 982, 991, 998, 1000]

ノード数が10倍になっても収束ラウンドは数ラウンド増えるだけ(10→約6.5、10000→約23)。感染数の推移 1,2,4,8,16,32,… が示す通り、初期は毎ラウンド倍増し、全体収束は O(log N)。中央サーバなしで対数時間に全ノードへ行き渡る——これがゴシップのスケーラビリティです。

故障検出 ── ハートビートから phi-accrual・SWIM へ

「ノードが死んだ」をどう判定するか。障害モデル(クラッシュ・オミッション・ビザンチン) の通り「遅い」と「死んだ」は区別できないので、確率的に疑う設計にします。

方式仕組み弱点
固定タイムアウト・ハートビートT秒来なければ死亡ネット揺らぎで誤検知多発
phi-accrual到着間隔の履歴分布から「今の遅延の疑わしさ」を連続値φで算出。閾値を用途別に調整計算がやや重い
SWIMランダムな相手をping、無応答なら他ノード経由で間接pingして確認。メンバー変化をゴシップで拡散実装が必要

SWIM(Consul/Serf が採用)は故障検出メンバーシップ拡散を統合し、間接pingで誤検知を減らし、負荷をクラスタ全体に均す優れた設計です。

正しさの観点 ── 確率的な保証

なぜ分散だと難しいか(直観)

「全員が同じ事実を知る」を中央なしで、しかも故障の中で達成するのが難所。ゴシップは厳密な即時一貫性を諦め、確率的な最終収束に振ることで、スケールと耐障害を得る。故障検出も「確実な判定」を諦め「疑いの度合い」に変えることで、誤検知と見逃しのバランスを運用で調整可能にしています。諦め方が上手いプロトコル群です。

⚠️ よくある誤解・落とし穴

対応ラボ

distributed-systems-study/labs/06-05_gossip.py(N=10〜10000 で収束ラウンドが O(log N)、感染数が倍増することを確認済み)。

関連

第6章 レプリケーションとパーティショニング 目次