コンテンツにスキップ

Cluster のアーキテクチャ

このページは、Cluster 実行層の内部構造を理解したい場合に読むページです。設定と起動例は Cluster の使い方 に分けています。

Cluster 実行層は、大きく 3 つの部品で構成されます。

部品役割
ClusterConfigメンバー構成、配置モード、ノードの能力、データ/API ポート、レプリケーションポート、ACK ポリシーを保持します。
ClusterManager明示された起動オプションからローカルノードの役割を決め、Cluster 関連の記録を残します。
ClusterRuntimePrimary ならレプリケーションサーバー、Replica ならレプリケーションクライアントを起動します。

ノードが PRIMARY として起動すると、設定されたレプリケーションポートで待ち受け、ローカル書き込みのレコードと Blob ペイロードを接続中の Replica へ送ります。REPLICA として起動すると、指定された Primary に接続し、受信したフレームをエンジンのコールバック経由で適用します。

現在の実行層は Primary を自動選出しません。MIRRORSTRIPE では、NodeStartupRole::AUTO は起動時に拒否されます。

Cluster は、完全な分散データベースの制御層より下の層です。エンジン実行層はエントリの複製、Replica 側での適用、メンバー構成の検証、決定的な配置を担当します。一方で、クラスタ運用上の判断は現時点では外部の層が持ちます。

責務現在の担当
メンバー構成の信頼できる情報源外部設定またはデプロイツール
Primary 選出外部オーケストレーション
split-brain 防止外部オーケストレーション
クライアント通信のルーティングAPI サーバー、プロキシ、アプリケーション層
snapshot 転送外部のバックアップまたはコピー手順
striped data の移行外部の移行手順
ヘルス状態に基づくフェイルオーバー外部監視またはオーケストレーション

この境界は重要です。実行層は明示された役割を受け入れるため、同じ書き込みストリームに対して二つのプロセスを Primary として起動しても、Cluster 自身が片方を停止させることはありません。

ClusterConfig は、構築時と読み込み時に内容を検証します。

ルール失敗する例
ノード ID は一意で、0 ではないこと。nodeId == 0、または重複した ID。
host は空ではないこと。空の NodeInfo::host
capability は既知のフラグだけであること。COORDINATOR_ELIGIBLEDATA_BEARING 以外のビット。
Cluster モードにはデータ保持ノードが必要。MIRROR / STRIPEDATA_BEARING ノードがない。
Cluster モードには coordinator 候補が必要。MIRROR / STRIPECOORDINATOR_ELIGIBLE ノードがない。
quorum は明示すること。AckPolicyMode::QUORUMquorum == 0

ClusterConfig はコンパクトな CRC 付きバイナリファイルで、magic は AKC5、version は 1 です。ClusterConfig::save() は、書き込み前に検証し、一時ファイル経由で保存します。ClusterConfig::load() は magic、version、ファイル長、CRC を確認し、検証済みの設定を返します。

実行時専用の設定は cluster.akcc に保存されません。通信モード、セキュア peer の pin、bind host の上書き、Primary endpoint の上書きは EngineOptions に残るため、永続化されたメンバー構成とプロセスごとの起動ポリシーを分けて扱えます。

ClusterRouterClusterConfig だけを見る純粋なメモリ上のルーターです。ネットワーク I/O は行わず、ノードが現在正常かどうかも見ません。

メソッド挙動
writeTargets(key)そのキーの書き込み先ノードを返します。
readCandidates(key)そのキーを読める候補ノードを返します。現在の実装では書き込み配置と同じです。

MIRROR では、すべてのデータ保持ノードを返します。STRIPE では、rendezvous hashing で決まる 1 つの所有ノードを返します。Cluster モードでデータ保持ノードがない場合、ルーティングは例外を投げます。

エンジン内のレプリケーション経路は、現在の Primary 書き込みを接続中 Replica へ送るものです。クライアントからのリクエストをどのプロセスへ送るかは、外部のルーターや運用側で決める必要があります。

決めること
ClusterRouterClusterConfig に基づいて、キーを所有するノード ID を決めます。
Primary 実行層ローカル書き込みストリームをどの接続済み Replica に送るかを扱います。
Replica 実行層受信したフレームをローカルエンジンにどう適用するかを扱います。
クライアントルーターアプリケーションリクエストをどのプロセスに送るかを決めます。これは外部責務です。

つまり、配置はデータがどこにあるべきかを表し、レプリケーションは Primary 実行層から Replica 実行層へ書き込みをコピーする現在の仕組みです。

Primary は主に 2 種類のデータを送ります。

メッセージ意味
ENTRYPUT または REMOVE のキー/値変更。
BLOB_PUTBlob 参照に対応する外部 Blob の内容。

AkkEngine::put()putHinted() はシーケンスを確保し、ローカルへ書き込んだ後に shipEntry() を呼びます。Blob manager によって大きな値が外部化された場合は、shipBlob() でも送信されます。remove()ReplOpType::REMOVEENTRY として表現されます。

Replica 側では、ENTRY フレームがエンジンの適用コールバックを呼びます。WAL や VersionLog が有効であればそこへ追記し、MemTable に適用し、ローカルシーケンスを進めます。BLOB_PUT は、Blob manager が有効な場合に Blob 内容を書き込みます。

ENTRY フレームには Primary のシーケンス番号が入ります。Replica は、レプリケーションストリームから読んだ順にエンジンのコールバック経由でエントリを適用します。ローカルシーケンスはエントリの適用に合わせて進みます。

