Skip to main content

Embedded Kahuna Node

Kahuna normally runs as a separate server that applications reach over the network. An embedded node runs the same engine inside your own .NET process instead: there is no server to install, no port to open, and no network call per operation.

Embed Kahuna when only your process needs the data. Run Kahuna Server when other applications or processes must share it: an embedded node exposes no REST or external gRPC endpoint, so they cannot connect to it.

Typical uses:

  • Integration tests that need real Kahuna behavior, such as transactions, locks, scripts, and Raft replication, without Docker or a running cluster. EmbeddedKahunaCluster runs several nodes in one process so tests can also cover elections and failover.
  • Local tools and single-process applications that want Kahuna's transactional key/value engine as a library.
  • Browser and WebAssembly hosts, through the thread-free build.

EmbeddedKahunaNode starts a single-node Kahuna engine inside the current .NET process, without the ASP.NET server or Kestrel:

using System.Text;
using Kahuna;
using Kahuna.Shared.KeyValue;
using Kommander.Time;

await using var node = new EmbeddedKahunaNode(new()
{
Storage = "memory",
WalStorage = "memory",
InitialPartitions = 1
});

await node.StartAsync();
await node.WaitForLeaderForKeyAsync("tenant/table/key-a");

byte[] value = Encoding.UTF8.GetBytes("value-a");

await node.Kahuna.LocateAndTrySetKeyValue(
transactionId: HLCTimestamp.Zero,
key: "tenant/table/key-a",
value: value,
compareValue: null,
compareRevision: -1,
flags: KeyValueFlags.Set,
expiresMs: 0,
durability: KeyValueDurability.Persistent,
cancellationToken: CancellationToken.None
);

API​

public sealed class EmbeddedKahunaNode : IAsyncDisposable
{
public IKahuna Kahuna { get; }
public IRaft Raft { get; }

public EmbeddedKahunaNode(
EmbeddedKahunaOptions options,
ILoggerFactory? loggerFactory = null
);

public Task StartAsync(CancellationToken cancellationToken = default);
public Task<string> WaitForLeaderForKeyAsync(string key, CancellationToken cancellationToken = default);
public ValueTask DisposeAsync();
}

In-Memory Embedded Cluster​

EmbeddedKahunaCluster starts several full Kahuna nodes in one process. Each member has its own storage, Raft log, actors, and partition leadership. The members communicate through in-memory Kahuna and Raft transports, so no socket is opened, but replication, elections, failover, and catch-up still run.

Use it for integration tests, demos, and deterministic failure scenarios:

await using EmbeddedKahunaCluster cluster =
await EmbeddedKahunaCluster.CreateInMemoryAsync(
3,
new EmbeddedKahunaOptions
{
NodeName = "demo",
Storage = "memory",
WalStorage = "memory",
InitialPartitions = 2
},
loggerFactory,
cancellationToken
);

EmbeddedKahunaNode node = cluster.GetNode(1);

int leader = await cluster.GetLeaderIndexAsync(0, cancellationToken);
await cluster.StopNodeAsync(leader);
await cluster.RestartNodeAsync(leader, cancellationToken);

CreateInMemoryAsync copies the base options for each member, assigns node ids 1..N, names them {NodeName}-{id}, and assigns ports from Port + index or 7000 + index when Port is 0. Storage and WAL must both be memory, because a restarted member starts empty and catches up from the others.

Useful cluster operations:

APIPurpose
GetNode(index)Return a running embedded node.
GetLeaderIndexAsync(partitionId)Poll until a running member believes it leads the partition. Treat the result as a routing hint during failover.
StopNodeAsync(index)Stop a member as if its host crashed. Other members elect new leaders.
RestartNodeAsync(index)Recreate a stopped member under the same endpoint and let it catch up.
BlockLink(from, to) / BlockLinkBothWays(a, b)Drop in-memory traffic between members for partition tests.
IsolateNode(index)Cut one member off from every other member while it keeps running.
UnblockLink, UnblockLinkBothWays, UnblockAllLinksRestore blocked in-memory links.

During failover, forwarded calls can fail with KahunaServerException or return MustRetry until a new leader is elected. Retry through the normal client or test harness path.

Thread-Free Browser/WASM Mode​

Kahuna also ships a thread-free embedded build for hosts that cannot create threads, especially browser-wasm with WasmEnableThreads disabled. Target net10.0-browser to select that build:

<Project Sdk="Microsoft.NET.Sdk.WebAssembly">
<PropertyGroup>
<TargetFramework>net10.0-browser</TargetFramework>
<RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
<WasmEnableThreads>false</WasmEnableThreads>
</PropertyGroup>
</Project>

Thread-free mode uses Kommander's host-pumped scheduler and only supports in-memory storage and WAL. It does not include the ASP.NET Core server surface, external gRPC/REST listeners, RocksDB, SQLite, or socket-based inter-node communication. Use EmbeddedKahunaNode or EmbeddedKahunaCluster with Storage = "memory" and WalStorage = "memory".

