Every caching strategy answers two questions.
Who fills the cache on a miss? And what does a write do to the cached copy, update it, skip it, or land there first?
Update the cache, or delete the key?
Bad: SET the new value
A paused writer lands last: cache ₹499, database ₹449.
Good: Delete, after commit
Deletes are idempotent and can’t be reordered into a wrong value.
Commit first, then delete. The order matters as much as the verb.
Delete first and a reader can miss, read the old row and put it back, for as long as your transaction runs.
A slow reader reads the row before your commit, then sets it after your delete.
The fix
Leases. A miss gets a token and a delete voids it, so the stale fill can’t land.
1.3K
peak DB queries/s on herd-prone keys, from 17K
Leases also tame herds. Each server issues at most one lease per key every 10 seconds; everyone else waits briefly instead of hitting the database.
Write-behind is fast and coalesced, until a node dies.
20,000 view increments a second, flushed every 5 seconds, is one UPDATE instead of 100,000. Lose the node first, and up to 100,000 increments vanish.
When the delete never happens
- Commit succeeds, delete fails: a dual write
- Outbox: write the invalidation in the transaction
- CDC: turn the database log into deletes
- Keep a TTL on every key as the backstop
Remember
Default to cache-aside: read the cache, fill misses with a TTL, and on a write commit, then delete. Close the races with leases or versions, and drive deletes from CDC.