skip to content

How do you decide whether a pushed event should replace a cache entry, patch one field, or invalidate it?

level: middleimportance: should knowfreq 47%

answer

  1. the payload decides the merge
  2. full record, partial record, bare notice
  3. one event, several keys
  4. patch the detail, invalidate the list
  5. invalidation is safe and costs a read

basics

~20 s

By what the payload can support: a full resource replaces the entry, a partial change with a stable identity patches it, a bare notification only invalidates the affected keys. Never patch from a payload that leaves the record inconsistent.

solid answer

~50 s

The merge is chosen per event shape, not once for the channel. If the event carries the whole resource, **replace** the entry — cheapest and always consistent. If it carries a stable identity plus the changed fields, **patch** that entry in place, which avoids a request but only works when the partial payload leaves the record internally consistent. If it carries only "something about `X` changed", **invalidate** the affected keys and let the next read refetch; that costs a round trip but can never write a half-truth. The second half of the decision is *which* keys: an event about one record usually touches the record's own entry and every list or aggregate entry that contains it, and those often need different merges — patch the detail, invalidate the list whose ordering or count may have shifted.

go deeper

for a junior

Learn the three outcomes an event can have on a cached value: it can overwrite it, change part of it, or just mark it out of date so it is fetched again.

for a middle

Explain what each merge requires from the payload, and why a partial payload without a stable identity can only justify invalidation. Name the list and aggregate entries a single event also touches.

for a senior

Show the guards: consistency between fields that travel together, a missing entry, a deletion, two patches arriving out of order, and coalescing a burst into one refetch.

for a principal

Treat the event contract as negotiable. A stable identity plus a monotonic marker turns an expensive invalidate-everything client into a cheap patching one; that is a backend conversation worth having early.

## The merge is a property of the event, not of the channel A channel is a pipe; the decision that matters is what each event authorises you to do to the cache. Teams that pick one policy for the whole channel either refetch far too much (invalidate everything) or corrupt entries (patch from payloads that cannot support a patch). The useful habit is to look at each event type and ask: **what is the strongest merge this payload can justify?** ## Three merges and what each demands | Merge | The payload must carry | What it costs | When it is right | |---|---|---|---| | **Replace** the entry | the full resource, in the same shape a read returns | nothing beyond the event itself | the server can afford to emit whole records | | **Patch** the entry | a stable identity plus the changed fields, enough to keep the record self-consistent | no round trip, but a merge rule per event type | high-frequency changes to a few fields | | **Invalidate** the key | only the identity of what changed | a round trip on the next read | notification-only events, or anything whose effect on derived data you cannot compute locally | A fourth option is worth naming because it is often the right one for a list: **invalidate but keep serving the current value** so the view does not empty out while the refetch runs. ## Which keys an event touches One change rarely means one entry. A single "order updated" event plausibly affects: - the **detail entry** for that order — usually patchable; - every **list or page entry** that contains it — patchable only if you can compute membership and position locally, which you often cannot; - **aggregates** such as counts, totals or badge numbers — usually not derivable from the event alone; - entries for **related records** whose displayed copy of this data is denormalised. This is why the merge decision and the key-mapping decision are the same decision. A common and defensible pattern is *patch the detail, invalidate the collection*: the open record updates instantly, and the list corrects itself on its next read without you re-implementing the server's sort and filter in the client. ## Patching safely A patch writes over part of an entry, so it can leave the entry in a state the server would never produce. Guard it: 1. **Identity first.** If you cannot map the event to exactly one entry, you cannot patch. 2. **Consistency.** If two fields must agree — a status and a timestamp, a total and its line items — patching only one produces a record that never existed. Either the event carries both, or you invalidate instead. 3. **Missing entry.** If the entry does not exist yet, decide deliberately: create it from the event (only if the event is a full resource), or drop the event. 4. **Deletions.** A removal event is its own merge: drop the entry and invalidate the collections that listed it. Patching a `deleted` flag onto an entry that readers render as present is a classic way to show ghosts. 5. **Ordering hazard.** If two patches for one entry can be applied in the wrong order, the entry ends up with an older field value. A revision or timestamp on the entry lets you reject the older write; without one, prefer invalidation for anything whose correctness matters. ## Shaping the event contract The merge you want is usually a backend conversation, not a frontend workaround. If the client keeps invalidating and refetching after every event, ask for fuller payloads; if the client keeps writing half-correct entries, ask for a revision marker and the fields that travel together. Two properties make an event stream cheap to consume: **a stable resource identity** and **something monotonic to order writes by**. Everything else — batching, filtering, which events a client subscribes to — is tuning. ## Cost model Invalidation is the safe default and the expensive one: a burst of events becomes a burst of reads unless the cache coalesces them. Replacement is safe and cheap but pushes payload size onto every subscriber. Patching is cheapest on the wire and the only one that can be *wrong*. Choose deliberately, per event type, and write the choice down next to the event contract — it is the part a future reader cannot infer from the code.

  • Why is invalidating a list entry often better than patching the pushed record into it?
    Because membership and position are server decisions. To patch a list correctly you must reproduce its filter, sort and pagination in the client, and any drift shows as items in the wrong place or missing entirely. Invalidating costs one read and gets the server's answer, which is the one the user compares against.
  • A burst of events invalidates the same key fifty times in a second. What should the cache do?
    Coalesce. Mark the key invalid once and schedule a single refetch on the next read or after a short window, rather than issuing a request per event. The same applies to replacements: collapse them and render the last, since intermediate values were never seen.
  • How should a removal event be merged?
    As its own case: evict or tombstone the record's entry and invalidate every collection that listed it. Patching a deleted marker onto an entry that readers render as a normal record leaves ghosts on screen, and silently dropping it without touching the collections leaves a list that still shows the row.

saying these in an interview costs you the question

  • Applying one merge policy to every event on the channel
  • Patching from a payload that cannot keep the record consistent
  • Forgetting the list and aggregate entries the record appears in
  • Refetching once per event during a burst instead of coalescing
  • Treating a removal as a field patch rather than an eviction
  • Creating a cache entry from a partial event payload