await using EmbeddedKahunaNode node = new(new EmbeddedKahunaOptions
{
Storage = "memory",
WalStorage = "memory",
InitialPartitions = 1
}, loggerFactory);

await node.StartAsync(cancellationToken);

Keep host code fully asynchronous. Blocking waits such as .Result, .Wait(), or GetAwaiter().GetResult() can stall a single-threaded WebAssembly runtime. Avoid console loggers that start their own background thread.

In a browser cluster, every node and timer shares one event loop, so keep the embedded Raft timing defaults unless you have measured the heartbeat gaps. Set MetricsSupport = true when you want metrics under WebAssembly, since System.Diagnostics.Metrics is commonly disabled by default there.

Kahuna's REST and persistence JSON paths use source-generated metadata, so trimming and reflection-disabled WebAssembly builds do not need reflection-based JSON serialization for Kahuna payloads.

Options​

Options are properties of EmbeddedKahunaOptions. The groups follow Server Configuration; where a server flag exists, its section explains the same setting. Embedded names, units, and some defaults differ from the server flags.

Node Identity​

Related server flags: Cluster Identity and Discovery.

OptionDefaultDescription
NodeNameembedded-1Logical node name.
NodeId1Raft node identifier.
HostlocalhostMust be a concrete host, not *.
Port0Unused by in-memory communication, but still part of the Raft configuration.
InitialPartitions1Number of Raft partitions.
JoinExistingSeedsnullSeed endpoints for joining an existing embedded cluster through the cluster constructor. Null or empty boots from the configured discovery roster.

Storage and WAL​

Related server flags: Storage and WAL.

OptionDefaultDescription
StoragememoryKey/value storage backend: memory, sqlite, or rocksdb.
StoragePathemptyStorage directory for persistent backends.
StorageRevisiongeneratedStorage revision name. Keep stable when reopening persisted state; it also determines the automatic receive-staging directory.
WalStoragememoryRaft WAL backend: memory, sqlite, or rocksdb.
WalPathemptyWAL directory for persistent backends.
WalRevisiongeneratedWAL revision name.
WalSyncWritestrueRequire synchronous durable writes for RocksDB or SQLite WAL storage.
StorageProvidersStorageProviders.CreateDefault()Storage engines the node can open by type name for Storage and WalStorage. The default registers Kahuna's own engines; register other engine types here before constructing the node.
ExtensionOptionsempty mapEngine-specific settings passed to each provider's PersistenceBackendContext or WalContext. See Custom Storage Engines.
RaftWalShardWriteBufferSizeMbnullRocksDB Raft WAL shard memtable size in MiB. Null keeps Kommander's default. Applies only when WalStorage is rocksdb.
RaftWalShardMinWriteBufferNumberToMergenullImmutable memtables merged into one RocksDB WAL shard flush. Null keeps Kommander's default.
RaftWalShardMaxWriteBufferNumbernullMaximum mutable plus immutable memtables per RocksDB WAL shard. Null keeps Kommander's default.
RaftWalShardLevel0FileNumCompactionTriggernullLevel-0 file count that triggers compaction for RocksDB WAL shard column families. Null keeps Kommander's default.
RaftWalShardLevel0SlowdownWritesTriggernullLevel-0 file count at which RocksDB starts slowing WAL writers. Null keeps Kommander's default.
RaftWalShardLevel0StopWritesTriggernullLevel-0 file count at which RocksDB stops WAL writers. Null keeps Kommander's default.
RaftWalShardMaxBytesForLevelBaseMbnullRocksDB max_bytes_for_level_base for WAL shard column families, in MiB. Null keeps Kommander or RocksDB defaults.
RaftWalShardUniversalCompactionnullSelects universal compaction for RocksDB WAL shard column families when set to true; null keeps Kommander's default.
RocksDbSharedMemoryEnabledfalseShare one RocksDB block cache and write-buffer manager between the key/value backend and Raft WAL when both use RocksDB.
StorageSharedCacheMb0Cache budget in MiB shared by the store and WAL. Required above 0 for shared RocksDB memory.
StorageSharedMemtableBudgetMb0Shared memtable budget in MiB, charged inside the shared cache when present. Required above 0 for shared RocksDB memory.
StorageCompactionRateBytesPerSec0Throughput budget for materialized store flush and compaction writes; 0 is unlimited.
StorageMaxBackgroundCompactions0Maximum concurrent materialized store compactions; 0 keeps the backend default.
RocksDbDirectReadstrueRead RocksDB SST files with direct I/O so the RocksDB block cache is the primary read cache. Applies only when Storage is rocksdb.
RocksDbStatisticsfalseEnable RocksDB internal statistics collection and LOG dumps every 60 seconds. Useful for tuning or diagnosis, but it adds per-operation overhead. Applies only when Storage is rocksdb.

To bound RocksDB memory across both the embedded key/value backend and Raft WAL, enable shared RocksDB memory with both storage layers set to RocksDB:

