Documentation / Limits and capacity
Limits and capacity
The constraints on this page change how you size and operate a deployment. They are stated plainly because finding them during a data load is far more expensive than reading them first.
A dataset larger than the resident ceiling does not load slowly. It does not load at all. The section below explains the ceiling and how to raise it.
Working data is bounded by memory
Table data lives in memory. Rows are held in an in-memory page store and made durable through
the write-ahead log, and pages are not evicted to disk. Once the resident store
reaches its ceiling, further row inserts are refused with an error naming the limit and the
bytes resident. That ceiling comes from --max-resident-bytes, or is derived from
the detected memory budget when the flag is unset.
ERROR XX000: out of memory: the in-memory store reached its resident-memory limit of 858993440 bytes (859001088 bytes resident); free rows (DELETE/TRUNCATE), raise the limit, or use a larger host
The refusal is loud and explicit, so this is a capacity limit rather than a correctness problem. What matters when planning is that a database's working data must fit inside the resident ceiling, and on a memory-constrained host that ceiling is a fraction of RAM.
Two details commonly surprise people:
- Updates and index builds are not gated by this ceiling, so an update-heavy workload already at the limit can still grow past it.
- Deleting rows frees pages for reuse but does not lower the resident meter, because page memory is recycled rather than returned. Restarting does not lower it either: recovery replays the log in order and rebuilds the same high-water mark.
Once the ceiling has been reached the remedies are raising it, moving to a larger host, or reloading the live rows into a fresh data directory, which lands residency at the level of live data.
The log grows with write history
The write-ahead log is the durable copy of the data, and no checkpoint truncates it yet. The data directory therefore grows with everything that has ever been written, not with how much data is currently live. A steady write workload adds to it indefinitely, and deleting rows does not shrink it.
Budget disk against write volume over time rather than against table size, and watch the data directory as an operational signal.
Restart time grows with the log
Recovery replays that history, so start-up time grows with the size of the log rather than the size of the data. On a database that has been written to for a long time without being reloaded, this is the number that decides how long a restart takes. If a short restart window matters, reloading into a fresh data directory resets both the log and the resident high-water mark.
Pages live in memory, the log is the durable copy, and nothing truncates the log. Checkpointing is what connects them: it is what would let the log be truncated, bound restart time, and give page eviction somewhere to write to. It is the most significant piece of work outstanding before 1.0.
There is no built-in backup or replication
NusaDB ships no backup, restore, or replication tooling. What you can do today is stop the
server and copy the data directory, or export the data over a connection with COPY and
reload it elsewhere. There is no point-in-time recovery, no streaming replica, and no built-in
scheduled backup.
This is the largest gap between NusaDB and a database you would run a business on unattended, and it is worth weighing before choosing it for data you cannot reproduce.
Single node
The engine runs on one machine. There is no clustering, no replication, and therefore no automatic failover: the availability of the deployment is the availability of that host and its disk.
Query and memory behaviour
The executor materialises each stage rather than streaming it. With --work-mem
set, a stage that exceeds the budget fails with an error naming the limit and the server stays
responsive; without it, a large enough query can exhaust host memory. Setting a work-memory budget
is the safer configuration, at the cost of failing queries that would otherwise have finished
slowly.
A spill directory flag exists and covers some operators; sorting, DISTINCT,
grouping and window functions do not yet stream their output to it, so those still fail at the
budget rather than spilling.
Vector index build cost
Vector search is available with a VECTOR(n) type, the four distance operators, and
an HNSW index whose recall reaches exact-search results at a high enough
hnsw_ef_search. Building the index is expensive, substantially more so than a specialised vector
extension, and the graph is rebuilt rather than checkpointed. Plan index builds as
scheduled work rather than something to do during a load.
Not in this release
| Area | Status |
|---|---|
| Backup, restore, point-in-time recovery | Not built. Stop-and-copy or export with COPY. |
| Replication and clustering | Not built. Single node only. |
| Checkpointing and log truncation | Not built. See above. |
| Page eviction to disk | Not built. Data is bounded by memory. |
| Temporary tables | Refused with a clear error. |
Cursors, PREPARE/EXECUTE statements | Refused. |
| Session time zone | Fixed at UTC. |
| Locale collations | Refused; byte ordering only. |
| Index methods beyond B-tree and HNSW | Refused. |
| Latency and storage metrics | Four counters only. |
Where NusaDB fits today
It is a reasonable choice when the working set fits comfortably in the memory you are willing to give it, when a single node is acceptable, and when you can reproduce the data or arrange your own export schedule. It is not the right choice yet for data that must survive unattended, for datasets that outgrow memory, or where a restart must be quick regardless of write history.
Everything on this page is a property of the current release rather than a permanent limitation. Check the release notes when a new version appears.