A meta-framework serves a route from a stored copy. After the underlying data changes, what makes visitors see the new value?
answer
- the cache is not watching the database
- two instruments only
- a signal, or the clock
- a signal needs a name for the copy
- the window is the backstop
basics
~20 sA stored copy never learns that the data behind it changed. It becomes unusable only when the application names that copy and marks it stale, or when its freshness window elapses and the next request produces the value again.
solid answer
~40 sA cached copy is bytes plus a key and a little metadata; nothing wires it to the database, so committing a write changes the source and leaves every stored copy exactly as it was. Two instruments can end that copy's life. The first is elapsed time: the copy carries a freshness window, and once the window ends the next request pays to produce the value again. The second is an explicit signal from the application after the write, which marks the copy stale on demand. Time needs no cooperation but bounds staleness only by the window you chose; a signal is immediate but works only for copies the application can name. Most meta-frameworks offer both, and real systems use a signal for the common case with a window as the backstop.
go deeper
Recall that a stored copy has no link back to the data source, and that only two things end its life: the freshness window running out, or the application explicitly marking it stale after the write.
Explain why invalidation makes a copy unusable rather than refreshing it, what that does to the next request, and why the same code path looks correct in development where server caching is bypassed.
Show that you design the write path to announce what it changed, keep a defensible window as the backstop, and can say which copies a signal will and will not reach before an incident forces the question.
Frame it as a contract question: state the maximum staleness each class of content may carry, decide whether the clock or the signal carries that promise, and make the choice reviewable rather than an accident of whoever wrote the handler.
## What a stored copy actually is When a meta-framework caches something on the server — the result of a data read, or the rendered output of a route — it stores three things: the **payload** (bytes, or a serialised value), a **key** that decides which later requests may be answered with it, and **metadata** (when it was stored, how long it should be treated as fresh, and any **labels** attached at the time). What it does *not* store is a connection to wherever the data came from. That independence is the whole point: a stored copy is fast precisely because answering from it touches nothing else. The consequence is the thing candidates underestimate — **committing a write is invisible to the cache**. The database is now right and every copy of the old value is now wrong, and no part of the system has noticed. ## The two instruments Only two mechanisms end a stored copy's usefulness, and every framework-level feature in this area is one of them dressed up: 1. **Elapsed time.** The copy carries a freshness window. Once that window has passed, the copy is no longer servable as-is and the value gets produced again. This needs no cooperation from the write path at all — nobody has to remember to do anything — but the staleness you accept equals the window you picked. 2. **An explicit signal.** After the write, the application tells the cache that a named copy (or a named group of copies) is stale. This is immediate and precise, but it only works for copies you can *address*, and only if the code that performs the write actually sends the signal. | | elapsed window | explicit signal | |---|---|---| | who triggers it | nobody; the clock | the write path, or an external event | | worst-case staleness | the length of the window | the propagation delay, if the signal arrives | | what it requires | choosing a number | a way to name the copy, and code that fires | | classic failure | a window too long for the content | a write path that forgets, or a copy with no name | Mature systems use both: the signal handles the normal case and the window bounds the damage when the signal is never sent, is dropped, or cannot reach a particular copy. ## What invalidation does, and what it does not Marking a copy stale is **not** the same as writing a new value into the cache. It makes the stored bytes unusable; the cost of producing the value again lands on whoever asks next. Two corollaries follow: - Invalidating a very popular route moves work onto the origin at exactly the moment it is being asked for. - Invalidation is not a fetch. If nobody requests that route afterwards, nothing is recomputed, and that is usually correct behaviour rather than a bug. What a request receives in the instant after invalidation differs between meta-frameworks — some make the next visitor wait for fresh output, others hand out the previous copy once more while the new one is produced — so answer at the level of the mechanism and say that the framework decides. ## Why this bites in production and not in development Most meta-frameworks run their development server with server-side caching disabled or effectively bypassed, so that an edit shows up on the next refresh. The stale path therefore never appears locally, and the first encounter with it is a production report: someone published a change and the site did not move. The fix is not to distrust the cache but to make the write path responsible for announcing what it changed. ## A practical checklist - For any value you cache, write down which of the two instruments is the **contract** and which is the optimisation. - Give every cached copy a freshness window you could defend out loud, even when you also signal it — the window is what protects you on the day the signal is lost. - Treat `a reload shows the new value` as evidence about **one** copy only; other copies of the same value may still be serving the old one. - Expect at least one copy you cannot address at all, such as a payload a visitor already has in an open tab; time and a fresh request are the only things that replace it. ## The mental model A cache is a promise you made in the past about a value you are no longer watching. Freshness is not something the data has; it is something you arrange, either by shortening how long the promise lasts or by taking the promise back explicitly after a write.
- If both a signal and a freshness window are in place, which one is the guarantee?The window. A signal can be forgotten in a new write path, dropped in transit, or unable to address a particular copy, and none of those failures announce themselves. The window is the staleness you are certain never to exceed, so pick it as the worst case you could defend, and treat the signal as what makes the typical case much better.
- Why does invalidating a very popular route sometimes make things worse for a few seconds?Because invalidation removes the answer without producing a replacement. Every request arriving after it has to wait for the value to be produced, so a route that was absorbing traffic from a stored copy suddenly forwards that traffic to the origin. Frameworks differ in whether they serve the previous copy meanwhile, which is exactly why that behaviour is worth knowing before you purge something hot.
A printed price list is accurate the moment it leaves the press. Changing the price in the back office changes nothing on the counter until someone walks over and pulls the sheet, or the sheet carries an expiry date.
saying these in an interview costs you the question
- Thinks committing a write updates cached copies by itself
- Says invalidation writes the new value into the cache
- Assumes what happens locally in development proves caching behaviour
- Treats one page reload as proof every copy was refreshed
- Confuses a freshness window with a promise the data is current
- Believes a copy with no label can still be targeted individually