Caching Patterns & Write Policies
Default to cache-aside reads plus invalidate-on-write, pick the write policy by its durability/latency trade, and always state the TTL and consistency window.
A cache is a bet, and the bet needs terms
A cache is a bet that the same data will be read many times before it changes. Getting the bet right means choosing how reads populate the cache, how writes keep it honest, and how stale entries leave. Get it wrong and you serve wrong data or you overload the database you were trying to protect.
The read path
Cache-aside (lazy loading) is the default in almost every real system. The application checks the cache; on a hit it returns; on a miss it loads from the database, writes the value back into the cache, and returns it. Only requested data is ever cached (no wasted memory), and a cache outage degrades to slower DB reads rather than an outage. The downside is that every cold key pays one miss, and your app code owns the population logic. Read-through hides that logic behind the cache client, which is cleaner but couples you to a client that understands your data source.
The write path, where the real tradeoffs live
- Write-through: every write goes to the cache and the database synchronously before the write returns. The cache is always consistent with the DB, but every write pays two hops of latency, and you cache data that may never be read again.
- Write-back (write-behind): the write updates the cache and returns immediately, and the cache flushes to the DB asynchronously in batches. Lowest write latency, absorbs bursts, but you now own a durability risk: if the cache node dies before the flush, those writes are gone. Use it only where some loss is tolerable (view counts, metrics).
- Write-around: writes go straight to the DB and skip the cache, so the cache fills only on the next read. Avoids polluting the cache with write-once data, at the cost of a guaranteed miss on freshly written keys.
The most common pattern in practice is cache-aside for reads plus invalidate-on-write: on a write, update the DB and then delete (not update) the cache key, so the next read re-populates from the source of truth. Deleting rather than updating avoids a subtle race where two concurrent writers leave a stale value behind.
Expiry, sizing, and the numbers
Every entry gets a TTL, and you add jitter (say 300s plus or minus a random 30s) so a cohort of keys written together does not all expire at the same instant. Eviction policy (LRU for recency, LFU for frequency) decides what leaves when memory fills. The number you optimize is the cache hit ratio: at 95% hits your DB sees 5% of read traffic, so a drop from 95% to 90% doubles DB load. Size the cache so the hot working set fits in memory; caching the long cold tail buys nothing. Negative caching (caching "this key does not exist" for a short TTL) stops repeated misses from hammering the DB for absent keys.
Interview nuance: saying "add a cache" with no invalidation story is the fastest way to lose a senior interviewer. Always pair a write policy with how and when entries become stale, and name your consistency window: with a 60s TTL and no invalidation you are promising up-to-60s-stale reads, fine for a product page and unacceptable for an account balance.
READ (cache-aside) WRITE (invalidate-on-write)
app -> cache? app -> DB (source of truth)
hit -> return then -> DELETE cache key
miss -> DB -> set cache next read re-populates
Recap: default to cache-aside reads plus invalidate-on-write, pick a write policy by its durability and latency tradeoff, and always attach a TTL-with-jitter and a stated consistency window so the cache is defensible, not just present.
You are defending a cache in a design review. Match each requirement to the write pattern you would reach for.
Apply
Your turn
The task this lesson builds to.
Design the caching layer for a read-heavy product page (95% reads) backed by a database that can serve only 10% of peak read traffic.
Think about
- Which write policy fits, and what is its durability tradeoff?
- How do you size the working set so the hot data fits in memory?
- How do you keep cache and source of truth in sync?
Practice
Make it stick
A second problem on the same idea, so it survives past today.
Design the caching strategy for Amazon's product detail page during a Prime Day spike where a small set of doorbuster SKUs draws 500K reads/sec while their price and inventory change every few seconds. Explain how you keep the displayed price correct while surviving the read volume, and lead with the concrete cache topology.
Think about
- Why does the hottest tier of the cache need to live in-process rather than in Redis?
- Where is the authoritative price actually enforced, and why is display staleness acceptable?
- What stops each 1-second expiry from stampeding the pricing service?