Configuration
CamusDB reads Config/config.yml at startup, merges explicit command-line
flags, validates the result, and then applies the resolved configuration before
the server starts accepting work.
Precedence is:
- command-line flags
- environment variables
Config/config.yml- built-in defaults
Only flags you pass explicitly override YAML values. Omitting a flag keeps the
value from config.yml.
Default File
The source repository ships a commented configuration reference at
CamusDB/Config/config.yml. Its active settings are:
data_dir: /tmp/camusdb/
initial_partitions: 3
The commented sections show the available server, cluster, transaction,
recoverable-drop, parser, and Kahuna engine settings. Authentication secrets
are intentionally not configured in YAML; see
Authentication And Authorization. For real
deployments, set data_dir to persistent storage instead of relying on /tmp.
Unified Reference
Most startup settings can now be configured either in config.yml or by a
matching command-line flag. CLI flags are useful for containers, scripts, and
per-node cluster settings; YAML is better for stable node configuration.
| YAML key | CLI flag | Default | Purpose |
|---|---|---|---|
data_dir | --data-dir | empty, with runtime fallback | Base directory for persisted database files. |
mode | --mode | standalone | Run mode: standalone or cluster. |
node_name | --raft-nodename | empty | Optional cluster node name; cluster mode falls back to the machine name when empty. |
raft_node_id | --raft-nodeid | 1 | Numeric Raft node id. Must be greater than 0. |
raft_host | --raft-host | localhost | Host address used for Raft communication. |
raft_port | --raft-port | 7070 | Port used for Raft gRPC traffic. |
initial_partitions | --initial-cluster-partitions | 3 | Number of Raft partitions to initialize. |
peers | --initial-cluster | empty list | Static Raft peer list in host:port form. |
http_peers | --http-peers | empty list | Per-peer HTTP addresses, parallel to peers. |
schema_ack_wait_timeout_ms | --schema-ack-wait-timeout-ms | 30000 | DDL schema-ack wait timeout in milliseconds. |
schema_ack_live_node_lease_ms | --schema-ack-live-node-lease-ms | 30000 | DDL live-node lease in milliseconds, or -1 for infinite. |
http_port | --http-port | 5095 | HTTP API listener port. |
https_port | --https-port | 7141 | HTTPS API listener port when a certificate is configured. |
https_certificate | --https-certificate | empty | Path to a PFX certificate for the HTTPS API listener; empty disables HTTPS. |
raft_certificate | --raft-certificate | empty | Path to a PFX certificate for the Raft gRPC listener in cluster mode. |
require_tls_when_auth_enabled | --require-tls-when-auth-enabled | true | When authentication is enabled, reject credential-bearing requests over plaintext except loopback. Set false only behind a trusted TLS terminator. |
The remaining YAML settings do not currently have command-line flags:
| YAML key | Default | Purpose |
|---|---|---|
grpc_enabled | true | Enable the client-facing gRPC API on a dedicated HTTP/2 listener. |
grpc_port | 5096 | Port for the client-facing gRPC API when grpc_enabled is true. |
grpc_batch_max_in_flight | 64 | Maximum concurrently executing operations per CamusSql.BatchExecute stream before backpressure. |
default_isolation_level | serializable | Default transaction isolation when a request does not choose one. |
default_transaction_locking | pessimistic | Default transaction locking strategy when a request does not choose one. |
range_lock_expires_ms | 150000 | Initial Serializable range-lock TTL; the coordinator renews live range locks; positive values must be at least 2x the effective Kahuna collection interval; <= 0 disables expiry. |
max_serializable_transaction_lifetime_ms | 3600000 | Maximum Serializable read-write transaction lifetime; <= 0 disables the cap. |
transaction_idle_timeout_ms | 300000 | Idle timeout for explicit client transactions before the background reaper rolls them back; <= 0 disables the CamusDB-side reaper. |
transaction_reaper_interval_ms | 30000 | Background sweep interval for abandoned explicit transactions. |
prepared_statement_idle_timeout_ms | 600000 | REST prepared-statement idle timeout; <= 0 disables REST handle reaping. |
prepared_statement_sweep_interval_ms | 60000 | Background sweep interval for expired REST prepared statements. Must be greater than 0. |
grpc_max_prepared_statements_per_stream | 512 | Maximum prepared statements per CamusSql.BatchExecute stream; 0 means unbounded. |
rest_max_prepared_statements_per_principal | 512 | Maximum REST prepared statements per principal on one node; 0 means unbounded. |
rest_max_prepared_statements | 8192 | Node-wide REST prepared-statement cap across principals; 0 means unbounded. |
max_prepared_statement_bytes | 65536 | Maximum SQL text size for one prepared statement; 0 means unlimited. |
grpc_max_prepared_statement_bytes_per_stream | 8388608 | Retained prepared SQL byte budget per gRPC batch stream; 0 means unlimited. |
rest_max_prepared_statement_bytes_per_principal | 8388608 | Retained prepared SQL byte budget per REST principal; 0 means unlimited. |
rest_max_prepared_statement_bytes | 67108864 | Node-wide retained REST prepared SQL byte budget; 0 means unlimited. |
orphan_retention_ms | 604800000 | Recoverable orphan retention window for normal root database/table drops; <= 0 keeps orphans indefinitely. |
orphan_reclaim_interval_ms | 300000 | Background sweep interval for reclaiming expired orphans; <= 0 disables automatic reclamation. |
lock_escalation_threshold | 50 | Shared point-lock count per bucket before escalation. |
lock_wait_deadline_ms | 500 | Per-operation Serializable conflict wait cap. |
key_range_sharding | false | Opt tables and eligible indexes into Kahuna key-range routing. |
stats_flush_interval_ms | 5000 | Advisory table-statistics flush interval. |
stats_analyze_sample_rows | 100000 | Manual ANALYZE full-scan/sample threshold; 0 means always full scan. |
stats_histogram_buckets | 100 | Histogram bucket count built per analyzed column. |
auto_analyze_enabled | false | Enable background automatic table analyze. |
auto_analyze_check_interval_ms | 60000 | Automatic analyze staleness sweep interval; <= 0 disables the loop. |
auto_analyze_fraction_stale_rows | 0.20 | Proportional staleness trigger for automatic analyze. |
auto_analyze_min_stale_rows | 500 | Minimum mutations before a table is stale for automatic analyze. |
auto_analyze_max_concurrent | 1 | Maximum background analyses running at once on a node. |
auto_analyze_max_rows_per_second | 50000 | Background analyze scan-rate throttle; <= 0 disables throttling. |
auto_analyze_histogram_sample_rows | 10000 | Reservoir sample size per column for background histograms. |
auto_analyze_hll_precision | 11 | HyperLogLog precision for automatic analyze distinct-value estimates; valid range 4..16. |
auto_analyze_load_pause_threshold | 16 | In-flight foreground work threshold above which automatic analyze backs off; <= 0 disables load backoff. |
auto_analyze_ownership_check_rows | 1000 | Rows between background analyze ownership/load re-checks. |
cost_based_access_path_enabled | false | Enable cost-based selection among viable table/index access paths. |
cost_based_join_order_enabled | false | Enable cost-based left-deep join-order enumeration for eligible joins. |
plan_cache_enabled | false | Enable the per-process query plan cache. |
plan_cache_max_entries | 512 | Maximum plan-cache entries; 0 effectively disables caching. |
regex_match_timeout_ms | 250 | Per-match timeout for regex operators and regex scalar functions. |
regex_cache_max_entries | 1024 | Maximum compiled regex patterns cached per process; 0 disables caching. |
engine_metrics_enabled | true | Observe embedded Kahuna/Kommander engine metrics for SHOW ENGINE STATS. |
query_result_cache_enabled | true | Enable the per-node in-memory query result cache. Queries still opt in individually with {cache=...}. |
query_result_cache_default_ttl_ms | 5000 | Default TTL for result-cache entries without a per-query ttl. |
query_result_cache_max_entries | 1024 | Maximum result-cache entries per process. |
query_result_cache_max_bytes | 67108864 | Total result-cache byte budget per process. |
query_result_cache_max_entry_bytes | 1048576 | Maximum bytes for a single result-cache entry. |
query_result_cache_max_entry_rows | 10000 | Maximum rows for a single result-cache entry. |
query_result_cache_max_deps | 4096 | Maximum dependency facts captured for one result-cache entry. |
query_result_cache_max_point_deps | 2048 | Maximum point-key dependencies captured for one result-cache entry. |
query_result_cache_max_ranges | 256 | Maximum range dependencies captured for one result-cache entry. |
query_result_cache_singleflight_wait_ms | 250 | Reserved single-flight wait setting for result-cache misses. |
query_result_cache_strict_validation_max_keys | 10000 | Maximum keys probed during strict validation before failing closed. |
query_result_cache_sweep_interval_ms | 10000 | Background TTL sweep interval. |
sql_parser_cache_ttl_seconds | 300 | Sliding TTL for cached SQL ASTs; 0 disables the parser cache. |
sql_parser_cache_max_entries | 2048 | Maximum cached SQL texts; 0 means unbounded. |
sql_parser_cache_sweep_seconds | 60 | Background sweep interval for expired parser-cache entries. |
max_identifier_length | 64 | Maximum length for database, table, column, and index names; <= 0 disables the limit. |
max_columns_per_table | 512 | Maximum user-declared columns per table; <= 0 disables the limit. |
max_indexes_per_table | 64 | Maximum user-visible secondary indexes per table; <= 0 disables the limit. |
max_tables_per_database | 10000 | Maximum tables per database; <= 0 disables the limit. |
max_index_columns | 32 | Maximum key plus INCLUDE columns in one index; <= 0 disables the limit. |
max_index_include_tuple_bytes | 4096 | Maximum encoded bytes for one index entry's key plus included-column payload; <= 0 disables the limit. |
max_mutations_per_transaction | 20000 | Maximum row/index mutations in one user transaction; <= 0 disables the user limit. |
spill_enabled | false | Enable spill-to-disk for blocking query operators. |
spill_threshold_rows | 500000 | Per-operator in-memory row cap before spilling starts. |
spill_merge_fan_in | 16 | Maximum spill runs read at once during merge passes. |
branch_snapshot_hold_lease_ms | 300000 | Lease window for branch snapshot-floor holds on parent MVCC history. |
kahuna | empty mapping | Allow-listed storage and Raft engine overrides. |
diagnostics | disabled mapping | Opt-in standalone Prometheus/OpenTelemetry diagnostics. |
Cluster mode is active when either mode: cluster is set or peers contains
at least one entry.
Server And Cluster Settings
data_dir
data_dir controls where CamusDB stores persistent state.
Both standalone and cluster mode use a shared storage node. The KV backend, WAL backend, query spill files, and other process-level durable/runtime files are created under this base path. Databases are separated by stable database-id key prefixes in the shared keyspace, not by per-database directories.
Use persistent storage here if you expect data to survive restarts.
peers and http_peers
peers contains the Raft endpoints used for static cluster discovery:
peers:
- 192.168.1.10:7070
- 192.168.1.11:7072
- 192.168.1.12:7074
http_peers maps each Raft peer to its HTTP API endpoint:
http_peers:
- 192.168.1.10:5095
- 192.168.1.11:5096
- 192.168.1.12:5097
Rules:
http_peers, when present, must have the same number of entries aspeers.- Entries must be valid
host:portvalues. - The
peersentry should byte-match the endpoint format reported by Raft.
If http_peers is omitted, CamusDB falls back to a uniform-port heuristic: it
uses the Raft endpoint host with this node's http_port.
HTTP And TLS
CamusDB always binds the HTTP API on http_port.
If https_certificate is set, CamusDB also binds https_port using that PFX
certificate. In cluster mode, raft_certificate enables TLS on the Raft gRPC
listener.
http_port: 5095
https_port: 7141
https_certificate: /etc/camusdb/api.pfx
raft_certificate: /etc/camusdb/raft.pfx
When authentication is enabled, CamusDB rejects credential-bearing requests over plaintext by default, except loopback development traffic. If TLS terminates in front of the node, keep that plaintext hop inside the trust boundary and set:
require_tls_when_auth_enabled: false
or:
camusdb --require-tls-when-auth-enabled false
Client gRPC
The client-facing gRPC API is enabled by default on a dedicated HTTP/2 listener:
grpc_enabled: true
grpc_port: 5096
grpc_batch_max_in_flight: 64
Set grpc_enabled: false when you only want the HTTP API listener. The gRPC
listener is separate from the REST/JSON API on http_port. It exposes the
CamusSql and CamusRows services described in gRPC API.
When raft_certificate is configured, CamusDB reuses it for TLS on the gRPC
listener. Without raft_certificate, the gRPC listener uses plaintext HTTP/2.
grpc_batch_max_in_flight bounds the number of concurrently executing
operations per CamusSql.BatchExecute duplex stream before the server applies
backpressure.
Runtime Log Verbosity
CamusDB also reads optional environment variables that tune console logging at
process startup. These are useful in containers because they do not require
editing appsettings.json or rebuilding the image.
| Environment variable | Purpose |
|---|---|
CAMUS_LOG_LEVEL | Default log level for categories without a more specific rule. |
CAMUS_LOG_LEVEL_KAHUNA | Log level for Kahuna categories. Defaults to Warning. |
CAMUS_LOG_LEVEL_KOMMANDER | Log level for Kommander categories. Defaults to Warning. |
CAMUS_LOG_LEVEL_GRPC | Log level for gRPC categories. Defaults to Warning. |
CAMUS_LOG_FILTERS | Comma-separated category filters in Category=Level form. |
Accepted levels are Trace, Debug, Information, Warning, Error,
Critical, and None, case-insensitively. Invalid values are ignored and the
built-in defaults remain active.
Examples:
CAMUS_LOG_LEVEL=Debug dotnet run --project CamusDB
CAMUS_LOG_LEVEL_KAHUNA=Information \
CAMUS_LOG_FILTERS="Microsoft.AspNetCore=Warning,CamusDB.Core=Debug" \
dotnet run --project CamusDB
Authentication Environment
The authentication switch and secrets are configured with environment variables
or an external secret provider, not with config.yml:
| Environment variable | Purpose |
|---|---|
CAMUSDB_AUTH_ENABLED | Set to true to require bearer tokens and enforce privileges. Defaults to off. |
CAMUSDB_AUTH_TOKEN_KEY | HMAC key for access-token secrets. Required when auth is enabled; must match on every cluster node. |
CAMUSDB_BOOTSTRAP_USER | First superuser name when the auth catalog is empty. |
CAMUSDB_BOOTSTRAP_PASSWORD | Initial password for the bootstrap superuser. |
CAMUSDB_NODE_SECRET | Shared node-to-node secret for internal cluster routes when auth is enabled. |
Do not put these values in YAML. See
Authentication And Authorization for bootstrap,
login, grants, TLS, and token usage. The non-secret
require_tls_when_auth_enabled transport policy is configured in YAML or with
--require-tls-when-auth-enabled.
Schema Ack Settings
These settings control the distributed schema two-version gate in cluster mode:
schema_ack_wait_timeout_ms- How long a DDL proposer waits for all live nodes to acknowledge the needed schema version before failing the operation.
schema_ack_live_node_lease_ms- How long since a member's last activity before the schema leader stops blocking on it.
-1means infinite lease.
Most users should keep the defaults unless they are testing cluster DDL behavior or diagnosing slow convergence.
Transaction And Locking Settings
Serializable is the default isolation level:
default_isolation_level: serializable
default_transaction_locking: pessimistic
Use read_committed only when you explicitly want weaker isolation by default.
Individual SQL/API transactions can still request an isolation level.
Use optimistic for default_transaction_locking only when the deployment
wants transactions to avoid explicit locks and validate conflicts at commit by
default. Individual SQL/API transactions can still request a locking strategy.
Locking settings tune Serializable read-write behavior:
range_lock_expires_ms: 150000
max_serializable_transaction_lifetime_ms: 3600000
transaction_idle_timeout_ms: 300000
transaction_reaper_interval_ms: 30000
lock_escalation_threshold: 50
lock_wait_deadline_ms: 500
Operational notes:
range_lock_expires_msis the initial TTL for range locks acquired by Serializable read-write scans. The Kahuna coordinator renews live range locks on its collection tick, so a positive value must be at least2xthe effectivekahuna.collection_interval_ms. The default is150000milliseconds, which is safe with Kahuna's default60000millisecond collection interval.max_serializable_transaction_lifetime_mslimits how long an active Serializable read-write transaction can remain open. The same value is used as the Kahuna transaction coordinator session timeout.transaction_idle_timeout_mscontrols how long an explicit client transaction may sit idle before CamusDB rolls it back. Set it to<= 0to disable the CamusDB-side reaper; Kahuna's transaction session timeout remains the final cleanup backstop.transaction_reaper_interval_mscontrols how often the abandoned transaction reaper scans for idle explicit transactions.lock_escalation_thresholdkeeps lock bookkeeping bounded by escalating many point locks in the same bucket.lock_wait_deadline_mslimits how long a single lock acquisition waits before surfacing a Serializable conflict.
Prepared Statements
Prepared statements register a SQL text once and execute it repeatedly by handle. See Prepared Statements for the client lifecycle.
prepared_statement_idle_timeout_ms: 600000
prepared_statement_sweep_interval_ms: 60000
grpc_max_prepared_statements_per_stream: 512
rest_max_prepared_statements_per_principal: 512
rest_max_prepared_statements: 8192
max_prepared_statement_bytes: 65536
grpc_max_prepared_statement_bytes_per_stream: 8388608
rest_max_prepared_statement_bytes_per_principal: 8388608
rest_max_prepared_statement_bytes: 67108864
Settings:
prepared_statement_idle_timeout_ms: idle cutoff for REST handles. Set it to<= 0to disable REST prepared-statement reaping.prepared_statement_sweep_interval_ms: how often the background reaper scans for expired REST handles. It must be greater than0.grpc_max_prepared_statements_per_stream: count cap for oneCamusSql.BatchExecutestream. Set it to0for unbounded.rest_max_prepared_statements_per_principal: count cap for one principal on one node. Set it to0for unbounded.rest_max_prepared_statements: node-wide REST count cap across principals. Set it to0for unbounded.max_prepared_statement_bytes: maximum SQL text size for a single prepared statement. Set it to0for unlimited.grpc_max_prepared_statement_bytes_per_stream: retained prepared SQL byte budget for one gRPC batch stream. Set it to0for unlimited.rest_max_prepared_statement_bytes_per_principal: retained prepared SQL byte budget for one REST principal. Set it to0for unlimited.rest_max_prepared_statement_bytes: node-wide retained REST prepared SQL byte budget. Set it to0for unlimited.
Exceeding a prepared-statement count or byte budget rejects the new
registration with CADB0521 PreparedStatementLimitExceeded. CamusDB does not
evict an existing handle silently.
Negative values are invalid at startup and return CADB0600 InvalidConfig.
Recoverable Drop Settings
Normal DROP DATABASE and DROP TABLE statements are deferred for root
databases and tables. The object disappears from the active catalog immediately,
but its data is retained as an orphan that can be recovered with
CREATE ... RELINK TO until it is reclaimed.
orphan_retention_ms: 604800000
orphan_reclaim_interval_ms: 300000
Settings:
orphan_retention_ms: how long a dropped database or table remains eligible for recovery. The default is seven days. Set it to0or a negative value to keep orphans indefinitely.orphan_reclaim_interval_ms: how often the background reclaimer checks for expired orphans. The default is five minutes. Set it to0or a negative value to disable the automatic sweep.
Use DROP ... FORCE when you want immediate permanent deletion instead of a
recoverable orphan. See Recover Dropped Objects
for SQL examples and operational guidance.
Key-Range Sharding
Set key_range_sharding: true to opt table row spaces and eligible secondary
index spaces into Kahuna key-range routing:
key_range_sharding: true
CAMUS_KEY_RANGE_SHARDING overrides YAML when it is set. Use 1 or true to
enable it from the environment:
CAMUS_KEY_RANGE_SHARDING=true dotnet run --project CamusDB
Operational notes:
initial_partitionsmust be at least2for key-range routing to have an effect.- With a single partition, enabling the option is safe but effectively a no-op.
- The server logs a warning when key-range sharding is enabled and
initial_partitions < 2.
Table Statistics
CamusDB updates advisory row-count, index-count, and min/max statistics in memory on DML and flushes them to durable storage on a schedule.
stats_flush_interval_ms: 5000
stats_analyze_sample_rows: 100000
stats_histogram_buckets: 100
auto_analyze_enabled: false
auto_analyze_check_interval_ms: 60000
auto_analyze_fraction_stale_rows: 0.20
auto_analyze_min_stale_rows: 500
auto_analyze_max_concurrent: 1
auto_analyze_max_rows_per_second: 50000
auto_analyze_histogram_sample_rows: 10000
auto_analyze_hll_precision: 11
auto_analyze_load_pause_threshold: 16
auto_analyze_ownership_check_rows: 1000
Values:
5000: default; flush at most about once every 5 seconds per table.0: flush after every change; highest write amplification.-1: disable automatic flush; persist on explicit flush or close only.
This affects planner statistics durability, not SQL correctness.
ANALYZE TABLE <name> builds richer statistics used by the cost-based
optimizer, including equi-depth histograms and distinct-value counts.
Automatic analyze can refresh stale table statistics in the background when
auto_analyze_enabled is true. See
Automatic Analyze for staleness rules, resource
limits, and per-table opt-out syntax.
Cost-Based Optimizer
The optimizer can use statistics to compare access paths, join algorithms, and eligible join orders.
cost_based_access_path_enabled: false
cost_based_join_order_enabled: false
plan_cache_enabled: false
plan_cache_max_entries: 512
Values:
cost_based_access_path_enabled: whentrue, CamusDB enumerates viable table/index access paths and chooses the cheapest estimated path. Whenfalse, it uses the stable heuristic index selector with the existing cost-based broad-range veto.cost_based_join_order_enabled: whentrue, CamusDB uses a System-R-style dynamic program to choose a cheaper connected left-deep order for eligible inner joins. Whenfalse, it uses the heuristic join order.
Both flags default to false. Missing statistics or unsupported query shapes
fall back to the heuristic planner.
The plan cache is also opt-in:
plan_cache_enabled: whentrue, CamusDB can reuse a cached optimization decision for the same query shape and compatible schema versions.plan_cache_max_entries: maximum number of cached plan entries. Set to0to keep caching effectively disabled even ifplan_cache_enabledistrue.
The cache is per process and does not change query correctness. It may preserve an older access-path choice until schema changes invalidate the cached entry or the process restarts.
Query Result Cache
The query result cache stores fully materialized result sets for repeated
single-table reads that explicitly opt in with a {cache=...} hint.
query_result_cache_enabled: true
query_result_cache_default_ttl_ms: 5000
query_result_cache_max_entries: 1024
query_result_cache_max_bytes: 67108864
query_result_cache_max_entry_bytes: 1048576
query_result_cache_max_entry_rows: 10000
query_result_cache_max_deps: 4096
query_result_cache_max_point_deps: 2048
query_result_cache_max_ranges: 256
query_result_cache_singleflight_wait_ms: 250
query_result_cache_strict_validation_max_keys: 10000
query_result_cache_sweep_interval_ms: 10000
Settings:
query_result_cache_enabled: master switch. Whenfalse, hinted queries read live storage and reportcache-disabled.query_result_cache_default_ttl_ms: TTL for entries without a per-queryttl=...hint.query_result_cache_max_entries: maximum number of result-cache entries. Older entries are evicted by LRU behavior when the cap is exceeded.query_result_cache_max_bytes: total byte budget across all cached entries.query_result_cache_max_entry_bytes: per-entry byte cap. Larger results are returned normally but not cached.query_result_cache_max_entry_rows: per-entry row cap. Larger results are returned normally but not cached.query_result_cache_max_deps: maximum combined range, point, and schema dependencies recorded for one entry. Exceeding it prevents publishing the entry.query_result_cache_max_point_deps: maximum point-key dependencies recorded for one entry. Strict entries are not stored if point dependencies are truncated.query_result_cache_max_ranges: maximum range dependencies recorded for one entry.query_result_cache_singleflight_wait_ms: reserved for future single-flight miss de-duplication. Current concurrent misses may compute independently.query_result_cache_strict_validation_max_keys: maximum probe budget for strict validation. Exceeding it treats the entry as stale and re-executes the query.query_result_cache_sweep_interval_ms: how often the background sweep removes expired entries.
The cache is enabled by default but inert until a query uses {cache=...} or
@{cache=...}. See Query Result Cache for query
syntax, cache metadata, freshness behavior, and manual eviction.
Spill To Disk
Spill-to-disk lets blocking query operators use temporary files when their intermediate row buffers grow past a configured threshold:
spill_enabled: false
spill_threshold_rows: 500000
spill_merge_fan_in: 16
Settings:
spill_enabled: master switch. Whenfalse, blocking operators use their in-memory paths.spill_threshold_rows: per-operator row count before spilling starts. The setting is ignored when spilling is disabled.spill_merge_fan_in: maximum number of spill runs read at once during merge passes. Larger values reduce merge passes but use more open readers.
See Spill To Disk for supported operators, temporary file layout, and failure behavior.
Schema Limits
These settings bound schema growth and identifier sizes:
max_identifier_length: 64
max_columns_per_table: 512
max_indexes_per_table: 64
max_tables_per_database: 10000
Set a value to <= 0 to disable that specific limit. Exceeding one of these
limits returns SchemaLimitExceeded.
SQL Parser Cache
These settings control the SQL parser AST cache:
sql_parser_cache_ttl_seconds: 300
sql_parser_cache_max_entries: 2048
sql_parser_cache_sweep_seconds: 60
Details:
sql_parser_cache_ttl_seconds: sliding TTL in seconds;0disables the cache.sql_parser_cache_max_entries: maximum number of distinct SQL texts to keep;0removes the cap.sql_parser_cache_sweep_seconds: how often the background sweep removes expired cache entries.
This cache affects parse overhead, not SQL semantics.
Regex Safety Settings
Regex operators and regex scalar functions share the same compiled-pattern cache and timeout settings:
regex_match_timeout_ms: 250
regex_cache_max_entries: 1024
Settings:
regex_match_timeout_ms: maximum time in milliseconds for a single regex match operation. A match that exceeds this limit fails withInvalidInput, orCheckConstraintViolationwhen the regex is being evaluated inside a check constraint.regex_cache_max_entries: maximum number of compiled regex patterns cached by the process.0disables caching. Queries still work when the cache is full; CamusDB compiles the pattern for the current operation and does not retain it.
These settings apply to ~, ~*, !~, !~*, and the regexp_* scalar
functions. See Regex Functions for function syntax,
flags, and matching rules.
Engine Metrics
CamusDB observes embedded Kahuna and
Kommander metrics for
SHOW ENGINE STATS by default:
engine_metrics_enabled: true
Set engine_metrics_enabled: false to detach the in-process engine metrics
listener. SHOW ENGINE STATS still succeeds, but returns zero rows.
This setting is independent from the standalone diagnostics exporters below.
See Engine Stats for the SQL statement, permissions, and
result columns.
Diagnostics
Standalone diagnostics are opt-in:
diagnostics:
enabled: false
prometheus_enabled: false
prometheus_path: /metrics
otlp_endpoint:
trace_sample_ratio: 0.01
include_runtime_metrics: true
When disabled, CamusDB does not register a metrics exporter, scrape endpoint, trace exporter, or diagnostics collector. When enabled on a standalone node, the server can expose Prometheus metrics and optional OpenTelemetry traces for request handling, SQL execution, scans, query cache activity, transaction commit, Kahuna, Kommander, and runtime metrics.
See Performance Diagnostics for metric names, security notes, and workload snapshot scripts.
Kahuna Engine Options
The kahuna section is an allow-listed passthrough to embedded
Kahuna options used by both standalone and
cluster nodes. Omit the section, or omit individual keys, to keep CamusDB's
mode-specific baseline. Standalone persistent storage uses RocksDB by default;
for cluster deployments, set the storage keys explicitly so every node uses the
same durable backend.
kahuna:
storage: rocksdb
storage_revision: v1
wal_storage: rocksdb
wal_revision: v1
wal_sync_writes: true
wal_group_commit_linger_ms: 0
wal_single_fsync_commit: false
default_transaction_timeout_ms: 5000
max_transaction_timeout_ms: 3600000
locks_workers: 8
key_value_workers: 8
background_writer_workers: 1
read_io_threads: 8
write_io_threads: 8
start_election_timeout_ms: 2000
end_election_timeout_ms: 4000
start_election_timeout_increment_ms: 100
end_election_timeout_increment_ms: 200
heartbeat_interval_ms: 500
voting_timeout_ms: 1500
max_entries_per_actor: 50000
max_bytes_per_actor: 268435456
cache_entry_ttl_ms: 300000
cache_entries_to_remove: 1000
collection_interval_ms: 60000
compact_every_operations: 1000
compact_number_entries: 50
max_entries_per_compaction: 5000
rocksdb_shared_memory: true
rocksdb_shared_memory_budget_mb: 320
rocksdb_shared_memtable_budget_mb: 128
Allowed storage backends are rocksdb, sqlite, and memory. Use rocksdb
for the default durable path, sqlite when you specifically want SQLite-backed
embedded files, and memory only for development and tests because data is lost
on restart.
The allow-listed Kahuna keys are:
- storage settings:
storage,storage_revision,wal_storage,wal_revision,wal_sync_writes,wal_group_commit_linger_ms, andwal_single_fsync_commit - transaction and worker settings:
default_transaction_timeout_ms,max_transaction_timeout_ms,locks_workers,key_value_workers,background_writer_workers,read_io_threads, andwrite_io_threads - Raft timing settings:
start_election_timeout_ms,end_election_timeout_ms,start_election_timeout_increment_ms,end_election_timeout_increment_ms,heartbeat_interval_ms, andvoting_timeout_ms - in-memory actor bounds:
max_entries_per_actorandmax_bytes_per_actor - time-based cache eviction:
cache_entry_ttl_ms,cache_entries_to_remove, andcollection_interval_ms - Raft log compaction:
compact_every_operations,compact_number_entries, andmax_entries_per_compaction - RocksDB shared memory:
rocksdb_shared_memory,rocksdb_shared_memory_budget_mb, androcksdb_shared_memtable_budget_mb
Entry eviction has two controls. max_entries_per_actor and
max_bytes_per_actor bound actor memory by size, while
collection_interval_ms runs a background sweep that removes up to
cache_entries_to_remove entries older than cache_entry_ttl_ms.
Raft log compaction is controlled by compact_every_operations,
compact_number_entries, and max_entries_per_compaction. Tune them together:
one controls how often compaction runs, one controls how many trailing log
entries are retained, and one caps how much a single pass can remove.
WAL Durability And Throughput
wal_sync_writes controls whether each WAL write is fsynced for crash
durability before acknowledgement. Keep it true for production durability.
Use false only for development or bulk-load experiments where losing the most
recent unflushed WAL writes is acceptable.
Two WAL settings can improve write throughput while preserving the durable acknowledgement contract:
wal_group_commit_linger_ms: a small bounded wait, in milliseconds, that lets concurrent commits share one group fsync.0disables the linger. Try1or2for fsync-bound write-heavy workloads.wal_single_fsync_commit: allows eligible single-round autocommit proposals to acknowledge once the durable propose quorum is written, while the commit marker rides a later durable flush. This removes one serial fsync from the critical path without acknowledging an undurable commit.
Both settings affect latency/throughput tradeoffs, not SQL semantics. Compare changes with the same workload shape and durability settings.
RocksDB Shared Memory
When both the KV backend and WAL backend use RocksDB, CamusDB can share one RocksDB block cache and one write-buffer manager across both embedded RocksDB databases:
kahuna:
storage: rocksdb
wal_storage: rocksdb
rocksdb_shared_memory: true
rocksdb_shared_memory_budget_mb: 320
rocksdb_shared_memtable_budget_mb: 128
Settings:
rocksdb_shared_memory: enables the shared RocksDB memory bundle. In CamusDB's RocksDB baselines this is enabled by default. Set it tofalsewhen you want the KV backend and WAL backend to use independent RocksDB memory resources.rocksdb_shared_memory_budget_mb: total shared block-cache budget in MiB. The memtable sub-budget lives inside this total.rocksdb_shared_memtable_budget_mb: memtable sub-budget in MiB, charged against the shared cache budget. It must be less than or equal torocksdb_shared_memory_budget_mb.
The setting is active only when both storage and wal_storage are
rocksdb. If either side uses sqlite or memory, there is no second RocksDB
database to share with, so the shared-memory setting is ignored.
The feature changes only in-process RocksDB memory objects. It does not change on-disk format, WAL semantics, recovery behavior, SQL behavior, or wire protocols.
Unknown kahuna keys are rejected at startup. Numeric worker, timeout, actor,
eviction, and compaction settings must be greater than 0; when both election
timeout bounds are set, start_election_timeout_ms must be less than
end_election_timeout_ms.
Validation Rules
CamusDB validates the resolved configuration after CLI overrides are applied.
Invalid configuration fails startup with InvalidConfig.
Important validation rules:
modemust bestandaloneorclusterraft_port,http_port,https_port, andgrpc_portmust be in1..65535raft_node_idmust be> 0initial_partitionsmust be>= 1schema_ack_wait_timeout_msmust be> 0schema_ack_live_node_lease_msmust be> 0or-1default_isolation_levelmust beserializableorread_committeddefault_transaction_lockingmust bepessimisticoroptimisticlock_escalation_thresholdandlock_wait_deadline_msmust be> 0transaction_reaper_interval_msmust be> 0grpc_enabledis boolean- positive
range_lock_expires_msvalues must be at least2xthe effectivekahuna.collection_interval_ms spill_enabledis booleanspill_threshold_rowsandspill_merge_fan_inmust be> 0diagnostics.trace_sample_ratiomust be in0..1diagnostics.prometheus_pathmust start with/diagnostics.otlp_endpoint, when set, must be an absolute URLengine_metrics_enabledis booleankahuna.wal_group_commit_linger_msmust be>= 0kahuna.max_transaction_timeout_ms, when set, must be>=max_serializable_transaction_lifetime_msstats_flush_interval_msmust be>= 0or-1stats_analyze_sample_rowsmust be>= 0stats_histogram_bucketsmust be>= 1auto_analyze_fraction_stale_rowsandauto_analyze_min_stale_rowsmust be>= 0auto_analyze_max_concurrentandauto_analyze_histogram_sample_rowsmust be>= 1auto_analyze_hll_precisionmust be in4..16auto_analyze_ownership_check_rowsmust be>= 1cost_based_access_path_enabledandcost_based_join_order_enabledare booleansregex_match_timeout_msmust be> 0regex_cache_max_entriesmust be>= 0kahuna.rocksdb_shared_memory_budget_mbmust be> 0kahuna.rocksdb_shared_memtable_budget_mbmust be> 0kahuna.rocksdb_shared_memtable_budget_mbmust be less than or equal tokahuna.rocksdb_shared_memory_budget_mbwhen shared RocksDB memory is activeplan_cache_enabledis booleanquery_result_cache_enabledis booleanmax_identifier_length,max_columns_per_table,max_indexes_per_table,max_tables_per_database,max_index_columns,max_index_include_tuple_bytes, andmax_mutations_per_transactionuse<= 0to disable the corresponding limitbranch_snapshot_hold_lease_msmust be> 0sql_parser_cache_ttl_secondsandsql_parser_cache_max_entriesmust be>= 0sql_parser_cache_sweep_secondsmust be> 0http_peerscount must matchpeerscount whenhttp_peersis suppliedpeersandhttp_peersentries must be validhost:portvalues
Standalone Example
data_dir: /var/lib/camusdb
mode: standalone
http_port: 5095
grpc_enabled: true
grpc_port: 5096
default_isolation_level: serializable
default_transaction_locking: pessimistic
stats_flush_interval_ms: 5000
stats_analyze_sample_rows: 100000
stats_histogram_buckets: 100
auto_analyze_enabled: false
cost_based_access_path_enabled: true
cost_based_join_order_enabled: true
plan_cache_enabled: true
plan_cache_max_entries: 512
query_result_cache_enabled: true
query_result_cache_default_ttl_ms: 5000
query_result_cache_max_entries: 1024
spill_enabled: false
sql_parser_cache_ttl_seconds: 300
sql_parser_cache_max_entries: 2048
sql_parser_cache_sweep_seconds: 60
kahuna:
storage: rocksdb
wal_storage: rocksdb
wal_sync_writes: true
wal_group_commit_linger_ms: 0
wal_single_fsync_commit: false
rocksdb_shared_memory: true
rocksdb_shared_memory_budget_mb: 320
rocksdb_shared_memtable_budget_mb: 128
Start with the YAML values:
dotnet run --project CamusDB
Override only the HTTP port:
dotnet run --project CamusDB -- --http-port=5096
Cluster Example
data_dir: /data
mode: cluster
node_name: camus-1
raft_node_id: 1
raft_host: 192.168.1.10
raft_port: 7070
http_port: 5095
grpc_enabled: true
grpc_port: 5096
initial_partitions: 3
peers:
- 192.168.1.10:7070
- 192.168.1.11:7072
- 192.168.1.12:7074
http_peers:
- 192.168.1.10:5095
- 192.168.1.11:5096
- 192.168.1.12:5097
schema_ack_wait_timeout_ms: 30000
schema_ack_live_node_lease_ms: 30000
key_range_sharding: true
default_transaction_locking: pessimistic
auto_analyze_enabled: true
cost_based_access_path_enabled: true
cost_based_join_order_enabled: true
plan_cache_enabled: true
plan_cache_max_entries: 512
query_result_cache_enabled: true
query_result_cache_default_ttl_ms: 5000
query_result_cache_max_entries: 1024
kahuna:
storage: rocksdb
wal_storage: rocksdb
start_election_timeout_ms: 2000
end_election_timeout_ms: 4000
Use http_peers whenever nodes do not all expose the API on the same HTTP
port.
The same node can be started with CLI overrides:
dotnet run --project CamusDB -- \
--mode=cluster \
--data-dir=/data \
--http-port=5095 \
--raft-nodename=camus-1 \
--raft-nodeid=1 \
--raft-host=192.168.1.10 \
--raft-port=7070 \
--initial-cluster-partitions=3 \
--initial-cluster 192.168.1.10:7070 192.168.1.11:7072 192.168.1.12:7074 \
--http-peers 192.168.1.10:5095 192.168.1.11:5096 192.168.1.12:5097
Related Pages
See Cluster Mode for cluster startup, Distributed Schema Changes for the schema ack gate, and Transactions And Isolation for transaction behavior.