This is an old revision of the document!
Table of Contents
Cache Invalidation Patterns in Real Production Systems
Overview
Cache invalidation ensures cached data stays consistent with the database in distributed systems.
In real production systems, the hardest problem is:
Keeping cache correct under concurrency and distributed timing issues.
Most systems use hybrid strategies, not a single pattern.
1. Cache-Aside (Most Common Pattern)
Concept
Application manages cache manually. Cache is disposable; DB is source of truth.
Read Flow
Client → Application → Cache ↓ miss Database → Cache → Response
Write Flow
Client → Application → Database update → delete cache key
Where it is used
E-commerce systems Microservices APIs Social platforms
Pros
Simple Safe Easy to debug
Cons
First read after miss is slow Small stale window possible 2. Write-Through Cache
Concept
Write goes through cache first, then DB.
Flow
Client → Cache → Database
Pros
Strong consistency No stale cache
Cons
Slower writes Higher cost 3. Write-Behind Cache
Concept
Write goes to cache first, DB updated asynchronously.
Flow
Client → Cache (ACK) ↓ async Database
Pros
Very fast writes High throughput
Cons
Risk of data loss Complex recovery 4. TTL-Based Invalidation
Concept
Cache expires automatically after time.
Example
user:123 → TTL = 5 minutes
Pros
Simple Safe fallback mechanism
Cons
Stale data until expiry 5. Event-Driven Invalidation
Concept
DB changes trigger events that invalidate cache.
Flow
Service → Database update → Event (UserUpdated) ↓ Cache Service → delete/update cache
Pros
Decoupled architecture Scales well
Cons
Eventual consistency Event delay or loss possible 6. Critical Concept: Update-on-write vs Invalidate-on-write (Concurrency) 🟥 6.1 Update-on-write (RISKY in concurrency)
Problem
When multiple requests update same data concurrently, cache can become inconsistent due to race conditions.
Scenario
Request A updates value = “A” Request B updates value = “B”
Sequence Diagram
Client A Go API DB Redis | | | | |---update A--->| | | | |---UPDATE--->| | | |<--OK--------| | | |---SET A------------------->| | | | | Client B Go API DB Redis | | | | |---update B--->| | | | |---UPDATE--->| | | |<--OK--------| | | |---SET B------------------->|
Problem
If timing is reversed:
DB: A → B (correct) Redis: A overwrites B (wrong)
Result:
DB = “B” (correct) Cache = “A” (stale ❌)
Conclusion
Update-on-write is unsafe because:
Race condition Out-of-order execution Distributed timing inconsistency 🟩 6.2 Invalidate-on-write (SAFE approach)
Concept
Do NOT update cache. Only delete it.
Sequence Diagram
Client A Go API DB Redis | | | | |---update A--->| | | | |---UPDATE--->| | | |<--OK--------| | | |---DEL cache--------------->| | | | | Client B Go API DB Redis | | | | |---update B--->| | | | |---UPDATE--->| | | |<--OK--------| | | |---DEL cache--------------->|
Why it is safe
Even if requests are out of order:
Cache only gets deleted multiple times No stale value is written into cache
Read Flow
Client → Redis (miss) → DB → set cache
Always consistent with DB.
7. Comparison Table Aspect Update-on-write Invalidate-on-write Cache operation SET DELETE Race condition risk ❌ High ✅ Low Out-of-order issue ❌ Dangerous ❌ Safe Debug complexity Hard Easy Consistency Fragile Reliable 8. Key Insight
Cache is not the source of truth.
DB = truth Cache = disposable optimization layer
Invalidate-on-write works because:
“Even if cache is wrong, it gets deleted anyway”
9. Production Pattern (Real Systems)
Most real systems use:
WRITE: 1. Update DB 2. Delete Redis key READ: Cache hit → return Cache miss → DB → set cache
10. Why update-on-write still exists
Used only when:
Simple data (low concurrency) Need ultra-fast read-after-write No complex cache relationships
Examples:
Session storage Feature flags Simple counters
