Caching Strategies
Caching trades memory for speed. The hard part is invalidation — cached stale data is worse than slow fresh data.
1 credit
Where to cache
5 itemsBrowser cache
Free. HTTP headers control it. Best for static assets.CDN edge
Closest to user. Hashed static assets → cache forever. Dynamic pages → short TTL + stale-while-revalidate.Application memory
Fastest but per-instance. Good for small, read-heavy lookups (feature flags, config).Redis / Memcached
Shared across instances. Good for sessions, DB result cache, rate-limit counters.DB materialized views
Precomputed queries. Good for complex aggregations refreshed periodically.Patterns
5 itemsCache-aside
App reads cache → miss → reads DB → writes cache. Most common. App controls TTL + invalidation.Write-through
App writes to both cache and DB. Read is always warm. Writes slower.Write-behind
App writes to cache; cache flushes to DB async. Fast writes, risk of loss.Read-through
App talks to cache only; cache handles DB miss. Simpler app code, harder to tune.Refresh-ahead
Auto-refresh before TTL expires. Avoids spike when popular key expires.Invalidation
- **TTL** — easiest. Stale for max TTL seconds. Good enough for most data.
- **Event-driven** — write-path emits invalidation event. Accurate but requires messaging.
- **Versioned keys** — `user:42:v3`. New version = instant swap, old keys expire naturally.
- **Tag-based** — group keys by tag, invalidate the tag. Your cache layer has to support it (Redis + app logic).
Common bugs
- **Cache stampede** — popular key expires, 1000 requests miss simultaneously, all hit DB. Fix: locking around regenerate, or refresh-ahead.
- **Negative caching** — not caching `null` results = you keep asking the DB for data that doesn't exist. Cache 404s for a shorter TTL.
- **Mismatched TTLs across layers** — browser cache 1 year, CDN 1 hour = old browsers see stale forever after a deploy.
- **Cache key collisions** — don't forget user context. `cart:latest` is great until you leak user A's cart to user B.