beprodready
← Build Your Own Database

Stage 3: SSTables -- Flushing to Disk

stage 3 of 4 · ~30 min · runs in your browser

Objective:Flush the memtable to sorted, immutable on-disk segments and layer reads across them.

SSTable (Sorted String Table)Immutable on-disk segmentsMulti-layer read path (memtable -> segments, newest first)Tombstone markers for deletesRead amplificationWrite O(1) vs read O(N segments)

A memtable can't grow forever. When it fills, LSM engines flush it to disk as an SSTable -- an immutable, sorted run of key-value pairs. Reads then check the memtable first, then SSTables newest-to-oldest: the most recent write always wins.

Extend your store with:

  • store.flush() -- moves the current memtable into store.segments as { entries: [[key, value], ...] } sorted by key, then empties the memtable. Deletes flush too, as [key, null] tombstones (you need them later -- an older segment may still hold the key).
  • get(key) -- memtable first; on miss, scan segments from newest to oldest; a tombstone (null) means "deleted", stop looking.
  • store.segments -- newest segment last in the array.

Why sorted? Binary search. An SSTable in production is a file on disk; finding a key in O(log n) requires the file to be sorted. You'll use a sorted entries array to model this. In LevelDB/RocksDB, the SSTable also has a sparse index and Bloom filter at the end -- keys pointing to blocks -- but the sort is the primitive.

Why immutable? Two reasons: (1) sequential writes are fast, in-place updates are not; (2) multiple readers can read the same SSTable file concurrently without locks. Immutability is a core correctness and performance property.

Why this matters in production

The memtable can't grow forever -- it's RAM. When it fills, LSM engines flush it to an SSTable (Sorted String Table): a sorted, immutable file on disk. From this point forward, reads must check ALL layers: memtable first (RAM, newest), then segments from newest to oldest. This multi-layer read path is the defining characteristic of LSM engines and the root cause of both their read amplification problem and their write advantage. After Stage 3 your engine has the same fundamental architecture as LevelDB's L0 flush -- you're missing only the index (Bloom filter + sparse index) that LevelDB adds to avoid scanning every SSTable byte by byte.

tests (6)

○ flush produces a sorted immutable segment

○ flush clears the memtable

○ reads fall through to segments after flush

○ the memtable beats older segments

○ newer segments beat older segments

○ tombstones flush and hide older values

createStore.js