Section 12: The Doors Left Unlocked - Follow-up Feature Evolution
Executive Summary: The five features Section 1 deferred, plus server-side copy and append, arrive in the last ten minutes, and the test is whether they land as additions or demolitions. Each is an index change plus a consumer of the partition commit log, with the bytes on disk untouched: versioning is a chain of ~200 B records under one key (D11); lifecycle re-encodes cold data from RS(10,5) at 1.5× to RS(20,6) at about 1.3× (N16); cross-region replication replays the commit log into another region with an RPO of minutes (D12); notifications fan the same feed out to a topic; object lock is a retention timestamp the compactor must honour; server-side copy is a manifest pointing at stripes that already exist. Three earlier doors make it cheap: immutable extents (D6), the multipart manifest (D7), and a commit log that was always a change feed in waiting.
The interviewer flips back to the first page of notes. "In minute three you said versioning, lifecycle, replication and events were 'later'. It's later. Which of them breaks your design?"
You: "None of them, and the reason fits in one sentence: nothing in this system ever overwrites bytes, and every index change already goes through one ordered, replicated log. A feature that wants history reads the chain; a feature that wants to react reads the log."
Versioning: the Chain Was Already There
Without versioning, a PUT to an existing key writes a new record, tombstones the old one, and the compactor reclaims the old extents a day or more later (D6): it reaches a 30%-dead stripe within hours, and the fragments are freed 24 h after retirement. The previous bytes were never overwritten, only scheduled for reclamation. Versioning is the decision to keep what we were about to discard.
The record under (bucket_id, key) becomes a version chain (D11): each PUT appends a new version_id with its own size, ETag, wrapped data key (D10) and extent list, and a latest pointer moves to it. A DELETE writes a delete marker as the newest version: a GET without a version id sees it and returns 404, a GET with ?versionId= serves any version, and deleting the marker makes the previous version current again. LIST shows the latest non-marker version.
Here is Priya's thumbnail after three overwrites and a delete.
Interviewer: "What does it cost?"
You: "Index entries, not bytes. Each retained version is one more ~200 B record (N6) in the same partition, so a key with a thousand versions is 200 KB of index, which is why lifecycle expires old versions. The only data-layer change is that the compactor stops treating a superseded record as dead. The rejected alternative, copy-on-write into a shadow bucket, doubles the bytes on every overwrite and loses the ordering between versions. It never flips."
Key Insight: Versioning is cheap because D6 made deletion two-phase from the start. A delete marker is a tombstone the customer may see; a retained version is a tombstone the compactor is told to ignore.
Quick check before moving on:
Sign in to continue reading
The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.
Sign in to read the full lesson