Backend & Node.js · APIs, data and failure
How do you invalidate a cache without serving stale data?
Short answer
Use TTLs as a safety net, delete or update cache entries on writes, and version keys when a whole class of data changes. Invalidation is a consistency trade-off, so define the maximum acceptable staleness and make it observable.
There are only two hard problems in cache invalidation: knowing when data changed and coordinating that change across every cache layer. TTLs bound staleness but do not make data fresh. Explicit invalidation improves freshness but adds coupling between the writer and the cache.
async function updateUser(id, patch) {
const user = await db.user.update({
where: { id },
data: patch,
});
await redis.del("user:" + id);
await redis.del("user:" + id + ":profile");
return user;
}Strategies and races
- TTL-only invalidation is simple and tolerates bounded staleness
- Delete on write, then let the next read repopulate the cache
- Write-through keeps the cache and database in step but adds write latency
- Versioned keys make old entries unreachable when a schema or dataset changes
- Event-driven invalidation through a queue decouples writers but introduces delay
- A read that started before a write can repopulate stale data after the delete
The classic race is a reader that misses, queries the old value, then writes it back after the writer has already deleted the key. Mitigations include a short delete-after-write delay, versioned keys, or a lock around the miss. No approach is perfect without stronger coordination; choose based on how bad stale data is.
Follow-ups you should expect
Interviewers use these to check whether the first answer was understanding or memorisation.
- 1Why is deleting on write often safer than writing the new value?
- 2How do versioned keys help invalidation?
- 3What is the read-repopulate-stale race?
- 4How do you invalidate a CDN cache?
Say it in the interview
I combine TTLs with explicit invalidation. On a write I commit to the database and delete the affected keys, letting the next read repopulate. For larger changes I version the key so old entries become unreachable. There is still a race where a slow reader writes back stale data, so I use short delays, locks or versioned keys depending on how much staleness the domain can tolerate, and I track invalidation lag.
Use this as a model, not a script. Saying it in your own words is what makes it survive follow-up pressure.
Related questions
Reading is step one
Now say it out loud.
Understanding an answer and delivering it under pressure are different skills. Rehearse this topic in a live Backend & Node.js mock interview and get scored feedback on depth, clarity and confidence.