A pushed update for a key arrives while a request for that same key is still in flight — which write wins?
answer
- arrival order is not age order
- a response is a snapshot from earlier
- compare markers, not timestamps of arrival
- no marker means refetch, not guess
- monotonic revision from the server
basics
~20 sNot simply the last to arrive: a response reflects server state from when the request started, so it can be older than an event landing after it. Order writes by a server-owned revision, or refetch when none exists.
solid answer
~40 sArrival order is not age order. A response carries the state the server had when it *handled* the request, which may predate a pushed event that arrives afterwards, so a naive last-write-wins cache can overwrite fresh pushed data with a stale response. The fix is to make writes comparable: if entries and events carry a monotonic revision or a server timestamp, the cache rejects any write whose marker is not newer than the entry's. Failing that, treat the collision as a signal rather than a merge — mark the key as touched while a request was in flight, and when the request settles, refetch that key and let the fresh read decide. The alternatives, discarding the response or discarding the event, are both coin flips.
go deeper
Take away one fact: data can reach the screen by two paths at once, and the one that arrives second is not necessarily the newer one.
Explain why a response is a snapshot taken when the server handled the request, and how a monotonic revision on both responses and events lets the cache drop the older write.
Demonstrate the production instinct: no marker means refetch rather than guess, and reconnect is where the race actually fires because the catch-up read and resumed events start together.
Own the contract. Asking the server for a per-record revision converts an unsolvable client heuristic into a comparison, and it fixes out-of-order events at the same time.
## Why the collision exists at all A cache entry has two writers. One is the request path: a component or loader asks for a key, and some time later a response arrives. The other is the bridge: an event for that key arrives whenever the server decides. Nothing coordinates them. The window is wide in practice — a slow request on a mobile network is hundreds of milliseconds to seconds, and the change that triggered the event may have happened inside that window. ## What a response actually means This is the point candidates miss. **A response is a snapshot of server state at the moment the server handled the request, not at the moment the client receives it.** So the sequence 1. client starts a read of `K`; 2. server reads `K` and starts serialising the old value; 3. someone changes `K`; the server emits an event; 4. the event reaches the client and the bridge writes the *new* value; 5. the response reaches the client carrying the *old* value ends with the cache holding the old value, despite every step behaving correctly. Last-write-wins on arrival is simply the wrong ordering function. ## Three resolutions | Approach | How it decides | Requires | Weakness | |---|---|---|---| | **Compare markers** | reject any write whose revision or server timestamp is not newer than the entry's | a monotonic marker on both responses and events | needs a server contract; clock-based markers are fragile across nodes | | **Refetch after the collision** | note that a push landed during the in-flight read, then refetch once it settles | nothing from the server | one extra round trip, and a brief window showing the older value | | **Prefer the event and drop the response** | assume the event is newer because it arrived later | nothing | wrong whenever the event described a change the response already includes, and it discards a full payload for a partial one | A fourth arrangement is worth knowing: **let the request own the entry while it is in flight**, meaning pushed events for that key are queued rather than applied, and replayed onto the entry after the response lands. It preserves order relative to the response but only if you are sure the response predates every queued event, which is exactly the thing you cannot know without a marker. ## Making the writes comparable The durable fix is a **revision** the server owns and stamps on both the resource and its events — a counter that only increases per record, or a version the write path already maintains for concurrency control. With one: - every write to an entry carries a marker; - the cache compares before applying and drops the older write; - out-of-order delivery of two events becomes safe too, not just push-versus-response. Timestamps are the common substitute and are weaker: they only order correctly if one clock produced them. A counter per record, or per stream, is more robust. ## Without a marker, prefer a refetch When you cannot get a marker, the honest position is that the cache **cannot tell which value is newer**, so it should not pretend. Mark the entry as contended, let the in-flight request settle, then invalidate and read again. It costs a round trip in an uncommon case, and it converges on the server's answer, which is the only one the user can check against. Do not paper over it with a delay before applying events — a fixed delay is an unmeasured guess and hides the race instead of resolving it. ## Where this bites in the product - **A detail screen opened right after an action.** The read starts, the action's own event arrives first, then the stale response paints over it, and the user sees their change revert on screen. - **A refetch triggered by the reconnect itself.** The catch-up read and a flood of resumed events race by construction; this is the single most likely place to hit the bug. - **Paged lists.** A response for page 2 that predates an insertion, merged next to pushed rows, produces duplicates or gaps that look like a pagination bug rather than a race. ## What good looks like in an answer Say that arrival order is not age order; name the marker-comparison fix and what it demands of the server; name the refetch fallback and its cost; and be explicit that discarding one side by rule is a guess. If you have to pick a default with no server changes available, the defensible one is *apply the event, then refetch once the request settles* — the user sees the newer value immediately and the cache converges on truth shortly after.
- Why are wall-clock timestamps a weak ordering marker for this?Because they only order writes correctly if one clock produced them. Responses and events often leave different processes or nodes, so two markers can disagree by more than the interval you are trying to resolve, and a backward clock step can make an older write look newer. A per-record counter avoids the whole class.
- Where in a live screen is this race most likely to fire?On reconnect. The catch-up read and the resumed flow of events start at the same moment by construction, so every subscribed key has a request and a push in flight together. If any collision handling is going to be exercised, it is exercised there — which is why the reconnect path deserves the marker comparison most.
- Is holding pushed events until the in-flight response lands a valid strategy?Only if you can be sure the response predates the queued events, which needs the same marker you were trying to avoid. Queue-and-replay does preserve order relative to the response, but applied blindly it can replay an event the response already reflects, and it delays fresh data behind a slow request.
saying these in an interview costs you the question
- Assuming whatever arrives last is the newest value
- Believing a pushed event is always newer than a response
- Using arrival timestamps taken on the client to order writes
- Blocking all pushed writes while any request is in flight
- Adding a fixed delay before applying events to dodge the race
- Treating the resulting flicker as a rendering bug