In revision.
Crisp5 min readGo deeper →

Caching strategies

Cache-aside, write-through, write-back, and refresh-ahead - pick based on read/write ratio and staleness tolerance.

"There are only two hard things in computer science: cache invalidation and naming things." Phil Karlton was right. The cache itself is trivial. Getting it right is hard.

The four patterns

Cache-aside (lazy loading). App reads cache. Miss = read DB, populate cache. Write = update DB, invalidate or update cache. This is the default. Use it unless you have a reason.

Write-through. App writes go to cache, cache writes to DB synchronously. Reads from cache. Consistent. Slower writes.

Write-back (write-behind). App writes to cache, cache writes to DB asynchronously. Fast writes. Risk: cache crash loses unflushed writes.

Refresh-ahead. Cache refreshes hot entries before TTL expires. Smooth, no stampede. Used by CDNs (stale-while-revalidate).

Cache-aside on a miss: fetch from DB, populate cache, return

Invalidation: pick one model

  • TTL only. Easy. Stale by default for up to TTL seconds. Use for data where staleness is acceptable.
  • Explicit invalidation on write. App deletes the cache key when it writes to DB. Tight consistency. Brittle if you forget to invalidate from every write path.
  • Event-driven (CDC, Debezium). DB change feed invalidates the cache. Robust. More moving parts.

The TTL+explicit hybrid is what most teams ship. TTL as a backstop, explicit invalidation for the hot paths.

The two famous failure modes

Thundering herd. TTL expires, 1000 requests miss simultaneously, all hit the DB. Solutions: locking around the recompute, jittered TTLs, refresh-ahead.

Cache stampede on cold start. Empty cache, full traffic. Pre-warm critical keys before going live.

What to cache

  • Read-heavy, infrequently changed. Product catalog, user profile.
  • Expensive to compute. Aggregations, recommendations.
  • Read by many. Feature flags, config.

Don't cache:

  • Write-heavy. Cache churn defeats the purpose.
  • Per-request unique. Auth checks for one user one time.
  • Critical-correctness reads. Account balances at withdrawal time.

Cache layers

Browser -> CDN -> API gateway -> App in-memory -> Redis -> DB query cache -> DB.

Each layer catches a different request type. CDN catches static assets. Redis catches expensive queries. App in-memory catches per-pod hot keys (sub-microsecond).

My defaults

Cache-aside in Redis with 5 minute TTL plus explicit invalidation on writes. Hot per-pod data in an in-memory LRU with 30 second TTL. CDN for anything public and static. Always measure hit rate. A cache with <80% hit rate is mostly cost.

Learn more