Cache Invalidation
"One of the two hard problems in computer science." Phil Karlton said that for a reason.
The core problem
You cached user:42 five minutes ago. The value was { name: "Alice" }. Just now, Alice changed her name to Aliçia. The database has the new name. The cache still has the old one.
Until you do something about it, every read for user:42 returns the stale name. Alice sees the wrong thing. Other users see the wrong thing. Search results, profiles, the whole app shows old data.
This is the core problem of caching. There are three common ways to solve it. Each has tradeoffs.
Strategy 1. TTL (time to live)
Every cached entry gets an expiration time. After say 5 minutes, the entry is deleted automatically. The next read falls through to the database and refills the cache with fresh data.
The good part is how simple it is. You write no extra code to invalidate. It works for any kind of data.
The bad part is the lag. Stale data hangs around until the expiration fires. If your TTL is 5 minutes, users can see stale data for up to 5 minutes after a write. For most apps that is fine. For a profile page where the user just edited their own name, it is not.
Tuning the TTL is an art. A long TTL means a high hit rate and more staleness. A short TTL means a low hit rate and less staleness. Pick based on how much staleness your users can put up with.
Common defaults look like this. Minutes for user profiles. Seconds for prices. Hours for static metadata that almost never changes.
Strategy 2. Delete on write
When you write data, also delete the matching cache entry. The next read will be a miss. It fetches fresh data from the database and refills the cache.
The good part is freshness. The cache reflects the latest data right away. No stale reads.
The bad part is that you have to know which keys to delete. That is easy for simple data like user:42 on a profile update. It is hard for derived data. If you cache a leaderboard, every score update invalidates it. If you cache search results, every new post invalidates many keys at once.
There is also a small race condition to watch for. Reader A reads from the database just before a writer updates it. The writer deletes the cache entry. Reader A then writes its old value back into the cache. Now the cache is stale again. This is rare in practice but matters at very high concurrency.
People often pair this with a TTL as a safety net.
Strategy 3. Write-through (skip the problem)
If every write goes through the cache, the cache is always current. There is no separate invalidation step. The write itself updates both stores.
The good part is consistency. No special logic. Always in sync.
The bad part is speed. Writes are slower because you wait for two stores. It also does not help when the underlying data changes through a side door. A batch job that writes straight to the database will leave the cache wrong.
In real systems, most teams use a combo of all three. Cache-aside for reads. Delete on write for freshness. TTL as a safety net in case a delete is missed.
That stack gives you three layers of correctness.