Phil Karlton famously said there are only two hard things in computer science: cache invalidation and naming things. He wasn't wrong. Caching is deceptively simple to add and surprisingly difficult to get right.
The three core strategies you need to know:
- Cache-Aside (Lazy Loading): The application checks the cache first. On a miss, it fetches from the source, writes to the cache, then returns. This is the most common pattern. You only cache what's actually requested, but the first request for any item is always slow.
- Write-Through: Every write goes to both the cache and the data store simultaneously. Reads are always fast, but writes are slower and you cache data that may never be read.
- Write-Behind (Write-Back): Writes go to the cache immediately, and the cache asynchronously flushes to the data store. Fast writes, but you risk data loss if the cache crashes before flushing.
Real-world example: Imagine a product catalog page. You use cache-aside with a 10-minute TTL on product details. Traffic spikes during a sale — 50,000 requests/second hit one product. The cache entry expires, and suddenly hundreds of concurrent requests all miss the cache and slam your database simultaneously. This is a cache stampede. The fix: use a lock so only one request fetches from the database while others wait for the cache to repopulate, or use probabilistic early expiration where items randomly refresh before their TTL actually expires.
Invalidation approaches, ranked by complexity:
- TTL (Time-To-Live): Simplest. Set an expiration. Acceptable staleness depends on your domain — 5 minutes for a product price, 24 hours for a user avatar URL.
- Event-driven invalidation: When data changes, publish an event that deletes or updates the cache entry. More complex but gives near-real-time consistency.
- Version-based: Append a version number or hash to cache keys. When data changes, increment the version. Old entries simply go unused and eventually evict.
Rule of thumb for TTL sizing: Start with TTL = acceptable staleness × 0.5. If your business can tolerate 10 minutes of stale data, set TTL to 5 minutes. This gives you a safety margin for clock drift, delayed invalidation, and the reality that stakeholders always underestimate how stale "acceptable" really feels.
Common mistakes:
- Caching errors or empty results without a short TTL — one bad upstream response poisons your cache for minutes.
- Not monitoring your hit rate. Below 80%, question whether the cache is earning its operational complexity.
- Using a cache to mask a slow query instead of fixing the query. Caching is treatment, not cure.