skip to content

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?

level: seniorimportance: nice to knowfreq 24%

answer

  1. N writes are not atomic
  2. random pick per request
  3. monotonic reads
  4. stick each client to one copy
  5. versions only move forward

basics

~20 s

Overwriting 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 s

With 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

for a junior

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.

for a middle

Explain how random copy choice during a partial update lets one reader see a value go back to an older version.

for a senior

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.

for a principal

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