Memory governor
Set the process envelope and understand normal, ramp, block, seal, and spill behavior.
The memory governor is the process-wide escape valve for sustained pressure. It forces ingest to slow or stop before the configured memory boundary becomes an OOM. It is enabled by default.
Resolve the limit
--memory-limit-bytes=0 selects automatically:
- cgroup v2
memory.max; - cgroup v1
memory.limit_in_bytes; or - system RAM;
then uses 90% of that value. A nonzero value is used verbatim.
For predictable operations, set an external container/cgroup limit and an explicit engine limit below it, leaving space for runtime overhead and behavior outside the tracked engine state.
At boot, verify the log line that reports whether the governor is enabled, its resolved limit, limit source, and governed-usage source. If no limit can be resolved, the governor reports that it is inactive.
What usage means
On cgroup v2, the governor observes cgroup usage and subtracts reclaimable clean file cache; dirty/writeback cache remains charged. On cgroup v1 it uses the equivalent cgroup accounting path. Outside a usable cgroup it falls back to process RSS.
This is deliberately closer to the memory pressure seen by the kernel than an application-object counter alone.
Pressure tiers
| Tier | Entry | Behavior |
|---|---|---|
| Normal | below 80% | Ordinary configured admission and background behavior |
| Ramp | at or above 80% | Graduated ingest delay; pressure flush/seal/spill work becomes more aggressive |
| Block | at or above 95% | New ingest waits only for the configured bounded backpressure interval; if pressure does not fall, it returns a retryable pre-WAL rejection |
A 2% downgrade hysteresis prevents rapid tier oscillation. A tier downgrades only after usage falls below its threshold minus that gap.
Keep --memory-governor=on in production. off is a benchmark or focused
diagnostic posture and removes both the last process-wide ingest gate and the
shared exact-native aggregate-state backstop.
Exact-native query-state backstop
The pressure tiers above govern process/cgroup memory and ingest. Query execution has an additional hard counter for exact native aggregate state across all concurrent statements:
| Control | Default | Effective value |
|---|---|---|
--query-global-exact-memory-bytes | 512 MiB | Configured value, capped at 50% of the resolved process/cgroup limit |
If limit detection is unavailable, NYXDB retains the finite configured value. The startup log reports the configured/effective exact-state ceiling, process limit, governor status, and local statement memory limit; capture that line with deployment evidence.
count_distinct, uniq, and uniq_exact are exact in the current release.
Their native buckets, metadata arrays, allocation-replacement peaks, and string
arenas reserve physical bytes against the statement and process counters before
allocation. A query with --query-max-operator-memory-bytes=0 still participates
in the process counter. Only the explicit --memory-governor=off escape valve
sets the global exact-state ceiling to unlimited.
When either limit rejects growth, the query fails with
NYXDB_EXEC_BUDGET_EXCEEDED: ... memory ...; it is not truncated and does not
return a partial relation. Destruction releases the exact reservation before the
failure crosses the one-shot boundary, so a subsequent query can proceed in the
same process. Inspect rows_scanned, peak_memory_bytes, and error in
system.query_log when investigating this condition.
Tail, seal, and spill
The committed tail has independent row and byte caps:
--committed-tail-max-rows, default 10,000,000;--committed-tail-max-bytes, default 2 GiB; and--committed-tail-backpressure-wait-ms, default 30 s.
The delay ramp begins at the configured ratio (default 0.5) and rises between the configured minimum and maximum delay. At the cap, blocking admission waits for bounded drain capacity and then returns a retryable rejection.
Two different mechanisms move different objects:
--sealed-memory-max-bytesand--sealed-spill-after-msgovern sealed in-memory NYXP parts for eligible disk-resident append tables. Oldest parts become registered durable disk parts, advancing durable coverage.--committed-tail-spill=ongoverns sealed raw committed-tail chunks. It serializes spill segments beneath--data-dir; those segments are not registered table parts, and recovery validates and reattaches them. The feature is off by default and requires--data-dir.
Spill is not a substitute for flush/compaction health. It trades RAM for local I/O and recovery state.
Observe
SELECT name, value, description
FROM system.metrics
WHERE name IN (
'memory_bytes_total',
'committed_tail_rows',
'committed_tail_max_rows',
'committed_tail_bytes',
'committed_tail_max_bytes',
'committed_tail_spilled_bytes',
'storage_sealed_in_memory_bytes'
);
SELECT name, value
FROM system.events
WHERE name IN (
'memory_governor_ramp_engagements',
'memory_governor_blocks'
);
SELECT sequence, rank, bytes, allocations, trigger, backtrace
FROM system.memory_profile
ORDER BY sequence DESC, rank ASC;An empty system.memory_profile means no usable profile is available—not that
there are no allocations.
system.metrics.memory_bytes_total is process RSS. Under cgroups, the governor
uses cgroup usage minus reclaimable clean cache instead; the current system
tables do not expose that governed usage, resolved limit, factor, or tier as one
authoritative row. Reconcile pressure incidents against the boot and throttled
memory report logs rather than comparing RSS directly with the governor's
cgroup threshold.
Respond to sustained Ramp or Block
- Preserve logs, metrics/events, live queries, subscriptions, and the latest memory profile.
- Stop or reduce nonessential ingest; do not restart blindly.
- Check committed-tail growth versus flush and part progress.
- Inspect high-memory queries and operator/cardinality limit failures.
- Check stream pending bytes/overflow and downstream consumption.
- Confirm the configured limit matches the actual cgroup.
- Fix the bottleneck or capacity mismatch before raising a limit.
Raise memory only after identifying what is resident and proving the host has corresponding headroom. Otherwise the change postpones the same failure.