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 — both measured against mongod 8.0.31 on a dedicated cloud instance, as part of cutting the release they describe. 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
“Rust DB” here is the Rust MongoDB server — these harnesses drive the MongoDB wire protocol, so the Rust PostgreSQL server is measured separately, against PostgreSQL. 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) | 73.2 ms | 148.4 ms | 2.0x | 807.7 ms | 11.0x |
| find indexed range | 10.0 ms | 9.1 ms | 0.9x | 68.0 ms | 6.8x |
| find full scan | 17.8 ms | 17.1 ms | 1.0x | 126.5 ms | 7.1x |
| find filtered scan | 15.6 ms | 16.2 ms | 1.0x | 165.1 ms | 10.6x |
| update_many (half) | 96.6 ms | 120.2 ms | 1.2x | 1601.8 ms | 16.6x |
| aggregate $group | 11.1 ms | 20.6 ms | 1.9x | 258.6 ms | 23.4x |
| aggregate multi-stage | 15.6 ms | 37.5 ms | 2.4x | 268.9 ms | 17.3x |
| delete_many (half) | 47.3 ms | 75.2 ms | 1.6x | 788.0 ms | 16.7x |
| change-stream drain* | 104.0 ms | 111.0 ms | 1.1x | 207.5 ms | 2.0x |
The short version: the Rust DB runs at 0.9x–2.4x of mongod per operation — the indexed range read beats mongod outright, and full scan, filtered scan and the change-stream drain sit at parity — and it is roughly 1.9x–13.3x 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 | 26,400 | 12,700 | 16,600 | 4,600 |
| 2 | 56,900 | 24,000 | 30,900 | 5,000 |
| 4 | 103,500 | 37,500 | 44,900 | 3,600 |
| 8 | 136,200 | 42,400 | 51,000 | 3,000 |
Three different shapes, honestly told. mongod scales — 5.2x its own single-writer aggregate at eight writers; that's its C++ scheduler above WiredTiger doing its job. The Rust DB scales monotonically — 1.9x at two writers, 3.0x at four, 3.3x at eight (~42k 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: ~51k 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
The trade SecantusDB started from was conformance and WiredTiger durability over raw speed, and on the Python implementation that is still the trade. The Rust server is what closed it: at 0.9x–2.4x of mongod per operation and scaling monotonically with writers, per-op latency has stopped being the reason to reach for a real mongod instead. What remains a reason is everything single-node scope excludes — replica sets, sharding, a cluster to fail over. And because the wire protocol is identical across all three, moving between them is a connection-string change.
Caveats that apply to every number here: one machine (a dedicated 8-vCPU cloud instance, not a laptop — a laptop moves every column at once and nothing in the output says so), 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.