What Are Cache Invalidation Strategies?
Learn cache invalidation strategies including TTL expiry, write-through, write-behind, and event-driven invalidation with examples.
Expected Interview Answer
Cache invalidation strategies are the mechanisms a system uses to remove or refresh stale cached data once the underlying source of truth changes, and the two dominant approaches are TTL-based expiry and explicit write-time invalidation.
TTL (time-to-live) expiry assigns every cache entry a lifespan after which it is automatically treated as stale and refetched, which is simple and self-healing but can serve outdated data for up to the TTL window and causes a “thundering herd” of refetches if many keys expire together. Explicit invalidation instead actively deletes or updates the cache entry the moment the underlying data changes, typically via write-through (update cache and database together), write-behind (update cache immediately, persist to database asynchronously), or cache-busting on write (delete the key so the next read repopulates it). Event-driven invalidation, where a database change stream or message bus notifies caches to purge specific keys, scales this pattern across distributed caches without polling. The right strategy balances staleness tolerance against the operational complexity of guaranteeing every writer correctly invalidates every affected key.
- TTL expiry is simple and self-healing without needing every writer to cooperate
- Explicit invalidation minimizes staleness by reacting to writes immediately
- Event-driven invalidation scales consistent purging across many distributed cache nodes
- Combining TTL with explicit invalidation bounds worst-case staleness while still reacting fast
AI Mentor Explanation
Cache invalidation is like how a stadium scoreboard stays accurate after every ball bowled. A TTL-style approach refreshes the whole board every thirty seconds regardless of whether anything actually changed, so for a moment fans might see a stale score. An explicit-invalidation approach instead has the scorer immediately push an update the instant a run is scored or a wicket falls, so the board reflects reality without waiting for the next refresh cycle. Real stadiums usually combine both: instant pushes for major events and a periodic refresh as a safety net.
Step-by-Step Explanation
Step 1
Underlying data changes
A write happens against the source of truth (database), making the currently cached copy stale.
Step 2
System decides how to react
A TTL-based cache waits for the entry’s lifespan to expire; an explicit strategy reacts to the write immediately.
Step 3
Stale entry is purged or updated
Write-through updates cache and database together; cache-busting simply deletes the key so the next read repopulates it.
Step 4
Next read repopulates the cache
A subsequent read misses (or refetches) and pulls the fresh value from the source of truth back into the cache.
What Interviewer Expects
- Distinguishes TTL-based expiry from explicit write-time invalidation
- Names concrete patterns: write-through, write-behind, cache-busting on write
- Mentions event-driven invalidation for distributed caches (e.g., change streams, pub/sub)
- Acknowledges the trade-off between staleness tolerance and invalidation complexity
Common Mistakes
- Assuming a longer TTL is always safer without weighing staleness cost
- Forgetting that explicit invalidation requires every writer to remember to invalidate
- Confusing cache invalidation with cache eviction (different problems: staleness vs capacity)
- Not considering the thundering herd effect when many TTLs expire simultaneously
Best Answer (HR Friendly)
“Cache invalidation is how a system makes sure cached data does not go stale after the real data changes underneath it. The two main approaches are giving cached data an expiration time so it automatically refreshes, or actively clearing the cache the moment the underlying data is updated, and most real systems use a mix of both to balance freshness and complexity.”
Code Example
async function updateUser(userId, changes) {
// 1. Update the source of truth first
const updated = await db.users.update(userId, changes)
// 2. Explicitly invalidate the stale cache entry
await cache.delete(`user:${userId}`)
// 3. Optionally repopulate immediately (write-through)
await cache.set(`user:${userId}`, updated, { ttlSeconds: 300 })
return updated
}
async function getUser(userId) {
const cached = await cache.get(`user:${userId}`)
if (cached) return cached
const user = await db.users.findById(userId)
await cache.set(`user:${userId}`, user, { ttlSeconds: 300 }) // TTL as safety net
return user
}Follow-up Questions
- What is the thundering herd problem and how does it relate to TTL-based invalidation?
- How would you invalidate a cache entry consistently across multiple distributed cache nodes?
- What is the difference between write-through and write-behind caching?
- How do you handle invalidation when the write happens outside your application (e.g., a direct database update)?
MCQ Practice
1. What is the main drawback of relying purely on TTL-based cache invalidation?
TTL-based expiry only refreshes data once the lifespan elapses, so stale data can be served for up to that window.
2. What does “cache-busting on write” mean?
Cache-busting removes the stale key right after a write, forcing the next read to fetch fresh data from the source of truth.
3. Why might a system use event-driven invalidation via a message bus?
Event-driven invalidation propagates purge signals across distributed caches reactively, avoiding wasteful polling.
Flash Cards
What is cache invalidation? — The mechanism for removing or refreshing stale cached data when the source of truth changes.
TTL-based invalidation? — Cache entries automatically expire after a fixed lifespan and get refetched.
Write-through caching? — Updates the cache and the database together at write time.
Why combine TTL and explicit invalidation? — Explicit invalidation reacts fast to writes; TTL bounds worst-case staleness as a safety net.