Morphium v6 Performance β MongoDB vs PoppyDB vs InMemory
With Morphium v6 we rewrote the connection pool from the ground up, overhauled the messaging system, and added PoppyDB as a MongoDB-compatible in-memory server. Time to put some numbers on the table.
All benchmarks were run on a Mac Studio M2 Ultra against MongoDB 8.2.4 (3-node replica set) and PoppyDB (3-node RS, 1.5 GB heap per node). The test suite has 879 tests across 5 backends.
Side note: One of our contributors, Heiko Kopp, recently shared impressive numbers from running Morphium with GraalVM native compilation in production β 65% faster startup, 80-85% less memory, 99% fewer DB errors. See his report on GitHub for details.
Connection Pool: v5 vs v6
The biggest architectural change in v6 was moving from a global lock to per-host locking in the connection pool. The old PooledDriver had a single synchronized block around the entire pool β meaning a slow operation on one host would block threads waiting for a connection to a different host.
v6 uses ConcurrentHashMap with per-host BlockingQueues. The difference shows up immediately under concurrent load:
| Benchmark | v5.1.9 | v6.x | Improvement |
|---|---|---|---|
| Concurrent reads (20 threads Γ 100 ops) | 22,869 ops/sec | 31,642 ops/sec | +38% |
| Messaging (500 msgs) | 10 msgs/sec | 21 msgs/sec | +110% |
| Bulk Writes (10K docs) | 43,544 docs/sec | 38,219 docs/sec | -12%* |
*The bulk write regression is still under investigation β might be related to the new write concern handling.
That messaging improvement is real: v5 actually timed out and only received 345 out of 500 messages. v6 gets all 500 through without breaking a sweat.
Messaging: Where PoppyDB really shines
Messaging is where the backend choice makes the biggest difference. Since v6, messaging is ChangeStream-based β the database pushes new messages to listeners instead of polling.
| Backend | Throughput | Avg Latency | vs MongoDB |
|---|---|---|---|
| MongoDB (3-node RS) | 89 msgs/sec | 11.28 ms | 1x |
| PoppyDB (3-node RS) | 223 msgs/sec | 4.47 ms | 2.5x faster |
| InMemory Driver | 281 msgs/sec | 3.56 ms | 3.2x faster |
PoppyDB's advantage comes from zero disk I/O on writes β Raft replication between nodes is just memory-to-memory over the network. MongoDB has to wait for the journal flush.
Test Suite Speed
We run the same 171 network-facing tests against all four backends (MongoDB RS, MongoDB Single, PoppyDB RS, PoppyDB Single) plus 195 InMemory tests. With our parallelized CI setup, the whole thing finishes in about an hour β down from 5 hours when running sequentially.
PoppyDB RS is roughly twice as fast as MongoDB RS for the full test suite. Most of that comes from faster collection creation, index building, and drops β no disk I/O. For individual tests that are dominated by sleeps and timeouts, the difference disappears.
Query Performance: Indexes matter
One area where MongoDB clearly wins: indexed queries.
| Field | MongoDB | InMemory |
|---|---|---|
| Indexed (500 $in values) | 5.52 ms | 81.30 ms |
| Non-indexed (50 $in values) | 10.39 ms | 16.60 ms |
The InMemory driver doesn't have real indexes β it does a full collection scan for every query. For indexed fields, MongoDB is 15x faster. For non-indexed fields, InMemory is actually competitive because MongoDB's collection scan has more overhead per document.
PoppyDB inherits the InMemory driver's query behavior, so the same tradeoff applies. For workloads that are mostly writes and messaging (like our production system), PoppyDB wins. For read-heavy, indexed workloads, nothing beats real MongoDB.
PoppyDB in Production
Our PoppyDB 3-node RS runs on a 10 GB RAM / 2 CPU LXC container with 1.5 GB heap per node. Raft elections use priority-based leader selection (100/75/50) and settle in under 5 seconds after a failover. Data is dumped to disk every 300 seconds for recovery.
We also use Morphium messaging in production to process millions of documents per day through a job orchestration system β but that's a story for another post.