EmbeddedKahunaOptions options = new()
{
Storage = "rocksdb",
WalStorage = "rocksdb",
RocksDbSharedMemoryEnabled = true,
StorageSharedCacheMb = 512,
StorageSharedMemtableBudgetMb = 128,
RocksDbDirectReads = true,
RocksDbStatistics = false
};

RocksDB uses these shared budgets only when both storage layers are rocksdb and RocksDbSharedMemoryEnabled is true. Custom providers can read the shared budgets and compaction settings from their context. The older RocksDbSharedMemoryBudgetMb and RocksDbSharedMemtableBudgetMb properties remain as obsolete aliases.

Workers and Runtime​

Related server flags: Workers and Runtime.

OptionDefaultDescription
LocksWorkers0Lock actors per durability ring. 0 auto-sizes to max(32, CPU cores * 4).
KeyValueWorkers0Key/value actors per durability ring. 0 auto-sizes to max(32, CPU cores * 4).
BackgroundWriterWorkers1Background persistence worker count.
SequencerWorkers0Sequence actors. 0 auto-sizes to max(8, CPU cores).
SequencerBlockSize1000Values reserved from one durable sequence record per compare-and-set (CAS) for sequences without their own block size. Larger blocks reduce storage traffic but can leave larger gaps if an abandoned block is not fully issued.
SequencerIdempotencyRetentionMax256Maximum keyed sequence-reservation idempotency entries retained per sequence record. A non-positive value disables the count cap.
SequencerIdempotencyRetentionTtl10 minutesWindow in which retrying a keyed sequence reservation replays the same allocation. TimeSpan.Zero disables age pruning.
SequencerMaxSequencesPerActor10000Maximum sequence records one sequence actor keeps resident before evicting least-recently-used records and abandoning their reserved blocks. A non-positive value leaves residency unbounded.
SequencerBlockLease5 secondsMaximum time a reserved sequence block can be served from memory before revalidation against the durable record. Safe sequence updates wait this long and refuse allocations during the wait. A non-positive value disables revalidation and sequence updates.
BackendReadIOThreads4Dedicated Kahuna backend read pool size for point gets, existence checks, read-before-write work, and scans. Separate from the Raft WAL read pool.
BackendWriteIOThreads1Dedicated Kahuna backend writer pool size for background batch writes and pruning.
BackendReadQueueDepth4096Per-partition pending queue depth for the backend read scheduler.
ScanPageRetryBudgetMs5000Maximum retry window for one range-scan page that keeps returning retryable state before the scan fails loudly with a retryable server error.
Functionsempty registryUser-defined script functions callable by this embedded node. Register functions before constructing the node.
FunctionSlowWarnMs50Logs a warning when a user-defined function takes longer than this many milliseconds. 0 disables the warning.
FailFastOnOutOfMemorytrueFail fast on OutOfMemoryException so an orchestrator can restart the embedded process. When disabled, Kahuna records the fatal fault and reports unhealthy through health surfaces that expose the core state.

Transactions, Admission, and Scripts​

Related server flags: Workers and Runtime.

OptionDefaultDescription
SessionOwnedIntentCeilingMs0Maximum age for session-owned write intents and no-expiry range locks after their owner disappears. 0 derives the ceiling from the transaction timeout, reaper grace, and participant-effect TTL. Prepared durable intents are exempt.
StagedBaseFenceRetentionMs600000How long the prepared-intent store remembers recently committed heads for validated-base fencing. Keep this above the longest transaction lifetime allowed by the deployment. Use the same value on every node when OnePhaseApplyTimeValidation is enabled.
OnePhaseApplyTimeValidationfalseAllows eligible read-modify-write and read-carrying durable transactions to use the one-phase fast path in multi-process Raft groups by validating their reads and bases at apply time against the replicated committed-head ledger. Enable only after every node in the group supports the ledger and gate.
DefaultTransactionTimeout5000Default transaction timeout in milliseconds.
MaxTransactionTimeout300000Maximum admitted interactive transaction timeout in milliseconds. Caller-provided timeouts are clamped to this bound.
MaxConcurrentTransactions0Script transactions that may execute concurrently before further ones queue and start in priority order. 0 disables the script admission gate.
MaxConcurrentSessions0Interactive transaction sessions that may be open concurrently before further ones queue and start in priority order. 0 disables the session admission gate.
TransactionPriorityReservedSlots0Slots out of each transaction concurrency ceiling that only High and Critical transactions may occupy.
TransactionPriorityAgingThreshold1000Milliseconds a queued transaction waits to gain one effective priority level. 0 disables aging.
TransactionPriorityMaxQueued4096Callers that may wait for an admission slot per gate before further ones receive AdmissionRefused. 0 makes the queue unbounded.
DefaultAdmissionWaitMs5000Admission wait used when the caller does not specify one.
MaxAdmissionWaitMs30000Maximum admission wait allowed by the embedded node. Caller-supplied waits are clamped to this value.
StagedWriteIntentLeaseMs15000Lease for staged writes before the commit probe. A successful probe holds the original intent through commit; cleanup or the session-owned ceiling bounds the hold.
ScriptActorTurnstrueLet eligible auto-commit scripts over one ephemeral key run inside one actor turn.
FusedEphemeralFinalizetrueLet a transaction whose whole write set is one ephemeral key finalize in one actor turn.
ScriptCacheExpiration1 minuteHow long parsed scripts stay cached.
MaxScriptLength65536Largest transaction script accepted, in bytes. Oversized scripts are refused before parsing.
MaxScriptDepth256Deepest transaction script syntax tree accepted. This bounds parser and evaluator stack use.
TransactionOutcomeRetentionMax10000Strict maximum retained terminal transaction outcomes. A non-positive value disables best-effort outcome retention.
TransactionOutcomeRetentionTtl5 minutesAge window for retained terminal transaction outcomes. A non-positive value disables age-based removal.

