Skip to content

Qirava DMS docs

Performance tuning

The DMS sizes itself to the host automatically: it scans the machine's CPU, memory, and storage and applies a safety budget so it never tries to use the whole box. For most deployments you do not need to tune anything. This page explains what the auto-configuration does, then documents the knobs you can turn when you need to — the connection ceiling, the reactor versus blocking serving model, replication, and the always-durable WAL — so you can make informed trade-offs.

The executor budget (auto-configured)#

When the DMS builds its single executor it calls the auto-configuration entrypoint, which scans the host's resources (CPU threads, memory bytes, storage bytes) and applies a safety budget on top. The whole query path — reads, writes, inline stdlib calls, auth checks — runs inside this one executor; the HTTP transport layer is deliberately kept inside the same budget so the server never spawns unbounded work.

System safety budget
80% by default. The executor caps thread, memory, and storage usage at this percentage of the scanned capacity and never exceeds the physical capacity reported by the scan.
Worker budget
80% by default. Together with the system budget this derives the worker-thread count from the available CPU threads, so worker + helper threads stay within the overall budget rather than overshooting it.
Helper pool
Fills the remainder of the system budget after worker threads, with a floor of at least one helper thread so concurrent fan-out (e.g. multi-leg reads, batch) always has somewhere to run.
Max jobs per worker
1000 by default — the per-worker load cap.

Connection ceiling (QDMS_MAX_CONN)#

The serving layer enforces a maximum number of concurrent in-flight connections so a connection flood cannot exhaust memory or the scheduler. The default ceiling is generous relative to the executor pool (most connections are idle keep-alive), but bounded: it scales with the executor's worker count and has a floor.

Default ceiling
worker_count × 256, with a minimum of 256. So a small host with few worker threads still allows at least 256 concurrent connections.
QDMS_MAX_CONN
An integer override for the ceiling. Set it to raise or lower the concurrent-connection limit for your deployment. The serving layer never exceeds this value, keeping it inside the resource budget. A value of 0 leaves the default in place.
bash
# Cap concurrent connections at 1024 for this deployment
QDMS_MAX_CONN=1024 ./target/release/qdms

Reactor vs blocking server (QDMS_REACTOR)#

The system worker can be served two ways. The default is an epoll reactor: a fixed pool of reactor threads multiplexes every connection, so the serving layer's thread count never scales with the number of connections and stays inside the DMS budget (HTTP runs async; a WebSocket upgrade is handed off to a blocking thread). The alternative is a thread-per-connection blocking server, which can reach higher peak throughput under active load at the cost of one thread per connection.

QDMS_REACTORServing modelWhen to prefer
unset / any value ≠ 0 (default)epoll reactor — fixed reactor-thread pool multiplexes all connectionsDefault. Keeps thread count bounded regardless of connection count; best for many mostly-idle / keep-alive connections.
0blocking server — one thread per connectionHigher peak throughput for a workload of continuously-active connections, accepting a thread per connection.
bash
# Force the thread-per-connection blocking server
QDMS_REACTOR=0 ./target/release/qdms
#   serving: system.worker via blocking server (thread per connection)

# Default (reactor) — equivalent to leaving QDMS_REACTOR unset
./target/release/qdms
#   serving: system.worker via epoll reactor (fixed pool threads)

User-deployed workers (bound on their own addresses) default to the blocking per-connection model; the host can be switched to bind newly-deployed workers through the reactor instead. The system worker's model is controlled by QDMS_REACTOR as above.

Replication and the writer gate#

The DMS supports optional single-leader TCP WAL replication: a master ships every committed change batch to its followers; a follower applies them durably and can be promoted on failover. Replication runs entirely on background threads and never blocks serving. The role and addresses come from environment variables; the default is standalone (no replication).

VariableRoleMeaning / default
QDMS_REPL_ROLEmaster | follower | (unset)Selects the replication role. Anything other than master or follower (including unset) is standalone.
QDMS_REPL_LISTENmasterAddress the master listens on for followers. Default 0.0.0.0:7180.
QDMS_REPL_RINGmasterCatch-up ring-buffer capacity (number of recent change batches retained for a reconnecting follower). Defaults to the built-in ring capacity; a follower too far behind triggers a full resync.
QDMS_REPL_MASTERfollowerhost:port of the master to follow. If a follower role is set without this, the node falls back to standalone.
bash
# Master: listen for followers on the default repl port (7180)
QDMS_REPL_ROLE=master ./target/release/qdms

# Follower: replicate from a master
QDMS_REPL_ROLE=follower QDMS_REPL_MASTER=10.0.0.5:7180 ./target/release/qdms

A node is the writer (leader) for Standalone and Master roles, and is NOT the writer for a Follower. The writer gate governs writer-only background work — most importantly the scheduled jobs in _sys_jobs (including the built-in TTL sweep that runs every 60s) — so those never double-run across a cluster. On the boot log you will see [follower: jobs paused] on a follower. A follower resumes replication from its already-applied WAL sequence; on failover, promoting a follower flips its writer gate so it begins running jobs and can serve as the new master.

WAL durability#

Durability is determined by data_dir. With a real directory (the default ~/.qdms/qdms-data) the engine is durable: every mutating transaction is written to the write-ahead log and fsync'd (the file is sync_data'd) before the call returns, making the transaction crash-atomic. Setting data_dir = memory runs an ephemeral in-memory engine with no WAL and no fsync — appropriate for benchmarks or throwaway instances.

data_dirDurabilityfsync per write
a directory path (default)Durable — WAL at <dir>/engine.wal, crash recovery on restartYes — committed and fsync'd before the write returns
memoryEphemeral — no persistence, lost on exitNo