Tune durability and performance
Single-node defaults are tuned for correctness and need no thought: one partition, synchronous durability, backpressure on. The knobs below only matter once you cluster or push a node toward saturation — pick each axis independently for your workload.
| If you want… | Set | Effect |
|---|---|---|
| Zero-data-loss durability (the default) | leave NANOBPMN_DURABILITY=sync, NANOBPMN_REPLICATION=quorum |
A 2xx means fsync'd locally and majority-committed. Strongest guarantee, highest write latency. |
| Lowest write latency | NANOBPMN_DURABILITY=async + NANOBPMN_REPLICATION=leader-durable |
Ack on the leader's local durable append — no fsync-before-ack wait, no follower round-trip. A just-acked tail can be lost on ungraceful leader loss (bounded, never divergent). |
| High throughput with many workers | Automatic (default NANOBPMN_REPLICATE_ACTIVATION=auto); force with =0 or =digest |
auto (zero-config) keeps the activation lease off the replication log and broadcasts a soft digest — identical to =digest, validated healthy at both extremes (2,400/s @ 50 KB, ~36k/s @ negligible payload). Force =0 for plain leader-local (no digest). Avoid =1/quorum at scale: the strict replicated lease adds a per-activation quorum commit that collapses throughput. Still at-least-once. |
| Even job drain across nodes | NANOBPMN_ACTIVATION_FAIRNESS=1 or =2 |
Spread the activation budget across nodes; 2 also drains the deepest backlog fastest. |
| A producer that outpaces workers | leave backpressure on (default), or pin NANOBPMN_BACKPRESSURE_MAX_INFLIGHT=<n> |
The engine sizes the in-flight watermark from measured latency and sheds excess creates with 503, so the producer converges to the drain rate. |
| Behaviour at the saturation ceiling | NANOBPMN_SLA_MODE=latency (default) or =admission |
latency keeps accepted instances fast by shedding admission (time-to-complete SLA); admission keeps admitting and lets latency grow (start-every-process SLA). OOM-safety rails apply in both. |
| Bounded memory after bursts | NANOBPMN_IDLE_PURGE_MS, NANOBPMN_HISTORY_MAX_INSTANCES |
Idle-purge returns freed memory to the OS; cap retained completed instances to bound read-model growth. |
Recommended profiles:
- Strong durability (default, money-movement workloads):
RF=3, leave durability/replication unset. Every ack is fsync'd and quorum-committed. Use a persistent data dir per node. - Low latency (interactive workflows, modest concurrency):
RF=3,NANOBPMN_DURABILITY=async,NANOBPMN_REPLICATION=leader-durable,NANOBPMN_REPLICATE_ACTIVATION=digest. - Max throughput / benchmarking: one partition led per node,
NANOBPMN_REPLICATE_ACTIVATION=0,NANOBPMN_ACTIVATION_FAIRNESS=2. Always benchmark the release binary.
What an acknowledgement means — and what a node crash costs
In a cluster (RF=3 recommended), the durability tier changes what a 2xx
promises when hardware fails. Both tiers preserve ordering and lose nothing they
have already replicated; they differ only in when the ack is returned.
-
Strict (default:
quorum+sync). A create/complete is acked only after a majority of nodes have replicated and applied it and the leader has fsync'd it to disk. If any single node is lost — leader or follower — everything the client saw acknowledged is already durable on the surviving majority, so the promoted leader has it. Nothing you observed is lost. The price is one cross-node quorum round-trip on the critical path (a ~10 ms/job floor at low concurrency; group commit amortizes it to ~33k jobs/s under many workers). -
Relaxed (
leader-durable+async). A create/complete is acked as soon as the leader alone has applied it and written it to the OS page cache (fsync deferred, bounded to a ~10 ms / 8 MiB window); followers replicate in the background. If the leader process merely crashes and restarts, it recovers from its own disk — no loss. Only if the leader is lost permanently and simultaneously (disk failure, or the VM destroyed) before followers catch up is the un-replicated tail — acks the client already saw — lost, and a follower is promoted from the most complete log it holds.
Why relaxed is a latency trade, not a correctness hole. Nano is at-least-once end to end. A dropped completion just means the job's lease expires and it is redelivered — and completion is keyed, so an idempotent worker already tolerates it. A dropped create means the instance was never durably admitted, and the (also at-least-once) producer retries. Relaxed never reorders and never loses anything already replicated: the leader's local log stays the single ordered source of truth per partition. So the trade is exact — a rare simultaneous-permanent-leader-loss turns from no loss into a bounded tail of millisecond-scale redeliveries, in exchange for taking the quorum round-trip off every ack.
⚠️ Two-node clusters are a trap. An
RF=2group has quorum 2, so losing either node halts writes — durability without availability. Use 3+ nodes for fault tolerance.