The 10,000-Product Cache Restart That Crashed the Database
A startup cached 10,000 product listings with TTL of exactly 300 seconds. Everything worked perfectly - until a Redis restart cleared the cache. All 10,000 entries were repopulated at the same moment. Exactly 300 seconds later, all 10,000 expired simultaneously. 10,000 database queries hit PostgreSQL in the same second. The database buckled. This happened every 5 minutes until an engineer added TTL jitter. The fix was one line of code: instead of TTL=300, use TTL=random(270, 330).
TTL Tuning Guide
TTL Selection by Data Type:
Data Type TTL Reasoning
-------------------- ---------- --------------------------
Static assets (CDN) 365 days Use content hash for versions
User session 30 min Inactive timeout standard
Product catalog 5-15 min Admin changes are infrequent
User profile 1-5 min Users expect quick updates
Feature flags 30-60 sec Need fast propagation
Inventory count 10-30 sec Must be reasonably fresh
Auth token cache matches JWT Align with token expiry
Analytics dashboard 1-24 hours Not real-time
Golden Rule: match TTL to business tolerance for staleness
'How old can this data be before users notice or care?'
Jitter Formula (prevent synchronized expiration):
ttl = base_ttl + random(-base_ttl*0.1, base_ttl*0.1)
Example: 300 + random(-30, 30) = 270 to 330 seconds
Spreads 10,000 expirations across 60 secondsCache Stampede Prevention
Stampede Prevention Techniques:
Technique How It Works Tradeoff
-------------------- ----------------------- ------------------
Mutex Lock First requester locks, Others wait (latency)
fetches from DB, fills for lock release
cache. Others wait.
Stale-While- Serve expired value Slightly stale data
Revalidate while 1 background for ~100ms
thread refreshes.
Probabilistic Each request has P% Slightly early
Early Refresh chance of refreshing refreshes (wastes
before TTL expires. a few DB queries)
P increases as TTL but prevents stampede
approaches.
Cache Warming Pre-load popular keys Requires knowing
(on restart) before sending traffic. which keys are hot
Query DB for top 1000, May load unused data
populate cache first.
Recommended combo:
1. TTL with jitter (prevent synchronized expiry)
2. Mutex lock on cache miss (prevent stampedes)
3. Cache warming on deploy/restart (prevent cold start)Interview Tip
TTL tuning and stampede prevention show caching maturity. Always mention jitter ('I add +/-10% randomness to TTLs to prevent synchronized expiration'). For stampede prevention, lead with the mutex lock pattern ('on cache miss, the first request acquires a lock, fetches from DB, and fills cache; others wait'). Bonus: mention stale-while-revalidate for read-heavy endpoints where a few milliseconds of staleness is acceptable.