Client Request Limits​

These options configure public REST/gRPC entry points; direct embedded operations do not inherit the key/value size, batch, or transaction mutation limits. Snapshot-hold bounds also apply to in-process hold calls. See Key/Value Request Limits. 0 disables each bound.

OptionDefaultDescription
MaxKeyBytes16384Maximum UTF-8 key bytes on client writes and transaction lock acquisition.
MaxValueBytes4194304Maximum value or compare-value bytes on client writes.
MaxBatchItems20000Maximum items in a client batch call.
MaxTransactionMutations20000Separate written-key and acquired-lock limits in client-started transactions.
SnapshotHoldMaxLeaseMs86400000Maximum requested lease on snapshot-hold acquire and renew.
SnapshotHoldMaxPerHolder4096Maximum registered snapshot holds per holder.
SnapshotHoldMaxTotal65536Maximum registered snapshot holds cluster-wide.
AdminApiKeyFile""Admin key file path. A custom HTTP/gRPC host must also register the gate.

Caching and Collection​

Related server flags: Workers and Runtime.

OptionDefaultDescription
RevisionsToKeepCached4Number of key revisions to keep cached in memory.
TrimFlushedRevisionsfalseTrim persistent archived revisions after backend flush, retaining unflushed revisions and the snapshot-hold boundary. Historical reads may fall back to disk more often.
PinnedValuePayloadsfalseCopy qualifying committed payloads to the pinned object heap; trades allocation/copy work for less GC relocation.
PinnedValuePayloadMinBytes256Minimum payload size for pinned allocation when enabled.
CacheEntryTtl5 minutesAge threshold used by lock cleanup and legacy cleanup paths. Key/value LRU eviction is budget-based.
CacheEntriesToRemove1000Maximum entries removed by cleanup paths that use this cap. Key/value collection uses CollectBatchMax.
CollectionInterval60 secondsInterval for cache collection and eviction checks.
MaxEntriesPerActor50000Maximum cached entries per actor before collection pressure applies.
MaxBytesPerActor268435456Approximate maximum cached bytes per actor before collection pressure applies.
CollectBatchMax1000Maximum number of entries evicted in one collection pass.
RevisionRetention4Recent revisions per key in memory. Persistent history can fall back to disk; ephemeral history older than this has no disk copy.
DirtyObjectsWriterDelay1000Delay between dirty object writer flush passes, in milliseconds. Longer values can increase batching but keep dirty persistent entries pinned in memory longer.
CheckpointInterval30 secondsMinimum checkpoint cadence for dirty partitions after flushes. Shorter intervals advance WAL compaction sooner; longer intervals reduce checkpoint churn.

Durable Transactions​

Related server flags: Durable Transaction Retention.

OptionDefaultDescription
CompletionReceiptRetentionTtl10 minutesAge after which orphaned durable completion receipts can be dropped once their transaction record has already been reclaimed.
DurableDecisionOutstandingMax100000Maximum outstanding undecided canonical durable transaction records admitted by this node. Completed records do not count against this budget.
DurableDecisionDeadlineFloorMs5000Lower clamp for the durable transaction decision-deadline margin.
DurableDecisionDeadlineCeilingMs60000Upper clamp for the durable transaction decision-deadline margin.
DurableDecisionDeadlineMaxExtensions8How many times one finalize attempt may renew its decision-deadline lease while it waits productively, for example on settlement of a decided predecessor, so slow settlement does not turn a healthy commit into a presumed abort. 0 disables renewal.
DurableRecordRetentionMax200000Maximum retained terminal durable transaction records per node before older records are reclaimed early. A non-positive value disables the count budget.
DurableRecordRetentionMaxBytes268435456Estimated heap-byte budget for terminal durable transaction records plus completion receipts. A non-positive value disables the byte budget.
DurableRecordRetentionHeapPressure0.85Managed-heap load ratio above which terminal records older than the retention floor are reclaimed aggressively. A non-positive value disables this pressure valve.
DurableRecordRetentionFloor90 secondsMinimum age below which terminal durable records are not reclaimed early by count, byte, or heap-pressure budgets.
DurableMaintenanceInterval5 secondsTick interval for prepared-intent recovery and durable record-retention sweeps. TimeSpan.Zero uses CollectionInterval.
DurableRecordGcMaxPerPass4096Maximum terminal transaction records reclaimed in one retention sweep batch.
DurableDeferredSettlementtrueReturn from durable commit once the canonical decision record is durable, then materialize values and settle intents in the background. Set false to await settlement inline.
DurableMaterializeOnResolvefalseInstall values from local prepared intents during settlement apply, before removing them. Overrides by-reference materialization; enable only after every replica supports this encoding. See Durable Settlement.
DurableMaterializeByReferencetrueMaterialize committed durable transactions with a value-free record that references the prepared intent already held by each replica. Set false only during mixed-version rollouts from builds that cannot apply MaterializeIntent.
DurablePreparedIntentMaxCount500000Resident prepared-intent count bound for durable transactions. A non-positive value disables the count bound.
DurablePreparedIntentMaxBytes1073741824Resident prepared-intent value-byte bound for durable transactions. A non-positive value disables the byte bound.

