When a viral post's cache entry is split into N suffixed copies, how do you update it without readers flip-flopping between old and new versions?
answer
- N writes are not atomic
- random pick per request
- monotonic reads
- stick each client to one copy
- versions only move forward
basics
~20 sOverwriting copies one by one lets a reader who picks copies at random see new, then old, then new. Pin each client to one forward-only copy, or have clients reject versions older than one already seen; TTL bounds failed updates.
solid answer
~50 sWith N copies, an update is N separate writes to different nodes, and they don't land at the same instant. A reader that picks a random suffix per request can hit an updated copy and then a stale one, so the edit appears and then disappears. To prevent that, choose the suffix from `hash(client_id) mod N`, so each client always reads the same copy. Also make each write **version-checked**, so a delayed older write can't overwrite a newer value. Then each client's reads only move forward. Alternatively, store a version in the value and have clients discard anything older than the highest version they have seen. A copy whose update failed stays stale until its **TTL** expires or a reader notices the older version and deletes it. For a viral post, a window of a second or so is usually acceptable.
go deeper
Recall that splitting one cache entry into several copies means an update has to change every copy, and they won't all change at the same moment.
Explain how random copy choice during a partial update lets one reader see a value go back to an older version.
Offer concrete fixes: client-affine suffixes with version-checked writes, reader-side version checks with repair, and TTL as the upper limit when an update fails.
Decide which hot values actually need monotonic reads, and weigh the extra write and read complexity against how visible a brief mix of versions would be.
## Why split copies create a new consistency problem **Key splitting** stores one hot value under N keys, `post:9#0` … `post:9#N-1`, which hash to different nodes. Readers choose a suffix, and read load per copy falls to about 1/N. Writes pay the price: - an edit is **N independent writes** to N nodes, not one atomic operation - they complete at different moments, and some may fail or be retried late - until all succeed, some copies hold the new value and some hold the old one If readers choose a **random** suffix per request, a single user refreshing the post can see: 1. the corrected text (from an updated copy) 2. the old text (from a copy not yet updated) 3. the corrected text again This is a violation of **monotonic reads**: a reader sees a value go back to an older version after already seeing a newer one. Users notice it, especially when the edit fixes something embarrassing. ## Technique 1: client-affine copies Choose the suffix from something stable about the client: - `suffix = hash(client_id) mod N` sends each client to one copy every time - load still spreads across copies, because different clients hash to different suffixes - a single client now sees its copy change once, from old to new This gives monotonic reads only if two things also hold: - **each copy only moves forward.** Writes carry a version and are applied only when newer than the stored one, so a delayed retry of an older write can't roll a copy back - **the client keeps its suffix.** If N changes or the client's id changes, it may land on a different copy that is behind ## Technique 2: versions checked by the reader Embed a monotonically increasing version, such as an edit counter from the source of truth, in every cached value: - the client remembers the highest version it has seen for that post - a read that returns a lower version is ignored: the client keeps its newer value or tries another copy - the reader can also **repair** the stale copy by deleting it, so the next read refills it from the source This works even with random suffixes. It costs a little client-side state and a slightly more complex read path. ## Technique 3: delete instead of overwrite On edit, delete all N copies instead of writing new values. Readers that miss refill from the source of truth, which already has the new version. - this shrinks the flip-flop window to the time the deletes take, but doesn't remove it: a copy not yet deleted still serves the old value - it costs up to N source loads, one per copy, which is acceptable for a rarely edited post and risky for a value that changes often ## Handling partial failure Some of the N writes or deletes will fail. The stale copy is limited by: - its **TTL**: with a 60-second TTL on every copy, a copy whose update failed is refilled within 60 seconds - **read repair**, as in technique 2 - a **retry queue** that re-applies the update to copies that failed | Technique | Monotonic per client? | Extra cost | |---|---|---| | Random suffix, overwrite | No | None | | Client-affine suffix + version-checked writes | Yes, while the suffix is stable | Conditional writes | | Reader-side version check | Yes | Client state, possible re-reads | | Delete all copies on edit | Narrower window only | Up to N source loads | ## When this matters For many hot values, such as view counts or a product description that rarely changes, a brief mix of versions is harmless and plain overwrite is fine. It matters when an edit is **visible and meaningful**, like a correction to a viral post, a price or a match score, and when one user reads the value repeatedly within the update window.
- Why is a version check on the write side needed even with client-affine suffixes?Updates can be retried or delayed. If an older write lands after a newer one, the client's copy moves backwards, and the client sees an old value after a new one. Applying a write only when its version is higher keeps every copy moving forward.
- What happens to client-affine reads when N changes from 8 to 12?`hash(client_id) mod N` gives a different suffix for most clients, so they switch copies. A client can move from an updated copy to one that is behind and briefly see an older value. Reader-side version checks, or changing N only between edits, avoid that.
saying these in an interview costs you the question
- The cache updates all suffixed copies in one atomic step
- Readers can see a mix of old and new fields in one value
- Random suffix choice guarantees each user sees only newer values
- Deleting all copies removes any chance of stale reads
- A copy whose update failed stays wrong until the next edit, even with a TTL