TurboKV: what a Rust key-value store's benchmarks really say
TurboKV is a fast embedded key-value store in Rust, but the most useful thing in the repo is how carefully it labels its own benchmark numbers. Here is how to read them.
TurboKV, a new embedded Rust key-value store, showed up on Hacker News this week with the kind of headline that usually makes me close the tab: "insanely fast." I opened the repository expecting a benchmark chart with no axis labels. I found the opposite.
The speed claims are real enough. But the genuinely instructive part of this project is the paragraph underneath the numbers, and that is what I want to talk about.
🔍 What TurboKV actually is
Strip the adjectives and it is a small, specific thing:
- Embedded, not networked. It runs inside your process as a library. There is no server, no port, no connection pool.
- Async, built on Tokio.
insert,getandremoveare allawaited. - LSM-tree shaped: memtables, SSTables, a write-ahead log, background compaction.
- Apache 2.0, requires Rust 1.85+, currently at version 0.6.0.
The API is about as small as this class of database gets:
let db = Db::open_with_options("./my-database", DbOptions::durable()).await?;
db.insert(b"user:1", b"Ada").await?;
assert_eq!(db.get(b"user:1").await?, Some(b"Ada".to_vec()));
db.close().await?;
Three durability presets ship in the box, and the README is blunt about what each one actually promises:
| Preset | When your write is acknowledged | Honest reading |
|---|---|---|
fast() |
In-memory visibility, no WAL | A cache. Crash means data loss. |
durable() (default) |
Appended to the WAL, no per-write sync | Survives a process crash, not a power cut |
paranoid() |
WAL group finished sync_all |
Strongest, still bounded by your disk's honesty |
📊 Read the benchmark table, not the headline
Here is the published comparison against fjall 2.11.2 and redb 2.6.3, measured on an Apple M4 with 32 GiB of RAM on 2026-08-28. Throughput is acknowledged keys per second, higher is better.
| Workload | TurboKV Recoverable | fjall Buffer | redb Eventual | TurboKV / fjall |
|---|---|---|---|---|
| Sequential fill (1 key/txn) | 1,407,678 | 485,252 | 1,397 | 2.901× |
| Random fill (1 key/txn) | 834,137 | 456,924 | 1,549 | 1.826× |
| Overwrite (1 key/txn) | 853,083 | 446,733 | 1,516 | 1.910× |
| Batch (100 keys/txn) | 2,272,259 | 511,600 | 80,197 | 4.441× |
| Batch (1,000 keys/txn) | 2,333,582 | 572,671 | 134,636 | 4.075× |
Now look at what the author does with that redb column. Instead of banking the thousand-fold win, the README states that redb's Durability::Eventual performs a macOS F_BARRIERFSYNC on every transaction, while TurboKV and fjall stop at their process-crash-recoverable OS-cache boundaries. It then calls its own single-key rows "architectural context rather than a like-for-like durability claim."
Key takeaway: a benchmark that tells you which of its own rows are unfair is worth more than a benchmark that is 4× faster. The second one you have to re-run yourself; the first one you can reason about.
The project also renames its own default. The preset is called durable() in code, but in the benchmark table it appears as "Recoverable", because it survives a crashed process and not a lost power supply. I have reviewed vendor benchmarks that would never make that distinction in public.
💰 Why "embedded" is the interesting word for a small team
Most of us here are not sizing a fleet. We are running one box, and the monthly bill is in dollars against an LKR income. That changes which database property matters.
A networked store like Redis or Postgres costs you a second process, its own memory floor, a socket, a supervisor entry and a backup story. An embedded store costs you a directory.
| Concern | Networked store | Embedded store like TurboKV |
|---|---|---|
| Extra process to run | Yes | No |
| Baseline RAM | Its own allocation | Yours, inside your app |
| Network round trip per read | Yes | No |
| Shared across services | Yes | No, single owner |
| Ops surface | Config, auth, ports | A folder on disk |
That last row is the trade, and it is a real one. TurboKV states plainly that one open Db handle exclusively owns its data directory. If two of your services need the same data, this is the wrong tool and no benchmark number changes that.
Watch the memory defaults if you are on a 1 GB or 2 GB VPS. Every preset starts with a 64 MiB memtable and a 64 MiB block cache, so roughly 128 MiB is spoken for before your application allocates anything. Both are public fields you can lower before opening. If your keys are actually embeddings rather than short strings, the arithmetic gets steeper fast, and our AI vector storage calculator will size that for you before you provision the box.
🛠️ Four things I would check before shipping it
- It is 0.6.0. That is a pre-1.0 version number on a storage engine. Storage bugs are the expensive kind, because they are discovered later than they happen.
- The build flags are not optional. The persisted Bloom-filter format uses hardware AES. You are told to build with
RUSTFLAGS="-C target-feature=+aes,+sse2"on x86_64 and+aes,+neonon ARM. Miss this in your CI image and you find out at runtime. - Dropping the handle is not a clean shutdown. Call
close()orclose_with_status(). This has to survive your panic paths and your container's stop signal, not just the happy path. - The benchmark hardware is not your hardware. Those numbers come from an Apple M4 with 32 GiB of RAM on APFS. Your $6 VPS has shared vCPUs and network-backed storage. Expect a different shape, not just a smaller multiple.
Bottom line: treat the published throughput as evidence that the design is sound, not as a number you will reproduce.
💡 What this means for you
If you are building a Rust service that needs local ordered storage, a queue, a cache with range scans, or an index that must survive a restart, TurboKV is worth an afternoon. The API surface is small enough to learn in one sitting and the scan and batch semantics are documented in unusual detail.
If you are not writing Rust, take the other thing instead. The habit worth stealing from this repo is the discipline of publishing what your numbers do not prove: the hardware, the durability boundary, the row that is not a fair fight, and the raw artifact so someone can check you. That applies to your own benchmarks, your client-facing performance claims, and your final year project.
Fast is a claim anyone can make. Legible is the harder one, and it is the one that survives review.
Original source
TurboKV: Insanely fast Rust key-value store