Key/Value Watch and History​

Related server flags: Key/Value Watch and History. See Watch Persistent Key Changes.

OptionDefaultDescription
CommitAdmissionLagMs0Milliseconds a commit timestamp may trail the log entry that carries it. A positive value enables watch progress watermarks and resume; 0 keeps live watches only. Use the same value on every node, and only when every node understands it.
HistoryHoldMaxLeaseMs86400000 (one day)Longest lease a history hold may request. 0 removes the bound.
HistoryHoldMaxPerHolder4096Maximum history holds one holder may register. 0 removes the bound.
HistoryHoldMaxTotal65536Maximum history holds in the registry. 0 removes the bound.
WatchMaxCapacity65536Largest frame capacity a watch may request.
WatchMaxBufferedBytes67108864 (64 MiB)Bytes one watch may buffer for a slow reader before it ends with an overflow.
WatchMaxPerNode65536Most watches open on the node at once. 0 removes the bound.
WatchMaxPerCall1024Most watches one gRPC watch call may hold open. 0 removes the bound.
WatchMaxConcurrentReplays64Most watch replays (one per resumed partition) the node runs at once. 0 removes the bound.
TransactionOriginTagsfalseKeep a transaction's origin label in its prepared intents so watch events carry it. Enable only when every node understands it.

Key/Value Write Coalescing​

Related server flags: Key/Value Write Coalescing.

OptionDefaultDescription
KeyValueWriteLingerMs1Delay from the oldest queued persistent partition write before a partition batch is proposed. 0 dispatches an idle partition immediately.
KeyValueWritePostCompletionHoldMs0Optional hold after a partition batch completes before the next sub-threshold batch is dispatched. This can improve batch density under saturated same-partition write load.
KeyValueWriteMaxBatchItems512Maximum log entries selected for one partition write coalescing Raft call.
KeyValueWriteMaxInFlightBatchesPerPartition1Maximum coalesced batches a partition may have awaiting Raft results at once. Higher values pipeline quorum waits while preserving FIFO dispatch order.
KeyValueWriteMaxBatchBytes4194304Target serialized bytes selected for one partition write coalescing Raft call.
KeyValueWritePreciseWakefalseUse one dedicated timer thread per node for linger, hold, and queue-age wakes; sleeps whole milliseconds and spins the remainder.
KeyValueWriteFillTargetTriggerfalseDispatch held/lingering buffers early at the learned submission-count target, with periodic timed probes.
PublishedHeadReadstrueServe quiet resident latest point gets from immutable published heads after leadership confirmation; fall back to actors whenever guards fail.
KeyValueWriteMaxQueuedItemsPerPartition8192Maximum admitted persistent submissions per partition, including writes already in flight.
KeyValueWriteMaxQueuedBytesPerPartition33554432Maximum admitted serialized bytes per partition, including writes already in flight.
KeyValueWriteMaxQueueDelayMs1000Maximum pre-dispatch wait before a queued write is released as MustRetry.
MaxKeyValueWriteAggregatorInboxSize16384Ordinary-submission inbox bound per aggregator lane. Control messages are exempt.
PersistenceMaxUnflushedItems1000000Maximum committed key/value writes held in memory awaiting background flush before ordinary writes receive retryable backpressure. 0 disables the bound.
PersistenceMaxUnflushedBytes536870912Maximum value bytes held in memory awaiting background flush before ordinary writes receive retryable backpressure. 0 disables the bound.
PersistenceWriteStallWarnMs500Milliseconds an in-flight backend store write may stay unacknowledged before the node logs a stall warning. 0 disables the log lines; the stall gauge and store-write histogram are always published.

Persistent Revision Retention​

Related server flags: Persistent Revision Retention.

