このページは、Cluster 実行層の内部構造を理解したい場合に読むページです。設定と起動例は Cluster の使い方 に分けています。
実行時の部品
Section titled “実行時の部品”Cluster 実行層は、大きく 3 つの部品で構成されます。
| 部品 | 役割 |
|---|---|
ClusterConfig | メンバー構成、配置モード、ノードの能力、データ/API ポート、レプリケーションポート、ACK ポリシーを保持します。 |
ClusterManager | 明示された起動オプションからローカルノードの役割を決め、Cluster 関連の記録を残します。 |
ClusterRuntime | Primary ならレプリケーションサーバー、Replica ならレプリケーションクライアントを起動します。 |
ノードが PRIMARY として起動すると、設定されたレプリケーションポートで待ち受け、ローカル書き込みのレコードと Blob ペイロードを接続中の Replica へ送ります。REPLICA として起動すると、指定された Primary に接続し、受信したフレームをエンジンのコールバック経由で適用します。
現在の実行層は Primary を自動選出しません。MIRROR と STRIPE では、NodeStartupRole::AUTO は起動時に拒否されます。
制御層との境界
Section titled “制御層との境界”Cluster は、完全な分散データベースの制御層より下の層です。エンジン実行層はエントリの複製、Replica 側での適用、メンバー構成の検証、決定的な配置を担当します。一方で、クラスタ運用上の判断は現時点では外部の層が持ちます。
| 責務 | 現在の担当 |
|---|---|
| メンバー構成の信頼できる情報源 | 外部設定またはデプロイツール |
| Primary 選出 | 外部オーケストレーション |
| split-brain 防止 | 外部オーケストレーション |
| クライアント通信のルーティング | API サーバー、プロキシ、アプリケーション層 |
| snapshot 転送 | 外部のバックアップまたはコピー手順 |
| striped data の移行 | 外部の移行手順 |
| ヘルス状態に基づくフェイルオーバー | 外部監視またはオーケストレーション |
この境界は重要です。実行層は明示された役割を受け入れるため、同じ書き込みストリームに対して二つのプロセスを Primary として起動しても、Cluster 自身が片方を停止させることはありません。
ClusterConfig は、構築時と読み込み時に内容を検証します。
| ルール | 失敗する例 |
|---|---|
| ノード ID は一意で、0 ではないこと。 | nodeId == 0、または重複した ID。 |
| host は空ではないこと。 | 空の NodeInfo::host。 |
| capability は既知のフラグだけであること。 | COORDINATOR_ELIGIBLE と DATA_BEARING 以外のビット。 |
| Cluster モードにはデータ保持ノードが必要。 | MIRROR / STRIPE で DATA_BEARING ノードがない。 |
| Cluster モードには coordinator 候補が必要。 | MIRROR / STRIPE で COORDINATOR_ELIGIBLE ノードがない。 |
| quorum は明示すること。 | AckPolicyMode::QUORUM で quorum == 0。 |
ClusterConfig はコンパクトな CRC 付きバイナリファイルで、magic は AKC5、version は 1 です。ClusterConfig::save() は、書き込み前に検証し、一時ファイル経由で保存します。ClusterConfig::load() は magic、version、ファイル長、CRC を確認し、検証済みの設定を返します。
実行時専用の設定は cluster.akcc に保存されません。通信モード、セキュア peer の pin、bind host の上書き、Primary endpoint の上書きは EngineOptions に残るため、永続化されたメンバー構成とプロセスごとの起動ポリシーを分けて扱えます。
ルーティング
Section titled “ルーティング”ClusterRouter は ClusterConfig だけを見る純粋なメモリ上のルーターです。ネットワーク I/O は行わず、ノードが現在正常かどうかも見ません。
| メソッド | 挙動 |
|---|---|
writeTargets(key) | そのキーの書き込み先ノードを返します。 |
readCandidates(key) | そのキーを読める候補ノードを返します。現在の実装では書き込み配置と同じです。 |
MIRROR では、すべてのデータ保持ノードを返します。STRIPE では、rendezvous hashing で決まる 1 つの所有ノードを返します。Cluster モードでデータ保持ノードがない場合、ルーティングは例外を投げます。
エンジン内のレプリケーション経路は、現在の Primary 書き込みを接続中 Replica へ送るものです。クライアントからのリクエストをどのプロセスへ送るかは、外部のルーターや運用側で決める必要があります。
配置とレプリケーション
Section titled “配置とレプリケーション”| 層 | 決めること |
|---|---|
ClusterRouter | ClusterConfig に基づいて、キーを所有するノード ID を決めます。 |
| Primary 実行層 | ローカル書き込みストリームをどの接続済み Replica に送るかを扱います。 |
| Replica 実行層 | 受信したフレームをローカルエンジンにどう適用するかを扱います。 |
| クライアントルーター | アプリケーションリクエストをどのプロセスに送るかを決めます。これは外部責務です。 |
つまり、配置はデータがどこにあるべきかを表し、レプリケーションは Primary 実行層から Replica 実行層へ書き込みをコピーする現在の仕組みです。
レプリケーションの流れ
Section titled “レプリケーションの流れ”Primary は主に 2 種類のデータを送ります。
| メッセージ | 意味 |
|---|---|
ENTRY | PUT または REMOVE のキー/値変更。 |
BLOB_PUT | Blob 参照に対応する外部 Blob の内容。 |
AkkEngine::put() と putHinted() はシーケンスを確保し、ローカルへ書き込んだ後に shipEntry() を呼びます。Blob manager によって大きな値が外部化された場合は、shipBlob() でも送信されます。remove() は ReplOpType::REMOVE の ENTRY として表現されます。
Replica 側では、ENTRY フレームがエンジンの適用コールバックを呼びます。WAL や VersionLog が有効であればそこへ追記し、MemTable に適用し、ローカルシーケンスを進めます。BLOB_PUT は、Blob manager が有効な場合に Blob 内容を書き込みます。
ENTRY フレームには Primary のシーケンス番号が入ります。Replica は、レプリケーションストリームから読んだ順にエンジンのコールバック経由でエントリを適用します。ローカルシーケンスはエントリの適用に合わせて進みます。
Blob ペイロードは ENTRY フレームとは別に送られます。そのため、Blob に退避された値が Replica 側で完全に使える状態になるには、エントリと対応する BLOB_PUT ペイロードの両方が必要です。
Catch-up の範囲
Section titled “Catch-up の範囲”ハンドシェイク時、Replica は次のペイロードを送ります。
[nodeId:u64][lastSeq:u64][role:u8][reserved:u8]Primary は次のペイロードを返します。
[nodeId:u64][currentSeq:u64][role:u8][reserved:u8]Primary は直近 4096 個の ENTRY フレームをメモリに保持します。Replica が接続すると、Replica の lastSeq より新しいバッファ済みエントリが送られます。
これは短い catch-up 範囲であり、永続的なログ配送ではありません。Blob フレームはこのバッファには保持されません。Replica がこのメモリ上の範囲を超えて遅れた場合は、再接続前に外部の snapshot/copy 手順で追いつかせる必要があります。
snapshot の境界
Section titled “snapshot の境界”外部 snapshot では、Replica のローカルビューが一貫するように必要なファイルを保つ必要があります。
| 領域 | 理由 |
|---|---|
node.id | Replica を設定済みノード ID に対応させるため。 |
| WAL、SST、manifest files | 基準となるキー/値状態を保つため。 |
| Blob directory | 外部化された大きな値を保つため。 |
| VersionLog files | VersionLog 有効時のバージョン履歴を保つため。 |
cluster.akcc | 永続化されたメンバー構成とポリシーを保つため。 |
cluster.identity | セキュア通信の identity を保つため。 |
snapshot を戻した後は、同じノード ID で Replica を起動し、ハンドシェイクでローカルシーケンスより新しいエントリを要求します。
ACK の挙動
Section titled “ACK の挙動”Replica は、設定された stage に対してだけ ACK フレームを送ります。Primary は最大で約 5 秒待ち、50 ms 間隔で条件を確認します。期限までに条件を満たさない場合、現在の実装では例外を投げずに待機から戻ります。
Blob フレームは Replica へ送られますが、ENTRY ACK ポリシーの待機対象にはなりません。
ACK の意味
Section titled “ACK の意味”| Stage | Primary が推測できること |
|---|---|
RECEIVED | Replica が ENTRY フレームをデコードした。 |
APPLIED | Replica がローカルエンジン経路にエントリを適用した。 |
DURABLE | Replica がエントリ適用後にローカル耐久性を強制した。 |
ALL_TARGETS と QUORUM は、設定された stage で対象シーケンスを ACK した、接続中で稼働している Replica を数えます。将来接続する Replica、切断中 Replica、Blob ペイロードの完了は含みません。
レプリケーションは TCP フレームで行われます。整数はすべてリトルエンディアンです。外側のフレームは次の形です。
[magic:u32 = "AKR5"][type:u8][flags:u8][payloadLen:u32][payloadCrc32c:u32][payload]CRC はペイロードだけを対象にします。対応しているメッセージ種別は次の通りです。
| 種別 | 値 | 方向 |
|---|---|---|
CLIENT_HELLO | 0x01 | Replica から Primary |
SERVER_HELLO | 0x02 | Primary から Replica |
ENTRY | 0x10 | Primary から Replica |
BLOB_PUT | 0x11 | Primary から Replica |
ACK | 0x12 | Replica から Primary |
READ_REQUEST | 0x20 | 予約済み |
READ_RESPONSE | 0x21 | 予約済み |
ENTRY ペイロードは次の形です。
[seq:u64][sourceNodeId:u64][op:u8][recordFlags:u8][keyLen:u32][valueLen:u32][key][value]BLOB_PUT ペイロードは次の形です。
[seq:u64][blobId:u64][contentLen:u64][content]ACK ペイロードは次の形です。
[seq:u64][stage:u8][reserved:u8]予約済みの read message はフレーミング層ではエンコード/デコードできますが、現在のレプリケーションクライアント/サーバー経路では、利用可能な Cluster read protocol として公開されていません。
セキュアハンドシェイク
Section titled “セキュアハンドシェイク”セキュアモードでは、native secure channel の上でレプリケーションフレームを流します。重要な identity 入力は次の通りです。
| 入力 | 役割 |
|---|---|
| Local identity seed | ローカルの static public key を導出する永続的な秘密素材。 |
| Ephemeral key | secure channel のセッションごとの鍵素材。 |
| Expected Primary id | Replica が意図した Primary ノード ID に接続していることを確認するため。 |
| Pinned peers | cluster node id から期待する public key への任意マップ。 |
identity seed をローテーションすると、そのノードの public identity が変わります。peer が public key を pin している場合は、pin と seed を同じ運用変更として更新します。
セキュアチャネル
Section titled “セキュアチャネル”TransportMode::SECURE では、レプリケーションフレームの前に native secure channel を開きます。identity seed は読み込み、存在しない場合は作成され、ローカルの static public key を導出するために使われます。
任意で pinnedPeers を設定すると、cluster node id ごとに期待する public key を pin できます。Replica 側では、primaryNodeId が未指定の場合に secure.expectedPrimaryNodeId を Primary ID として使えます。
TransportMode::PLAIN は、advertised host が loopback、link-local、unique-local IPv6、または private IPv4 の場合だけ許可されます。
障害時の挙動
Section titled “障害時の挙動”| ケース | 現在の挙動 |
|---|---|
| Replica が Primary より先に起動する | 接続とハンドシェイクをバックグラウンドで再試行します。 |
| Primary が再起動する | Replica は再接続し、ローカルシーケンスより新しいエントリを要求します。 |
| Replica の遅れがバッファ内 | Primary がハンドシェイク後にバッファ済み ENTRY フレームを再送します。 |
| Replica の遅れがバッファ外 | 安全に再接続する前に、外部 snapshot/copy が必要です。 |
| ACK 期限を超える | 約 5 秒で待機から戻ります。現在のコードは待機から例外を投げません。 |
| secure identity が変わる | pinned key を持つ peer は期待外の identity を拒否します。 |
| 2 つの Primary を起動する | 実行層は選出や fencing を行いません。デプロイエラーとして扱います。 |
Cluster Manifest
Section titled “Cluster Manifest”cluster.akmf には、node join、node leave、primary lease の記録が永続的な手がかりとして書かれます。現在の Manager はこれらのイベントを書きますが、メンバー構成の復元や Primary 選出のために再生するわけではありません。
primary lease の記録は観測された Primary 所有権情報を残しますが、合意に基づく lease ではありません。分散ロックではなく診断用の状態として扱います。
テストで確認している範囲
Section titled “テストで確認している範囲”スモークテストでは、現在の Cluster の公開面を結合レベルで確認しています。
| 領域 | 確認していること |
|---|---|
| 設定の保存/読み込み | バイナリ永続化、CRC チェック、検証失敗。 |
| 役割の選択 | Primary / Replica の起動時検証。 |
| レプリケーション | Primary から Replica への entry / blob 配送。 |
| ACK モード | NONE、ALL_TARGETS、QUORUM、ACK stage。 |
| セキュア通信 | セキュアなレプリケーション経路と identity の扱い。 |
自動フェイルオーバー、データ再配置、外部 snapshot のオーケストレーションは現在の実行境界の外側なので、これらのテスト範囲には含まれません。
運用上の境界
Section titled “運用上の境界”現時点では、次の制約を明示的に扱ってください。
- 自動 Primary 選出はありません。
- split-brain を安全に避ける自動フェイルオーバーはありません。
STRIPEのデータ移行は自動ではありません。- Replica はレプリケーションの受信側であり、独立した書き込み入口ではありません。
- メモリ上の catch-up buffer は 4096
ENTRYフレームまでです。 - Blob レプリケーションは ACK 待機の対象ではありません。
- 読み取りルーティングとクライアント通信のルーティングは外部の責任です。
- メンバー変更には、外部の移行計画とロールアウト計画が必要です。
今の Cluster 層は、エンジンレベルのレプリケーション部品として見るのが自然です。本番向けに使う場合は、メンバー管理、フェイルオーバー、トラフィックルーティング、snapshot 転送、ヘルスチェック、移行を担当する制御層を別途用意する前提になります。