photoncloud-monorepo/chainfire/advice.md
centra 8f94aee1fa Fix R8: Convert submodule gitlinks to regular directories
- Remove gitlinks (160000 mode) for chainfire, flaredb, iam
- Add workspace contents as regular tracked files
- Update flake.nix to use simple paths instead of builtins.fetchGit

This resolves the nix build failure where submodule directories
appeared empty in the nix store.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 16:51:20 +09:00

6 KiB
Raw Blame History

RaftとGossipプロトコルを用いた、クラスター管理のための数万台までスケールするKey-Value Storeを書いてほしいです。

  • プログラミング言語rust
  • テストをちゃんと書きながら書くことを推奨する。
  • クラスターへの参加/削除/障害検知を行う。

では、「Raft合意形成」と「Gossip情報の拡散」を組み合わせた場合、具体的にどうデータが流れ、どうやってードが動き出すのか、その具体的なフローを解説します。


前提:このシステムの役割分担

  • Control Plane (CP): Raftで構成された3〜7台Raftアルゴリズムでうまく合意が取れる範囲のサーバー。情報の「正規の持ち主」。いなくなったら自動でWorker Nodesから昇格する。
  • Worker Nodes (VM/DB Hosts): 数百〜数千台の実働部隊。CPのクライアント。

1. データはどのように書き込まれるか? (Write)

書き込みは 「必ず Control Plane の Raft Leader に対して」 行います。Gossip経由での書き込みは順序保証がないため行いません。

「VM-A を Node-10 で起動したい」

  1. API Call: 管理者またはCLIが、CPのAPIサーバーにリクエストを送ります。
  2. Raft Log: CPのリーダーは、この変更を Put(Key="/nodes/node-10/tasks/vm-a", Value="START") としてRaftログに追加します。
  3. Commit: 過半数のCPードがログを保存したら「書き込み完了」と見なします。

ここまでは普通のDBと同じです。

2. 各ノードはどのようにデータを取得し、通知を受けるか? (Read & Notify)

ここが最大のポイントです。数千台のードが「自分宛ての命令はないか」と毎秒ポーリング問い合わせすると、CPがDDoS攻撃を受けたようにパンクします。

ここで 「Watchロングポーリング という仕組みを使います。

A. Watchによる通知と取得これがメイン

Kubernetesやetcdが採用している方式です。

  1. 接続維持: Node-10 は起動時に CP に対して Watch("/nodes/node-10/") というリクエストを送ります。
  2. 待機: CP は「Node-10 以下のキーに変更があるまで、レスポンスを返さずに接続を維持(ブロック)」します。
  3. イベント発火: 先ほどの書き込みVM起動命令が発生した瞬間、CP は待機していた Node-10 への接続を通じて「更新イベントEvent: PUT, Key: ...vm-a, Value: START」を即座にプッシュします。
  4. アクション: Node-10 は通知を受け取り次第、VMを起動します。

結論: 「書き込み後の通知」は絶対に必要です。それを効率よくやるのが Watch API です。


3. じゃあ Gossip はどこで使うのか?

「Raft + Watch」で完結しそうに見えますが、10台以上のスケール、特にVM基盤のような動的な環境では Gossip が以下の「3つの穴」を埋めるために不可欠 になります。

① Nodeの死活監視・インベントリ管理下り方向

CPが「Node-10にVMを立てたい」と思ったとき、「そもそもNode-10は生きているのか IPは 空きメモリは?」という情報を知る必要があります。

  • Gossipの役割: 各Worker Nodeは、GossipSWIMプロトコルでお互いに、そしてCPに対して「自分は生きてるよ、IPはこれだよ」と喋り続けます。
  • CPはこのGossip情報を聞いて、最新の「ード一覧リストMemberlist」をメモリ上に保持します。

② サービスのディスカバリ(横方向)

DB基盤の場合、「DBードA」が「DBードB」と通信したいことがあります。いちいちCPに聞きに行くと遅いです。

  • Gossipの役割: ード同士で「私はここにいるよ」とGossipし合うことで、CPを介さずに直接通信相手を見つけられます。

③ "Actual State"(現状)の報告(上り方向)

VMが起動した後、「起動しました」というステータスをどうCPに伝えるか。

  • Raftに書く: 確実ですが、頻繁に変わるステータスCPU使用率などを全部Raftに書くとCPがパンクします。
  • Gossipで流す: 「VM-Aは起動中、負荷50%」といった情報はGossipに乗せて、「結果的にCPに伝わればいい」 という扱いにします。

設計のまとめRaftとGossipの使い分け

これから作る「汎用管理DB外部依存型」は、以下のようなインターフェースを持つことになるでしょう。

アクション 通信方式 具体的なデータ例
命令 (Desired State)
「こうなってほしい」
Raft + Watch
(強整合性)
・VMの起動/停止命令
・DBのデータ配置情報の変更
・パスワードや設定変更
現状 (Actual State)
「今こうなってます」
Gossip
(結果整合性)
・ノードの生存確認 (Heartbeat)
・リソース使用率 (CPU/Mem)
・「VM起動完了」などのステータス
通知 (Notification) Watch (HTTP/gRPC Stream) ・「新しい命令が来たぞ!」というトリガー

実装のアドバイス

もし「etcdのようなもの」を自作されるなら、「Serf (Gossip)」と「Raft」をライブラリとして組み込み、その上に「gRPCによるWatch付きのKVS API」を被せる という構成になります。

これができれば、VM基盤は「Watchして、VMを起動して、Gossipでステータスを返すエージェント」を作るだけで済みますし、DB基盤も同様に作れます。非常にスケーラブルで美しい設計です。