OptionDefaultDescription
PersistentRevisionRetentionCount0Maximum persisted revisions retained per key. 0 keeps every revision.
PersistentRevisionRetentionAge0Maximum persisted revision age. TimeSpan.Zero disables age-based retention.
PersistentRevisionCleanupInterval5 minutesMinimum interval between full persistent-revision cleanup sweeps.
PersistentRevisionCleanupBatchSize10000Maximum revision records deleted per cleanup pass.
PersistentRevisionCleanupOnWritetrueQueue keys touched by writes for targeted revision cleanup.
PersistentRevisionCleanupTimeBudget250 msWall-clock budget for one targeted persistent revision cleanup pass during a flush cycle.

Live snapshot holds clamp persistent revision cleanup. While a hold is active, the boundary revision needed by the held timestamp and every newer revision are kept even if PersistentRevisionRetentionCount or PersistentRevisionRetentionAge would otherwise remove them.

Backups and Point-in-Time Recovery​

Related server flags: Backups and Point-in-Time Recovery.

OptionDefaultDescription
PitrWindow1 hourRecoverable WAL history. Values are normalized to more than zero and at most 6 hours.
BaseSnapshotInterval30 minutesIntended interval between base checkpoints. It must be positive and no greater than PitrWindow. It also contributes to the protected WAL floor.
BackupDiremptyRoot directory for backup manifests and artifacts. Backup methods on node.Kahuna are disabled when empty.
BackupTargetlocalBackup storage target. local uses BackupDir; other target names require BackupStorageProvider.
BackupScratchDiremptyLocal staging directory for backup targets that cannot receive checkpoints directly. Size it for one full backup.
BackupStorageProvidernullHost-supplied factory for object storage or another non-local backup target. Null uses the local directory implementation.
BackupClusterIdemptyOperator-assigned cluster identity stamped into backup manifests. Use the same value on every node.
BackupMacKeyFileemptyPath to the HMAC-SHA-256 key file used to authenticate backup manifests. Keep it outside BackupDir.
RestoreRootemptyServer-owned root directory that restore targets must be contained within. Setting it enables confined remote restore.
AllowUnconfinedRemoteRestorefalseAllows remote restore without RestoreRoot. Use only in trusted administrative environments.
BackupRetentionMaxChains0Keep at most this many most-recent backup chains. 0 is unbounded. Retention is off unless at least one retention bound is set.
BackupRetentionMaxAge0Delete backup chains whose newest backup is older than this age. TimeSpan.Zero is unbounded.
BackupRetentionMaxBytes0Keep the most-recent backup chains within this artifact byte budget. The newest chain is always kept. 0 is unbounded.
BackupGcInterval1 hourPeriodic backup garbage-collection cadence. TimeSpan.Zero disables the periodic pass, but GC still runs after backups.
BackupCaptureThrottleBytesPerSec0Per-capture byte budget for RocksDB image trim/compaction, hash/verify, and staged copy. Non-positive is unlimited.
BackupApplyBarrierTimeout30 secondsFull-capture apply wait; a lagging replica refuses with PartitionCaptureFailed. Non-positive uses the default.
BackupRestoreThrottleBytesPerSec0Throughput budget for a restore checkpoint copy. 0 is unlimited.

Key-Range Split and Merge​

Related server flags: Key-Range Split and Merge.

OptionDefaultDescription
RangeSplitThreshold1000Sampled key count that triggers count-based range splitting. 0 disables this trigger.
RangeSplitMinRangeSize10Minimum sampled keys required in each child range.
RangeMergeMinSize10Adjacent key ranges smaller than this value can be considered for automatic merging. 0 disables automatic merge.
RangeSplitLoadThreshold0Replicated writes per second required for load-based splitting. 0 disables this trigger.
RangeSplitLoadMinQueueDepth8WAL backlog required alongside the load threshold.
RangeSplitLoadMinCommitWaitMs0Optional commit-wait gate in milliseconds. 0 disables it.
RangeSplitLoadWindow15 secondsTime the complete load predicate must remain satisfied.
RangeSplitLoadPollInterval5 secondsFrequency of load-based split checks.
RangeSplitLoadImbalanceMax0.8Maximum acceptable write fraction assigned to either child.
RangeSplitIndivisibleCooldown5 minutesDelay before reconsidering an indivisible range.
RangeSplitSettleWindow10 secondsPost-split delay before evaluating either child again.
RangeMoveSettleTimeout10 secondsMaximum quiesce wait for in-flight transactions in a moving range before split or merge cutover.
RangeMoveQuiesceTtl30 secondsLifetime of a split/merge write fence. An exhausted window refuses cutover. Minimum 1 second.
RangeMaxRangesPerKeySpace0Per-key-space range quota; 0 is unbounded.

Leader Balancing​

Related server flags: Raft Leader Balancing.

OptionDefaultDescription
EnableLeaderBalancerfalseEnable cross-node load reports and leader redistribution. Required with load splitting.
LeaderBalancerReportInterval5 secondsInterval between node load reports.
LeaderBalancerInterval30 secondsInterval between balancing passes.
LeaderBalancerReportTtl20 secondsMaximum accepted load-report age.
MinLeaderStability5 secondsMinimum leadership age before transfer.
LeaderBalancerOpsWeight1.0Operations-per-second weight in the balancer load score.
LeaderBalancerQueueWeight0.5Queue-depth weight in the balancer load score.

