Design a Durable Key-Value Store
Start with a hash map behind a socket; end with a crash-survivable storage engine. The write-ahead log — append before apply, fsync, and replay-on-recovery — is the whole story, drawn and computed.
System design · Systems. The source ↗
A free, interactive, animated visual explainer of Design a Durable Key-Value Store — built to be understood, not skimmed.
Questions
- How does a key-value store survive a crash?
- With a write-ahead log (WAL): every update is appended to an on-disk log and fsynced before the write is acknowledged, and only then applied to the in-memory structures. On restart the store replays the WAL to reconstruct the in-memory state it lost, so no acknowledged write is ever forgotten. RocksDB puts it plainly: "write ahead logs can be used to completely recover the data in the memtable."
- What is a write-ahead log and why append before apply?
- A WAL is an append-only file the store writes to before touching any other structure. Appending is sequential, so it is fast, and because the durable record exists before the in-memory apply, a crash at any point leaves a log you can replay. If you applied first and logged second, a crash between the two would lose the write with no record it happened.
- What does fsync do and why is it the durability/latency trade-off?
- A write to a file only lands in the OS page cache; fsync forces it to the physical disk. fsync-per-write is the safest (zero writes at risk) but caps you near the disk’s fsync rate (~1,000/s). Group commit fsyncs a batch once per small window (e.g. 5 ms), so the durability window is that batch and latency is roughly half the window — far higher throughput for a bounded, honest risk. Letting the OS flush is fastest but risks tens of seconds of writes.
- What happens if the store crashes after the WAL append but before the memtable apply?
- The write survives. The WAL is the source of truth, so on recovery the store replays the log and re-applies that record to the memtable — whether or not the in-memory apply happened before the crash. The only writes lost are ones whose WAL record was never fully written and fsynced; a torn tail record fails its checksum and is discarded, and that write was never acknowledged.
- What is the difference between an LSM tree and a B-tree storage engine?
- An LSM tree (LevelDB, RocksDB, Cassandra) buffers writes in a memtable, flushes them to immutable sorted files (SSTs), and compacts in the background — write-optimized, sequential I/O, higher read/space amplification. A B-tree (InnoDB, most SQL engines) updates pages in place — read-optimized with predictable point lookups, but writes do more random I/O. Both still need a WAL for crash durability.