Blob ペイロードは ENTRY フレームとは別に送られます。そのため、Blob に退避された値が Replica 側で完全に使える状態になるには、エントリと対応する BLOB_PUT ペイロードの両方が必要です。

ハンドシェイク時、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 では、Replica のローカルビューが一貫するように必要なファイルを保つ必要があります。

領域理由
node.idReplica を設定済みノード ID に対応させるため。
WAL、SST、manifest files基準となるキー/値状態を保つため。
Blob directory外部化された大きな値を保つため。
VersionLog filesVersionLog 有効時のバージョン履歴を保つため。
cluster.akcc永続化されたメンバー構成とポリシーを保つため。
cluster.identityセキュア通信の identity を保つため。

snapshot を戻した後は、同じノード ID で Replica を起動し、ハンドシェイクでローカルシーケンスより新しいエントリを要求します。

Replica は、設定された stage に対してだけ ACK フレームを送ります。Primary は最大で約 5 秒待ち、50 ms 間隔で条件を確認します。期限までに条件を満たさない場合、現在の実装では例外を投げずに待機から戻ります。

Blob フレームは Replica へ送られますが、ENTRY ACK ポリシーの待機対象にはなりません。

StagePrimary が推測できること
RECEIVEDReplica が ENTRY フレームをデコードした。
APPLIEDReplica がローカルエンジン経路にエントリを適用した。
DURABLEReplica がエントリ適用後にローカル耐久性を強制した。

ALL_TARGETSQUORUM は、設定された stage で対象シーケンスを ACK した、接続中で稼働している Replica を数えます。将来接続する Replica、切断中 Replica、Blob ペイロードの完了は含みません。

レプリケーションは TCP フレームで行われます。整数はすべてリトルエンディアンです。外側のフレームは次の形です。

[magic:u32 = "AKR5"][type:u8][flags:u8][payloadLen:u32][payloadCrc32c:u32][payload]

CRC はペイロードだけを対象にします。対応しているメッセージ種別は次の通りです。

種別方向
CLIENT_HELLO0x01Replica から Primary
SERVER_HELLO0x02Primary から Replica
ENTRY0x10Primary から Replica
BLOB_PUT0x11Primary から Replica
ACK0x12Replica から Primary
READ_REQUEST0x20予約済み
READ_RESPONSE0x21予約済み

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 として公開されていません。

セキュアモードでは、native secure channel の上でレプリケーションフレームを流します。重要な identity 入力は次の通りです。

入力役割
Local identity seedローカルの static public key を導出する永続的な秘密素材。
Ephemeral keysecure channel のセッションごとの鍵素材。
Expected Primary idReplica が意図した Primary ノード ID に接続していることを確認するため。
Pinned peerscluster node id から期待する public key への任意マップ。

identity seed をローテーションすると、そのノードの public identity が変わります。peer が public key を pin している場合は、pin と seed を同じ運用変更として更新します。

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 の場合だけ許可されます。

ケース現在の挙動
Replica が Primary より先に起動する接続とハンドシェイクをバックグラウンドで再試行します。
Primary が再起動するReplica は再接続し、ローカルシーケンスより新しいエントリを要求します。
Replica の遅れがバッファ内Primary がハンドシェイク後にバッファ済み ENTRY フレームを再送します。
Replica の遅れがバッファ外安全に再接続する前に、外部 snapshot/copy が必要です。
ACK 期限を超える約 5 秒で待機から戻ります。現在のコードは待機から例外を投げません。
secure identity が変わるpinned key を持つ peer は期待外の identity を拒否します。
2 つの Primary を起動する実行層は選出や fencing を行いません。デプロイエラーとして扱います。

cluster.akmf には、node join、node leave、primary lease の記録が永続的な手がかりとして書かれます。現在の Manager はこれらのイベントを書きますが、メンバー構成の復元や Primary 選出のために再生するわけではありません。

primary lease の記録は観測された Primary 所有権情報を残しますが、合意に基づく lease ではありません。分散ロックではなく診断用の状態として扱います。

スモークテストでは、現在の Cluster の公開面を結合レベルで確認しています。

領域確認していること
設定の保存/読み込みバイナリ永続化、CRC チェック、検証失敗。
役割の選択Primary / Replica の起動時検証。
レプリケーションPrimary から Replica への entry / blob 配送。
ACK モードNONEALL_TARGETSQUORUM、ACK stage。
セキュア通信セキュアなレプリケーション経路と identity の扱い。

自動フェイルオーバー、データ再配置、外部 snapshot のオーケストレーションは現在の実行境界の外側なので、これらのテスト範囲には含まれません。

現時点では、次の制約を明示的に扱ってください。

  • 自動 Primary 選出はありません。
  • split-brain を安全に避ける自動フェイルオーバーはありません。
  • STRIPE のデータ移行は自動ではありません。
  • Replica はレプリケーションの受信側であり、独立した書き込み入口ではありません。
  • メモリ上の catch-up buffer は 4096 ENTRY フレームまでです。
  • Blob レプリケーションは ACK 待機の対象ではありません。
  • 読み取りルーティングとクライアント通信のルーティングは外部の責任です。
  • メンバー変更には、外部の移行計画とロールアウト計画が必要です。

今の Cluster 層は、エンジンレベルのレプリケーション部品として見るのが自然です。本番向けに使う場合は、メンバー管理、フェイルオーバー、トラフィックルーティング、snapshot 転送、ヘルスチェック、移行を担当する制御層を別途用意する前提になります。