Replica Placement and Membership​

Related server flags: Replica Placement.

OptionDefaultDescription
MaxPartitions0Maximum data partitions cluster-wide; 0 is unbounded. A positive value must be at least InitialPartitions. Set identically on all nodes.
ReplicationFactor0Desired voter replicas per partition. 0 keeps full replication. Prefer odd values such as 3 or 5 in multi-node deployments.
EnablePlacementRebalancerfalseEnable ongoing replica-placement repair and balancing. Initial placement still applies when ReplicationFactor is positive.
JoinTimeout60 secondsJoin admission/initialization ceiling; does not bound the learner promotion wait.
JoinPromotionStallTimeout5 minutesNo-progress ceiling during learner promotion, not total catch-up duration.
StartupLeaderWaitTimeout.InfiniteTimeSpanHow long StartAsync waits for a partition to elect a leader before the start fails. The default waits until the start is cancelled or the node is disposed, so a node that restarts during a leaderless moment keeps its vote available. Set a finite value only when a supervisor restarts the process instead.
PlacementPassInterval5 secondsCadence for placement-controller passes. Commit-triggered passes can still run immediately.
MaxReplicaMovesPerPass4Maximum new replica add/remove sequences started in one placement pass across repair and balance priorities.
MaxConcurrentReplicaTransfers1Maximum partitions with an in-flight learner catch-up or replica removal caused by balance moves.
MaxConcurrentReplicaRepairs3Maximum in-flight repair moves for under-replicated ranges or replicas stranded on departed nodes.
DecommissionDrainTimeout2 minutesGraceful leave wait for evacuating this node's hosted replicas before removal.
ReplicaCountDeadband1Replica-count imbalance tolerated before balance moves start.
ZonenullOptional zone or rack hint used to spread replicas across failure domains.
EnableLoadReportsfalseGossip per-partition load reports even when leader balancing, placement rebalancing, or replication factor did not already enable them.

Raft Communication​

Related server flags: Raft Communication.

OptionDefaultDescription
ReadIOThreads4Number of Raft read I/O threads.
WriteIOThreads8Number of Raft write I/O threads.
EnableSharedExecutorPooltrueShare a bounded worker pool across Raft partitions instead of using one OS thread per partition.
PartitionExecutorPoolSize0Shared Raft executor worker count. 0 auto-sizes to the processor count.
EnableHostPumpedSchedulingtrueThread-free build (net10.0-browser) only. Runs Kommander's scheduling work as async continuations on the host's scheduler, such as the browser event loop, instead of on worker threads. Requires EnableSharedExecutorPool and memory storage and WAL. Set it to false only when something else pumps the node.
HttpSchemehttps://HTTP scheme used by Raft REST communication.
HttpAuthBearerTokenemptyBearer token sent with Raft REST communication.
TransportSecuritynullOptional node-to-node transport security options passed to Kommander. Embedded in-memory peers do not need it, but custom hosts that expose Kahuna gRPC services on a real transport use it for node-only trust checks.
HttpTimeout5Raft REST request timeout in seconds.
HttpVersion2.0HTTP protocol version used by Raft REST communication.

Snapshot receive staging and deadline options (RaftSnapshotMaxPendingBytes, RaftSnapshotMaxPendingSessions, RaftSnapshotStagingDirectory, RaftSnapshotStagingMemoryBytes, RaftSnapshotChunkAckTimeout, and RaftSnapshotTransferStepTimeout) are nullable and keep Kommander defaults unless set. Their defaults, validation rules, automatic staging directory, and an embedded example are in Snapshot Options. Server follower-apply scheduling and live-replica lag-window/cap flags have no matching embedded options.

Raft Timing​

Related server flags: Raft Timing.

OptionDefaultDescription
HeartbeatInterval100 msLeader heartbeat interval.
RecentHeartbeatHeartbeatInterval / 4Recent-heartbeat de-duplication window. Leave unset to track the heartbeat cadence safely; set explicitly only when it remains below HeartbeatInterval.
VotingTimeout1500 msVote wait timeout.
EnableCheckQuorumtrueClustered embedded nodes honor this option. The standalone node constructor forces it off, along with backfill, because its phantom witnesses are not real replicas.
CheckQuorumIntervalMultiplier0Heartbeat intervals without majority acknowledgement before check-quorum steps down a leader. 0 derives the window from StartElectionTimeout.
CheckLeaderInterval250 msLeader check interval.
TimerInitialDelay2500 msInitial delay before Raft timers start.
UpdateNodesInterval5000 msNode registry update interval.
StartElectionTimeout500Minimum election timeout in milliseconds.
EndElectionTimeout1500Maximum election timeout in milliseconds.
StartElectionTimeoutIncrement100Minimum election timeout increment in milliseconds.
EndElectionTimeoutIncrement200Maximum election timeout increment in milliseconds.

Raft Logging, Batching, and Compaction​

