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は、Gossip（SWIMプロトコル）でお互いに、そして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)**<br>「こうなってほしい」 | **Raft + Watch**<br>(強整合性) | ・VMの起動/停止命令<br>・DBのデータ配置情報の変更<br>・パスワードや設定変更 |
| **現状 (Actual State)**<br>「今こうなってます」 | **Gossip**<br>(結果整合性) | ・ノードの生存確認 (Heartbeat)<br>・リソース使用率 (CPU/Mem)<br>・「VM起動完了」などのステータス |
| **通知 (Notification)** | **Watch (HTTP/gRPC Stream)** | ・「新しい命令が来たぞ！」というトリガー |

#### 実装のアドバイス

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

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