One hash lookup, one pread

A get is a constant-time keydir lookup followed by exactly one pread at the recorded offset — bounded at one disk seek worst case, and zero seeks when the page cache is hot.

Previously

The writer was sequential because the disk cares. The keydir doesn't — it's a hash, and lookups are O(1). One pread closes the loop.

Scene 03

One hash lookup, one pread

  1. Watch
  2. Try it
  3. Predict
  4. Capture
DATA DIRECTORY001.dataclosed · immutablecrc:7a|t04|k…k1 = v1-o…crc:9b|t05|k…k7 = v7002.dataclosed · immutablecrc:c2|t11|k…k1 = v1crc:1f|t12|k…k2 = v2crc:55|t13|k…k3 = v3003.dataactive · acceptingcrc:33|t14|k…k4 = v4crc:e1|t15|k…k5 = v5KEYDIR (in RAM)keyfile_idvalue_posvalue_szk1002143264Bk2002152896Bk3002162448Bk40038480Bk500318056Bk700122464Bpread 64 B @ 1432OPERATIONGET k1002.data@1432 · 64B 0 µsPAGE CACHEwarm: 0 / 64 pageshit rate: hit 0% · 0 preadshitmisslast 0Cold cache — first get pays one pread (~ms). The seek is bounded; a B-tree would pay log N.
What to watch for

Watch a single GET k1. The keydir row pulses, an arrow flies to file 002 at offset 1432, and the pread cursor highlights exactly value_sz bytes — not the whole record (value_pos already skips the header). The pread counter ticks up by one.

Continue unlocks when the animation finishes.
Implementation

Highlighted lines are the ones running in the diagram right now.

KeydirEntry (struct)
fixed-size RAM entry per live key — no value bytes
struct KeydirEntry:
file_id # which .data file holds the live record
value_pos # byte offset of the VALUE (header skipped)
value_sz # exact length to pread
tstamp # last-write timestamp (wins on replay)
Bitcask.get
O(1) keydir lookup, then one pread at the recorded offset
def get(key):
entry = keydir.get(key) # O(1) hash probe
if entry is None:
return None # key never existed / deleted
fd = fd_cache.open(entry.file_id)
buf = pread(fd,
n=entry.value_sz, # exactly value_sz bytes
offset=entry.value_pos)
return buf # caller may verify CRC
OS.pread (kernel)
the syscall is the same; the kernel decides if disk is touched
def pread(fd, n, offset):
page = (offset // PAGE_SIZE) * PAGE_SIZE
if page in page_cache: # HOT — bytes already in RAM
return copy_from(page_cache[page], offset, n)
block = disk.seek_and_read(fd, page) # COLD — one seek
page_cache[page] = block
return copy_from(block, offset, n)

Where this sits in Build a Bitcask-style KV store

Scene 03 of 9, in the Anatomy act — Log on disk, hash in RAM — and the four-step write.. get is a constant-time keydir lookup + at most one pread; bounded by one disk seek, zero when the page cache is hot.

Up next. One seek for any read, hot or cold. That ceiling exists because the index lives in RAM. The next scene asks how much RAM.

All 9 scenes in Build a Bitcask-style KV store · Every curriculum

Built with Arqly
Every scene in Build a Bitcask-style KV store builds on the one before it.All 9 Build a Bitcask-style KV store scenes