Open source · Apache-2.0
One engine for transactions and analytics.
NusaDB is a relational database for a single machine. It gives you serializable transactions, a cost-based planner over a vectorized executor, and commits that are durable before they are acknowledged. All of it runs in one binary. There is no cluster to operate.
-- write and read, one engine, one connection BEGIN; UPDATE stock SET qty = qty - 1 WHERE sku = 'A-77'; INSERT INTO orders (sku, region) VALUES ('A-77', 'east'); COMMIT; SELECT region, count(*) AS n, sum(total) AS revenue FROM orders WHERE placed >= now() - INTERVAL '30 days' GROUP BY region ORDER BY revenue DESC; region | n | revenue --------+------+----------- east | 1204 | 184320.55 west | 980 | 97015.20
What the engine does today
Everything here is implemented and covered by tests. Anything planned but not built is on the limits page instead of in this list.
MVCC and snapshot isolation
Readers work from a snapshot and never block writers. All four standard isolation levels are available, up to serializable snapshot isolation, with savepoints and partial rollback.
Clustered B+tree
Rows live in the leaves of a B-link/B+tree keyed by a row id the engine mints, so reaching a row by primary key does not need a second lookup in a separate heap.
Write-ahead log
Every record carries a CRC32 and is lz4-compressed. A commit is acknowledged only once its record is durable. A background worker checkpoints the log into an image, so recovery replays only the tail written since.
Cost-based planner
Histograms and most-common-value statistics drive join order and index choice. Plans run through a vectorized executor in batches rather than one row at a time.
A broad SQL surface
Window functions, recursive CTEs, set operations, lateral joins, grouping sets, MERGE, ON CONFLICT, views, materialized views, sequences, domains, triggers and routines.
Authentication and access control
SCRAM-SHA-256 over optional TLS, including mutual TLS. Roles, GRANT and REVOKE, and row-level security policies are enforced inside the engine.
It speaks its own protocol
The first thing to settle before planning an integration.
NusaDB does not implement another database's wire format. A client built for a different engine will not connect: the server does not answer a handshake it does not recognise, so from the client's side the socket looks dead. That is a design decision, not an unfinished feature.
The SQL dialect is a separate question. Queries written for other engines generally run unchanged once they arrive over a NusaDB connection. So porting an application means changing how it connects, not rewriting its SQL.
- Default port 5678
- Shell
nusadb-cli, interactive and batch - Drivers Rust, Java, Node, Python, Go, Ruby, PHP, .NET
- Authentication SCRAM-SHA-256, optional mutual TLS
- Bulk transfer
COPY … FROM STDIN/TO STDOUT - Observability Prometheus endpoint
- Databases Many per server, many schemas per database
Numbers from our own runs
These come from the project's test machines, not from a formal benchmark suite. They are here because a release note that only says "faster" is not worth reading.
The first pair is the one to look at. TRUNCATE used to remove rows one
at a time, so its cost grew with the table. It now takes roughly the same time
whichever size you point it at.
Release build, single node, one connection. Your hardware will not produce these exact figures. Nothing here is a comparison against another database.
The TRUNCATE pair was measured when that work landed; the other three
come from an earlier build. The engine has moved a long way since, so treat these
as the shape of the result rather than as the current number, and re-read this
section when the next release notes appear.
| Operation | Rows | Time |
|---|---|---|
TRUNCATE | 100,000 | 28.5 ms |
TRUNCATE | 200,000 | 77.8 ms |
COUNT(*) | 1,000,000 | 249 ms |
UPDATE a filtered set | 100,000 of 1,000,000 | 6.0 s |
| Primary-key lookup, 1,000 of them | 1,000,000 in table | 569 ms |
Vector search reaches the same answers as an exact scan once
hnsw_ef_search is raised far enough. Building that index is the slow
part, and the limits page says so.
The row lives inside the index
Most engines keep rows in a heap and have the index point at them, so reading by key costs a descent and then a second fetch. Here the row is the leaf entry. The descent is the read.
- No heap fetch. The key lookup ends at the leaf
- No reader locks. Visibility comes from the stamps on the row
- No background flush to wait on. The log is what makes a commit durable
Running in about a minute
The container image needs no build step. From source you need a Rust toolchain and nothing else.
Container
docker run -d --name nusadb \ -p 5678:5678 \ -v nusadb-data:/var/lib/nusadb \ -e NUSADB_USER=app \ -e NUSADB_PASSWORD=change-me \ nusadb/nusadb docker exec -it nusadb \ nusadb-cli --user app --database nusadb
From source
git clone https://github.com/nusadb/nusadb.git cd nusadb cargo build --release ./target/release/nusadb-server \ --listen 127.0.0.1:5678 \ --data-dir ./data \ --auth-user app:change-me
With no --auth-user and no environment pair, the server accepts every
client without a password and logs a warning at start-up. That is fine on a laptop
and wrong for anything others can reach.
Read the whole thing
Six pages. They are written to be looked things up in, not skimmed once.
Getting started
Install, connect, create databases and schemas, load and export data in bulk, and run statements from scripts.
SQL reference
Types, statements and query features, plus the places where NusaDB deliberately behaves differently and why.
Transactions
Isolation levels, how conflicts surface, the server-side retry for single statements, savepoints and locking.
Clients and protocol
The official drivers, connection settings, TLS and SCRAM, and what implementing the protocol involves.
Configuration
Every server flag, the small-by-default resource limits, a systemd unit, and the metrics that are exposed.
Limits and capacity
What bounds a deployment today: what still has to fit in memory, log growth, restart time, backup and standby, and what is not built yet.
Where the edges are
NusaDB is before 1.0, and these are the constraints that change how you size a deployment. They are here rather than buried because meeting them during a data load costs far more than reading them now.
Some structures stay in memory
Table and index pages are cached, evicted and spilled, so data can outgrow memory. Oversized index entries cannot leave memory, and once they reach the resident ceiling, inserts and updates are refused with an error naming the limit. Vector indexes are held in memory whole.
Checkpoints need a quiet moment
A background worker truncates the log once it passes a threshold, but only when no transaction is active. A transaction held open for a long time keeps the log growing until it ends.
Restart replays the log tail
Recovery replays the writes since the last checkpoint, so start-up time grows with that tail. Size restart windows against it, not against table size.
Backup and standby are yours to schedule
Hard-link snapshots, a log archive with point-in-time restore, and a read-only standby are built in. Scheduling snapshots, pruning the archive and promoting the standby are manual.
Single node
One machine, so no automatic failover. The availability of the deployment is the availability of that host and its disk, plus a standby you promote by hand.
All of these are properties of this release rather than permanent limits.
Read the limits page