</> MAANG.io

Expert

Design landmark systems in depth, from Dynamo, GFS, Bigtable and Kafka to payments and ads pipelines.

0 of 232 solved 0%

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.

Versioning chain for key clip-00123/0001.jpg: the latest pointer leads to a v4 delete marker, whose previous links run v3 (40 KB, extent list, pointing at an EC stripe of 15 fragments) to v2 to v1; a GET with no version id sees the marker and gets 404, a GET for versionId v3 is served normally, and a DELETE of versionId v4 removes the marker so v3 is current

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

Sign in to MAANG.io

Use your Google or Microsoft account — no password to remember.

Continue with Google Continue with Microsoft

Please accept the terms above to continue.