Keeping the Cache Fresh

An editor publishes a post. The database now has it, but the cache still holds the published list from before the publish. Every reader who hits the cache gets a list without the new post.

Any write to a row that a cached value was built from leaves the cached value wrong until something replaces it.

Invalidation

The direct fix is to remove the cached copy when the data it was built from changes. That is invalidation. When an editor publishes a post, the application commits the database write and then deletes the published list from the cache. The next reader misses, the application reads the new list from the database, and puts it in the cache. Readers whose requests overlap the publish can still see the old list for a short time.

Invalidation has to happen in the application, in the same code that writes to the database. Whenever the application writes something a cached entry was built from, it also removes that entry from the cache.

Invalidation depends on the developer remembering to remove the entry in every place the application changes that data. If one place is missed, the cache keeps serving the old value and nothing tells you that it happened.

Expiry

Another way to keep the cache fresh is expiry. Each cache entry is given a time to live, a duration after which the cache drops the entry on its own. A request for that entry after it is dropped misses, and the application rebuilds the entry from the database.

If the developer forgets to remove an entry, expiry still removes it when its time to live runs out. If the published list expires after a day, a missed invalidation means readers see the old list for at most a day instead of indefinitely.

Expiry alone is not enough. With a time to live of a day and no invalidation, a newly published post could take up to a day to appear in the list. We should use both for HopPress. Invalidation removes the old list after a publish, so the next rebuilt entry includes the new post. Expiry limits how long a stale entry stays in the cache if an invalidation is missing.

Staleness

Between the database write and the moment the cached entry is removed and rebuilt, readers see the old value. That window is staleness. It is the cost a cache pays for faster reads. A materialized view has the same kind of cost, which we called refresh lag. Denormalization has a related risk, called drift: if its two writes do not stay together, the copies disagree.

You can set the time to live for each key separately. You decide it from what the system requires and from how people use it. For a system that accepts delayed publishing, a few hours might be reasonable. For a news website, a few seconds may be too late.