Module 05 · Lesson 02

Five cache strategies and the bugs each one has

Cache-aside is the default; write-through, write-behind, read-through and refresh-ahead exist for specific shapes.

Intermediate 8 min read Updated 2026-11-01

What you will be able to do

  • Distinguish cache-aside from read-through and write-through
  • Explain the race that makes cache-aside write-then-delete fragile
  • Choose a strategy for read-heavy, write-heavy, and write-then-read systems
  • Describe a stampede and how to prevent it

Cache-aside (lazy loading)

Read
Look in the cache. On a hit, return it. On a miss, read the database, write the value into the cache with a TTL, and return it.
Write
Write to the database, then delete the cache key. Do not update the key unless you can do it atomically.
Why delete
Two concurrent writers can interleave so that the older value is written to the cache last. Deletion makes the next reader reload from the source of truth.

Cache-aside is the default because failures are contained: if Redis is down, reads go to the database and the site is slow but correct. It also caches only what is actually requested, so memory goes to the working set.

The read-modify-write race
Reader misses, fetches old value. Writer commits new value and deletes the key. Reader now writes the old value into the cache. The key is stale with a fresh TTL. Mitigate with a short TTL, a version check (only cache if the version still matches), or double-delete after a small delay.

The other four

StrategyWrite pathRead pathCost
Cache-asideDB write, then delete keyCache, else DB then fillFirst reader after a write pays a miss
Read-throughSame as asideLibrary loads on miss automaticallyHides the miss; still lazy
Write-throughWrite cache and DB togetherAlways a hit after writeEvery write pays cache latency; cache holds data nobody reads
Write-behindWrite cache, flush to DB laterFast, always warmData loss on crash; ordering and retry complexity
Refresh-aheadTTL plus background refresh before expiryRarely missesExtra work on keys that may not be read again

Write-behind is the one that needs a warning. It turns the cache into a write buffer, so a crash can lose everything not yet flushed. It is defensible for counters and metrics where a few lost increments are acceptable; it is not defensible for orders.

Stampedes and hot keys

Three failure shapes recur, and naming them is what separates a memorised answer from an engineering one.

  • Stampede — a popular key expires, thousands of requests miss simultaneously and hammer the database. Fix: a per-key mutex so one loader runs, the rest wait; or serve slightly stale data while refreshing.
  • Hot key — one key (a celebrity, a flash sale) absorbs most traffic and exceeds a single Redis node's capacity. Fix: replicate the key across nodes with a random suffix, or cache it in-process at the edge of each service.
  • Cache penetration — requests for keys that do not exist always miss and hit the database. Fix: cache the negative result briefly, or use a bloom filter to reject impossible keys.
get(key):
  value = cache.get(key)
  if value: return value
  if not mutex.acquire(key, ttl=2s):
      return stale_copy(key) or db_read_with_timeout(key)
  try:
      value = db.read(key)
      cache.set(key, value, ttl=300 + rand(0,60))   # jitter
then
  return value
  finally: mutex.release(key)

TTL jitter matters more than it looks: if a deploy warms a thousand keys with the same TTL, they all expire in the same second an hour later. Randomising the TTL spreads the reload.

Say the strategy, then the bug
"I would use cache-aside with a five minute TTL and delete on write. The risk is a stale read after a concurrent write, so I would bound it with a short TTL and a version check rather than an update-in-place." Naming the failure before being asked is the senior signal.

Worth remembering

  • Cache-aside: application reads cache, loads on miss, and writes the database then invalidates.
  • Write-through keeps the cache current at the cost of every write paying cache latency.
  • Write-behind is fast and risky: a crash loses writes that were only in memory.
  • Delete on write rather than update; concurrent updates leave stale values behind.

Checkpoint

Answer all 3 questions to finish the lesson.
1. In cache-aside, why delete the cache key on a write instead of updating it with the new value?
Updating introduces a race in which a slow writer overwrites a newer value. Deleting forces the next read through the database, which is authoritative.
2. Which strategy risks losing acknowledged writes on a crash?
Write-behind acknowledges before the database is updated, so unflushed writes exist only in the cache and disappear with it.
3. A single celebrity profile key is overwhelming one cache node. Best fix?
Hot-key pressure is a distribution problem. Copying the value across nodes, or caching it locally, splits the traffic without changing correctness.