Measured, not promised
Every number on this page is measured on the same machine, through the same pymongo client, against the same on-disk WiredTiger storage — with real mongod as the reference. Two dimensions: per-operation latency (one client, six workloads) and throughput under concurrent writers (1–8 parallel writer processes). Both harnesses ship in the repo; both tables regenerate with one command.
Per-operation latency
Bar length is how many times slower than mongod each server is — median of five runs per workload, 10,000-document dataset, end-to-end through the wire protocol and driver.
Data table (with raw milliseconds)
| Workload | mongod | Rust DB | x mongod | Python DB | x mongod |
|---|---|---|---|---|---|
| insert (10k docs) | 55.0 ms | 65.8 ms | 1.2x | 256.7 ms | 4.7x |
| find indexed range | 4.3 ms | 6.3 ms | 1.5x | 31.5 ms | 7.3x |
| find full scan | 7.3 ms | 16.7 ms | 2.3x | 60.4 ms | 8.3x |
| find filtered scan | 5.5 ms | 9.2 ms | 1.7x | 70.2 ms | 12.8x |
| update_many (half) | 33.4 ms | 36.8 ms | 1.1x | 445.9 ms | 13.3x |
| aggregate $group | 5.5 ms | 5.1 ms | 0.9x | 95.3 ms | 17.4x |
| aggregate multi-stage | 5.8 ms | 9.9 ms | 1.7x | 142.0 ms | 24.3x |
| delete_many (half) | 20.9 ms | 17.6 ms | 0.8x | 218.2 ms | 10.5x |
| change-stream drain* | 45.8 ms | 36.9 ms | 0.8x | 64.3 ms | 1.4x |
The short version: the Rust DB runs at 0.8x–2.3x of mongod per operation — delete, single-stage $group, and the change-stream drain now beat mongod outright — and it is roughly 1.7x–19x faster than the Python DB workload-for-workload. All three sit on the identical WiredTiger C library; the gaps are the layers above it. (* The change-stream row's mongod reference is a single-node replica set — mongod's change streams require one.) Methodology & how to reproduce →
Throughput under concurrent writers
Does throughput grow with writers? Each line is a server's aggregate throughput relative to its own single-writer rate — N writer processes each streaming insert_many batches for 30 seconds against their own collections.
Data table (absolute docs/second)
| Writers | mongod | Rust DB | Rust DB — async stack | Python DB |
|---|---|---|---|---|
| 1 | 105,700 | 35,400 | 50,400 | 12,100 |
| 2 | 203,300 | 53,700 | 66,000 | 8,900 |
| 4 | 366,100 | 80,200 | 100,300 | 10,400 |
| 8 | 481,000 | 96,400 | 119,200 | 7,600 |
Three different shapes, honestly told. mongod scales — 4.6x its own single-writer aggregate at eight writers; that's its C++ scheduler above WiredTiger doing its job. The Rust DB scales monotonically — 1.5x at two writers, 2.3x at four, 2.7x at eight (~96k docs/s aggregate), with no cliff: the per-collection write-lock split, RecordId keying (write amplification cut from four WiredTiger writes per document to three), a key-only oplog prune, and append-tuned oplog btrees turned the old peak-then-collapse shape into real scaling at every writer count, fully durable. The remaining flattening is WiredTiger itself — cache eviction and checkpoint pressure in one embedded connection — not a SecantusDB lock. The Rust DB's async stack sits highest (dashed) — opting into the async + non-logged oplog (oplog_async + oplog_nonlogged, options on the embedded handle and daemon flags, or the SECANTUS_* env vars) moves the oplog write off the writers' critical path and out of the write-ahead log: ~119k docs/s at eight writers, ~1.2x the fully-durable default, trading crash durability of the oplog tail (data stays fully durable; change streams stay exactly-once). The Python DB degrades under contention to ~0.6x — the GIL plus the measured WiredTiger-binding ceiling — but degrades gracefully: write conflicts retry server-side exactly like mongod's writeConflictRetry, and a client never sees an error. The full concurrency story →
What to make of it
SecantusDB's trade is deliberate: conformance and WiredTiger durability over raw speed. For its home turf — embedded test and dev databases, single-node prototypes, CI — per-operation latency in the milliseconds and single-writer throughput in the thousands of documents per second rarely matter. When they do, the Rust DB is the answer within the family, and a real mongod is the answer beyond it — and because the wire protocol is identical, moving between all three is a connection-string change.
Caveats that apply to every number here: one machine (Apple Silicon), no network, no concurrent readers mixed with writers, dataset that fits in cache. The harnesses (bench.compare_servers, bench.concurrency) ship in the repository — run them on your own hardware and workload before drawing conclusions that matter.