Tombstones — deletes that come back
A tombstone is a record stating absence; if merge garbage-collects the tombstone before every older segment containing the key has been merged away, restart will replay the older live record and resurrect the deleted key.
fsync chose how much of the tail to keep. Merge chose how much of the dead to reclaim. The collision between those two policies is where deleted keys come back from the dead.
Scene 07
Tombstones — deletes that come back
- Watch
- Try it
- Predict
- Capture
Naive merge rule. Five steps animate left to right, ~2s each: PUT, DELETE, merge, crash, restart. Step 5 is the punchline — restart rebuilds the keydir from disk and the deleted key reappears with a red WRONG badge.
Highlighted lines are the ones running in the diagram right now.
def delete(key):tomb = Record(key = key,value = TOMBSTONE_MARKER, # vsz = 0tstamp = now(),)active_file.append(tomb)keydir.pop(key, None) # RAM only — disk still holds older PUT
def merge(window_files, keydir):out = new_data_file()for f in window_files:for rec in f.scan():if rec.is_tombstone:# key already gone from keydir → assume redundantif rec.key not in keydir:continue # drop the tombstoneelif keydir[rec.key] != (f.id, rec.offset):continue # stale PUT, shadowed by newer recordout.append(rec)atomic_swap(window_files, out)
def merge(window_files, keydir, all_files):out = new_data_file()for f in window_files:for rec in f.scan():if rec.is_tombstone:older = oldest_file_with(rec.key, all_files)if older and older not in window_files:older.append(rec) # carry tombstone backwardscontinue# safe to drop: no older file mentions this keycontinueelif keydir[rec.key] != (f.id, rec.offset):continueout.append(rec)atomic_swap(window_files, out)
def open(dir):keydir = {}for f in sorted(dir.data_files(), key=lambda f: f.tstamp):for rec in f.scan():if rec.is_tombstone:keydir.pop(rec.key, None)else:keydir[rec.key] = (f.id, rec.offset, rec.tstamp)return keydir # whichever record wins by tstamp wins on disk
Where this sits in Build a Bitcask-style KV store
Scene 07 of 9, in the Sharp edges act — Tombstone resurrection and the design ceiling.. If merge GCs a tombstone before every older segment containing the key has been merged away, restart resurrects the deleted key. The riak_kv #925 fix.
Up next. Tombstones must outlive every older segment that mentions the key — the rule is nontrivial because every log-structured store ships some version of this trap. Now zoom all the way out and design.
All 9 scenes in Build a Bitcask-style KV store · Every curriculum