Append, fsync, update, ack
Every put is four ordered steps — append at end-of-file, fsync per policy, update keydir in place, return ack — and the single open writer (enforced at open(), not at write time) makes that ordering the throughput ceiling of one Bitcask instance.
The split is the shape; the order is the contract. Append, then fsync, then keydir update, then ack — and one open writer at a time. That ordering is what makes the disk and the RAM agree.
Scene 02
Append, fsync, update, ack
- Watch
- Try it
- Predict
- Capture
A single producer issues PUT k1=v1. Watch the four steps light up in order — append at the tail, fsync, keydir row updated, ack to the client — and the throughput meter climb as more puts stream in.
Highlighted lines are the ones running in the diagram right now.
def put_record(key, value):rec = encode(crc, tstamp, ksz, vsz, key, value)# 1) append at end-of-file on the active filepos = active_file.sizeactive_file.write(rec) # sequential append# 2) fsync per sync_strategy (none | o_sync | interval)if sync_strategy == 'o_sync':active_file.fsync()# 3) update keydir entry in placekeydir[key] = (active_file.id, vsz, pos, tstamp)# 4) ack to the clientreturn ok
def open(dir, mode):if mode == read_write:# one writer per directory, enforced herelock = try_flock(dir + '/bitcask.write.lock')if lock is None:return error('already open for writing')active_file = open_or_create_active(dir)keydir = scan_or_load_hint_files(dir)return Handle(dir, active_file, keydir, lock)# read-only handles share freelyreturn ReadOnlyHandle(dir)
def maybe_rotate(active_file):if active_file.size < max_file_size: # default 2 GBreturn active_file# 1) close the current active file — now immutableactive_file.close()immutable_files.append(active_file)# 2) open a fresh active file for subsequent appendsnext_id = active_file.id + 1return create_active(dir, next_id)# rotation is not merge: closed bytes are unchanged here
Where this sits in Build a Bitcask-style KV store
Scene 02 of 9, in the Anatomy act — Log on disk, hash in RAM — and the four-step write.. Every put is four ordered steps on one open writer. Concurrent writers are rejected at open(), not at write time.
Up next. Writes funnel through one append + one fsync. Reads don't share that bottleneck — the keydir is random-access, so a get is bounded by the laziest part of the OS, not by the writer.
All 9 scenes in Build a Bitcask-style KV store · Every curriculum