このページは、Cluster のメンバー構成を定義し、ノードを起動したい場合に読むページです。実行時の内部構造、ルーティング、通信形式、運用上の境界は Cluster のアーキテクチャ に分けています。
事前に決めること
Section titled “事前に決めること”Cluster は peer を自動発見しません。components.clusterEnabled を有効にする前に、ローカルノードを識別し、クラスタ全体を説明する情報を用意します。
| 項目 | 理由 |
|---|---|
| 安定した node id | ローカルプロセスを 1 つの NodeInfo に対応させるため。 |
| メンバー一覧 | 各ノードの host、data/API port、replication port、capability を定義するため。 |
| 配置モード | local-only、mirror、key owner 分散のどれにするかを決めるため。 |
| 起動ロール | プロセスを Primary / Replica のどちらで起動するかを明示するため。 |
| Primary 接続先 | Replica が Primary のレプリケーション待ち受けに接続するため。 |
| ACK ポリシー | Primary の書き込みが Replica の確認をどこまで待つかを決めるため。 |
| 通信モード | local/private network 向けの plain か、secure replication かを選ぶため。 |
Cluster は、レプリケーション実行層と配置情報です。service discovery、client routing、自動 failover、snapshot orchestration は、server、control plane、deployment tooling 側で持つ前提です。
モードを選ぶ
Section titled “モードを選ぶ”ReplicationMode は配置方法を決めます。この値は ClusterConfig に保存されます。
| モード | 意味 |
|---|---|
STANDALONE | ローカルだけで動作します。レプリケーションは不要です。 |
MIRROR | 書き込みをすべてのデータ保持ノードへ送ります。 |
STRIPE | キーごとに、決定的な rendezvous hashing で 1 つのデータ保持ノードを選びます。 |
STRIPE は、ノード構成を変えても既存データを自動で移動しません。Striped な構成でメンバーを変える場合は、運用上の移行作業として扱います。
モード選択の目安
Section titled “モード選択の目安”| 目的 | まず使うもの | メモ |
|---|---|---|
| 1 プロセス、またはローカルテスト | STANDALONE | Cluster を無効、または local-only に近い形で扱います。 |
| 同じ書き込みを複数ノードに持たせる | MIRROR | Primary が entry と Blob payload を Replica へ送ります。 |
| キーごとに 1 つのデータ保持ノードを決める | STRIPE | 配置は決定的ですが、移行は外部で行います。 |
| 自動 scale-out / rebalance | 外部 control plane | 現在の Cluster は基本部品であり、自動スケール機構ではありません。 |
現時点で最も素直な replicated deployment は MIRROR です。STRIPE は、client routing、membership change、data migration を上位層で制御する場合に向いています。
起動ロールを選ぶ
Section titled “起動ロールを選ぶ”非 standalone モードでは、NodeStartupRole を明示します。
| 起動ロール | 実行時ロール | メモ |
|---|---|---|
PRIMARY | PRIMARY | ローカルノードが config 内に存在し、COORDINATOR_ELIGIBLE を持つ必要があります。 |
REPLICA | REPLICA | Primary の node id、host、replication port を解決できる必要があります。 |
AUTO | MIRROR / STRIPE では拒否 | Cluster モードでの自動ロール選択は未実装です。 |
Primary / Replica は実行時ロールです。配置モードとは別の概念です。たとえば MIRROR でも、書き込みを送る Primary プロセスと、それを受け取る Replica プロセスがあります。
最小 2 ノード構成
Section titled “最小 2 ノード構成”最初の replicated setup では、構成を単純にして明示的に起動します。
| 手順 | Node 1 | Node 2 |
|---|---|---|
node.id を作る | 1 | 2 |
cluster.akcc を置く | 同じ 2 ノード構成 | 同じ 2 ノード構成 |
| 起動ロール | PRIMARY | REPLICA |
| Primary id | 1 | 1 |
| レプリケーションポート | Node 1 の replPort で待ち受け | Node 1 の replPort へ接続 |
| データディレクトリ | Node 1 専用 | Node 2 専用 |
各プロセスには専用の dataDir を持たせます。2 つのノードで同じ WAL、SST、Blob、VersionLog ファイルを共有しないでください。
ノード ID を準備する
Section titled “ノード ID を準備する”各エンジンインスタンスは、安定した数値の node id を持ちます。AkkEngine::open() は paths.nodeIdPath から node id を読み込み、ファイルがなければランダムな非ゼロ uint64_t を生成して保存します。
paths.dataDir を設定している場合、既定パスは次の通りです。
<dataDir>/node.id非 standalone モードでは、この id が ClusterConfig 内の NodeInfo::nodeId と一致している必要があります。実運用では、初回の Cluster 起動前に node.id を作成・配置するか、生成された id を読んで config に追加してから components.clusterEnabled を有効にします。
node.id はローカルプロセスの identity です。メンバー構成を自動生成する仕組みではありません。削除して別の id が作られると、他ノードが期待する NodeInfo と一致しなくなる可能性があります。
Cluster Config を定義する
Section titled “Cluster Config を定義する”Cluster 設定は cluster::ClusterConfig で表します。node id、peer host、data/API port、replication port、node capability、配置モード、ACK ポリシーを保持します。
#include "akk/engine/AkkEngine.hpp"#include "akk/engine/cluster/ClusterConfig.hpp"
#include <utility>#include <vector>
namespace engine = akkaradb::engine;namespace cluster = akkaradb::engine::cluster;
cluster::ClusterConfig makeConfig() { std::vector<cluster::NodeInfo> nodes{ { .nodeId = 1, .host = "127.0.0.1", .dataPort = 7070, .replPort = 7170, .capabilities = cluster::COORDINATOR_ELIGIBLE | cluster::DATA_BEARING, }, { .nodeId = 2, .host = "127.0.0.1", .dataPort = 7071, .replPort = 7171, .capabilities = cluster::DATA_BEARING, }, };
cluster::AckPolicy ack{}; ack.mode = cluster::AckPolicyMode::ALL_TARGETS; ack.stage = cluster::AckStage::APPLIED;
return cluster::ClusterConfig{ std::move(nodes), cluster::ReplicationMode::MIRROR, ack, };}NodeInfo::host は他ノードから接続されるアドレスです。replPort はレプリケーション待ち受けポートです。dataPort はそのノードの公開データ/API ポートとして記録される値で、これだけで API サーバーが起動するわけではありません。
永続設定として保存する
Section titled “永続設定として保存する”ClusterConfig は EngineOptions と分けて保存・読み込みできます。各ノードに同じメンバー構成を置き、起動時にローカルの role だけを選びます。
cluster::ClusterConfig cfg = /* build or load from your own config source */;cfg.save("data/node-1/cluster.akcc");cfg.save("data/node-2/cluster.akcc");各プロセスでは、ローカルの copy を参照します。
EngineOptions opts;opts.components.clusterEnabled = true;opts.paths.dataDir = "data/node-2";opts.paths.clusterConfigPath = "data/node-2/cluster.akcc";cluster.akcc には、メンバー構成、配置、capability、ACK ポリシーが保存されます。起動 role、secure pin、Primary の上書き、bind host のような実行時専用値は EngineOptions 側に残ります。
Primary として起動する
Section titled “Primary として起動する”AkkEngine::open() の前に Cluster コンポーネントを有効にします。opts.cluster.config が指定されていればそれを使い、指定されていなければ paths.clusterConfigPath を読み込みます。
engine::AkkEngineOptions opts;opts.paths.dataDir = "data/node-1";opts.paths.nodeIdPath = "data/node-1/node.id";opts.components.clusterEnabled = true;
opts.cluster.config = makeConfig();opts.cluster.runtime.startupRole = cluster::NodeStartupRole::PRIMARY;opts.cluster.runtime.transportMode = cluster::TransportMode::PLAIN;opts.cluster.runtime.replBindHost = "127.0.0.1";
auto db = engine::AkkEngine::open(std::move(opts));Primary 起動は、ローカル node id が config 内に存在しない場合、またはローカルノードが COORDINATOR_ELIGIBLE を持たない場合に失敗します。レプリケーション待ち受けには、ローカルノードに設定された replPort が使われます。
同じ書き込み stream に対して、Primary として起動するプロセスは 1 つだけにします。現在の Cluster runtime は leader election や fencing を持たないため、split-brain の防止は deployment layer の責務です。
Replica として起動する
Section titled “Replica として起動する”Replica 起動には Primary の id が必要です。primaryNodeId が ClusterConfig 内に存在する場合、runtime はその config entry から primaryHost と primaryReplPort を補完できます。
engine::AkkEngineOptions opts;opts.paths.dataDir = "data/node-2";opts.paths.nodeIdPath = "data/node-2/node.id";opts.components.clusterEnabled = true;
opts.cluster.config = makeConfig();opts.cluster.runtime.startupRole = cluster::NodeStartupRole::REPLICA;opts.cluster.runtime.primaryNodeId = 1;opts.cluster.runtime.transportMode = cluster::TransportMode::PLAIN;
auto db = engine::AkkEngine::open(std::move(opts));Primary の接続先は直接上書きできます。
opts.cluster.runtime.primaryNodeId = 1;opts.cluster.runtime.primaryHost = "10.0.0.10";opts.cluster.runtime.primaryReplPort = 7170;Primary id が未指定、ローカルノード自身を指している、coordinator 候補ではない、または host と replication port を解決できない場合、Replica 起動は失敗します。
Replica クライアントはバックグラウンドで再接続します。接続やハンドシェイクに失敗した場合、エンジンが開いている間は約 200 ms 待ってから再試行します。
Replica を追加・復帰する
Section titled “Replica を追加・復帰する”Replica が新規の場合、または停止時間が短く、外部 snapshot やメモリ上の catch-up window で差分を補える場合は、次の流れで復帰させます。
- Replica に新しい snapshot が必要なら、書き込みを止めるか一貫した source copy を取ります。
- Replica 専用の
dataDirと安定したnode.idを用意します。 - Primary と Replica の両方を含む
cluster.akccを置きます。 NodeStartupRole::REPLICAと期待するprimaryNodeIdを指定して起動します。- Replica が接続し、新しい entry を適用していることを logs / stats で確認します。
Primary が保持するのは短いメモリ上の entry buffer だけです。この範囲を超えて遅れた Replica は、再接続前に外部の data copy で追いつかせる必要があります。
Replica を停止・削除する
Section titled “Replica を停止・削除する”計画的に Replica を外す場合は、次の流れにします。
- Replica process を停止します。
- 残るノードで使う durable cluster config を更新します。
- deployment model に合わせて process を restart / reload します。
- Primary の ACK policy を確認します。
ALL_TARGETSとQUORUMは live replica 数に依存します。 - recovery に不要になったことを確認してから、Replica の data directory を削除または保管します。
現在の membership change は config change として扱います。Cluster は striped data の rebalance や file migration を自動では行いません。
ACK ポリシーを設定する
Section titled “ACK ポリシーを設定する”AckPolicy は、Primary の書き込みが Replica の確認をどこまで待つかを制御します。
| ポリシー | 意味 |
|---|---|
NONE | Replica からの ACK を待ちません。 |
ALL_TARGETS | 現在 live なすべての Replica が対象 sequence を ACK するまで待ちます。 |
QUORUM | 少なくとも quorum 個の live Replica が対象 sequence を ACK するまで待ちます。 |
AckStage は、ACK が何を意味するかを決めます。
| Stage | Replica 側の動作 |
|---|---|
RECEIVED | entry bytes を受信し decode した後に ACK します。 |
APPLIED | ローカルエンジンへ適用した後に ACK します。 |
DURABLE | ローカルの durability sync を強制してから ACK します。 |
強い stage ほど確認内容は強くなりますが、write latency は増えます。Blob frame は Replica へ送られますが、entry ACK ポリシーの待機対象にはなりません。
ACK ポリシー例
Section titled “ACK ポリシー例”cluster::AckPolicy fireAndForget;fireAndForget.mode = cluster::AckPolicyMode::NONE;
cluster::AckPolicy appliedOnAll;appliedOnAll.mode = cluster::AckPolicyMode::ALL_TARGETS;appliedOnAll.stage = cluster::AckStage::APPLIED;
cluster::AckPolicy oneDurableReplica;oneDurableReplica.mode = cluster::AckPolicyMode::QUORUM;oneDurableReplica.quorum = 1;oneDurableReplica.stage = cluster::AckStage::DURABLE;DURABLE は、Replica が local durability path を強制してから ACK することを求めます。組み込み stage の中では最も強い確認ですが、その分コストも高くなります。復旧時の意味を latency より優先する経路で使います。
セキュア通信を設定する
Section titled “セキュア通信を設定する”TransportMode::SECURE では、レプリケーション frame の前に native secure channel を開きます。secure.identitySeedPath が空で、paths.dataDir が設定されている場合、runtime は次の path を使います。
<dataDir>/cluster.identityopts.cluster.runtime.transportMode = cluster::TransportMode::SECURE;opts.cluster.runtime.secure.identitySeedPath = "data/node-1/cluster.identity";opts.cluster.runtime.secure.expectedPrimaryNodeId = 1;TransportMode::PLAIN は、すべての advertised node host が loopback、link-local、unique-local IPv6、または private IPv4 の場合だけ許可されます。public/WAN host は拒否されるため、SECURE を使ってください。
Primary identity を固定する
Section titled “Primary identity を固定する”secure replication では、Replica 側で接続先 Primary の node id を固定できます。
opts.cluster.runtime.primaryNodeId = 1;opts.cluster.runtime.secure.expectedPrimaryNodeId = 1;より厳密に検証する場合は、pinnedPeers に cluster node id ごとの期待 public key を入れます。この key は各ノードの cluster.identity seed から導出されます。identity seed は durable に保持し、backup 対象にしてください。置き換えると、そのノードの secure identity が変わります。
永続ファイル
Section titled “永続ファイル”paths.dataDir が設定されている場合、Cluster 関連の既定パスは次の通りです。
| Path | 既定値 |
|---|---|
paths.clusterConfigPath | <dataDir>/cluster.akcc |
paths.nodeIdPath | <dataDir>/node.id |
| Cluster manifest | <dataDir>/cluster.akmf |
| Secure identity seed | secure mode が既定 path を必要とする場合、<dataDir>/cluster.identity |
cluster.akcc はメンバー構成とポリシーを保存します。replBindHost、Primary の上書き、secure pin、transport mode のような実行時専用値は、Cluster config には保存されません。
運用プレイブック
Section titled “運用プレイブック”計画的な Replica restart
Section titled “計画的な Replica restart”Replica を停止し、data directory は保持したまま、同じ node.id と config で起動します。停止時間が短ければ、handshake 時に Primary が buffered entry を replay できます。長時間停止していた場合は、再接続前に snapshot で Replica を更新します。
Primary process restart
Section titled “Primary process restart”Primary を再起動する前に書き込みを止めます。同じ node.id、config、PRIMARY role で起動します。Replica はバックグラウンドで再接続し、利用可能な catch-up window の範囲で local sequence 以降の entry を受け取ります。
手動 failover
Section titled “手動 failover”別ノードを Primary に昇格する処理は、runtime の自動機能ではなく運用手順です。対象ノードを選び、その data が用途に対して十分に新しいことを確認し、config と client routing を更新してから、Primary として起動する process を 1 つだけにします。
起動失敗時の確認
Section titled “起動失敗時の確認”| 症状 | 確認すること |
|---|---|
selfNodeId not found | バイナリの node.id の値が NodeInfo::nodeId のどれとも一致していない。 |
| Primary 起動が拒否される | ローカルノードに COORDINATOR_ELIGIBLE が付いていない。 |
| Replica 起動が拒否される | primaryNodeId がない、ローカル node id と同じ、または coordinator 候補ではない。 |
| config load が失敗する | cluster.akcc がない、壊れている、version が違う、CRC が合わない。 |
| plain transport が拒否される | NodeInfo::host に public/WAN address が含まれている。 |
| Cluster backend が見つからない | cluster runtime backend がリンクされていない、または runtimeBackendPath から load できない。 |
実行時の確認
Section titled “実行時の確認”| 症状 | 確認すること |
|---|---|
| Replica が再接続を繰り返す | Primary host、replPort、transport mode、secure identity の期待値。 |
| 書き込みが Replica を待たない | AckPolicyMode が NONE、または policy を満たす live replica がない可能性。 |
QUORUM が完了しない | quorum が、選択した stage を ACK できる connected replica 数より大きい可能性。 |
| reconnect 後に Blob data が足りない | Blob frame は Primary の catch-up buffer に残らないため、長い gap では snapshot/copy が必要。 |
STRIPE の read が membership change 後に外れる | 外部 migration なしで key ownership が変わっている可能性。 |