Related server flags: Raft Logging and Compaction.

OptionDefaultDescription
SlowRaftStateMachineLog50Slow state-machine operation log threshold in milliseconds.
SlowRaftWALMachineLog25Slow WAL state-machine operation log threshold in milliseconds.
RaftMaxWalGroupBatchPartitions64Maximum partitions coalesced into one cross-partition WAL group-commit batch.
RaftWalGroupCommitLingerMs0Optional group-commit linger window in milliseconds. 0 disables linger.
RaftWalSingleFsyncCommitfalseEnables Kommander's single-fsync commit fast path for embedded nodes. Server defaults differ; embedded hosts opt in explicitly.
CompactEveryOperations1000Number of committed operations between automatic Raft WAL compaction checks.
CompactNumberEntries50Number of Raft WAL entries removed per compaction batch.
MaxEntriesPerCompaction5000Maximum Raft WAL entries processed per compaction run.

Notes​

  • The embedded node uses in-memory Raft and inter-node communication.
  • StartAsync joins the single-node cluster and waits for leaders for the configured partitions.
  • WaitForLeaderForKeyAsync waits for the partition that owns a specific key.
  • Use distinct StoragePath and WalPath values when using sqlite or rocksdb.
  • Set BackupDir to enable backup, catalog, and offline restore methods through node.Kahuna. See Backups and Point-in-Time Recovery.
  • Load-based splitting requires a multi-node embedded deployment, key-range-routed spaces, and EnableLeaderBalancer = true. See Load-Based Range Splitting.
  • Positive ReplicationFactor values are intended for multi-node embedded deployments. 0 keeps the single-node/full-replication default. See Replication Factor and Replica Placement.
  • Use EmbeddedKahunaCluster.CreateInMemoryAsync when tests need leader election, failover, restart, or network-partition behavior without Docker or sockets.
  • Always dispose the node with await using or DisposeAsync to stop local Raft/actors and release resources. Disposal is not an operator membership-removal guarantee; explicitly decommission clustered nodes when removing them from the roster.

Disposal and In-Flight Work​

DisposeAsync first signals background maintenance to stop, then drains the partition write aggregator while Raft and replication callbacks are still attached. Queued writes are released retryably; in-flight durable batches can still obtain ordered apply results. The write drain has a five-second budget. After callbacks detach, parked durable completions are released as unobserved rather than waiting for an apply that can no longer arrive.

Raft is then disposed and actors receive a separate five-second graceful-stop budget. These are per-stage limits, not a guarantee that the entire dispose takes five seconds or that every pending transaction and backend row is flushed. A shutdown result can remain uncertain; do not infer rollback from an unobserved completion. Logs report drain, Raft-dispose, and actor-stop durations for diagnosing slow teardown.

Internal Defaults​

These KahunaConfiguration fields keep their internal defaults: neither Kahuna.Server command-line flags nor EmbeddedKahunaOptions expose them. They are listed for reference:

OptionDefaultDescription
ScriptCacheMaxEntries1000Maximum parsed scripts retained in the server-side script cache. New entries are dropped when the limit is reached.
MaxKeyValueActorInboxSize16384Maximum ordinary user messages queued in one key/value actor before new ordinary messages receive retryable backpressure. Control messages such as completions, cache-coherence updates, and maintenance are exempt. 0 disables the bound.
KeyValueWriteTerminalReserveItemsPerPartition256Extra per-partition item headroom reserved for terminal durable transaction work such as decision, materialization, settlement, recovery, and metadata handoff.
KeyValueWriteTerminalReserveBytesPerPartition4194304Extra per-partition byte headroom reserved for terminal durable transaction work.
KeyValueWriteMaxQueuedItemsGlobal131072Node-wide ordinary submission item cap across all partitions.
KeyValueWriteMaxQueuedBytesGlobal536870912Node-wide ordinary submission byte cap across all partitions.
KeyValueWriteTerminalReserveItemsGlobal8192Node-wide item headroom reserved for terminal durable transaction work.
KeyValueWriteTerminalReserveBytesGlobal67108864Node-wide byte headroom reserved for terminal durable transaction work.
KeyValueWriteMaxOperationBytes67108864Hard ceiling for one admitted serialized partition write. Values above the ceiling are rejected retryably.
KeyValueWriteBatchExecutionTimeoutMs30000Maximum Raft round-trip time for one aggregator batch before the batch is released retryably.
DurableRecoveryMaxPartitionsPerPass64Maximum partitions driven by prepared-intent recovery in one sweep.
DurableDecisionDeadlineMultiplier4Multiplier applied to the larger of total finalize p99 and per-intent p99 × manifest size, before clamping the decision-deadline margin.
SnapshotHoldStartupGraceWindow5 minutesGrace window for durable snapshot holds loaded after restart. Expired holds restored from disk are protected briefly so holders can renew before cleanup advances the snapshot floor.

HttpsTrustedThumbprint also exists on KahunaConfiguration, but it is derived from HttpsCertificate by configuration validation rather than being an independent operator setting.