~learn.
?

Preview9 minIntro+60 XP

Caching — Beyond "Just Add Redis".

2 code

watch it first · newThe whole lesson in a 9-minute animated lecture2 quiz checkpoints · ask the AI about any moment · captions, chapters and speed

Intro · 2 min

What's Coming in Week 4#

Last week, you mastered messaging and async processing. You learned how to reliably move data between services, handle failures gracefully, and maintain audit trails.

This week, we tackle the most deceptively simple topic in distributed systems: caching.

THE CACHING PARADOX

Everyone knows: "Just add Redis, it'll be fast!"

Reality:

"There are only two hard things in Computer Science: cache invalidation and naming things."

— Phil Karlton

What makes caching hard
  • When do you invalidate? Too early = wasted cache. Too late = stale data.
  • What do you cache? Everything = memory explosion. Nothing = no benefit.
  • How do you handle cache miss storms? 1 miss = 1 DB hit. 10K misses = DB crash.
  • How do you stay consistent? User updates profile, sees old data. Confused.

Week 4 teaches you to cache strategically, not reflexively.

Cache-aside · hits come from memory, a miss refills from the database, and a herd of misses coalesces into one query
LIVECache-aside · hits come from memory, a miss refills from the database, and a herd of misses coalesces into one query
ExplainerThe whole lesson: Week 4, caching beyond "just add Redis"

A cache hit is fast, but caching is hard: when to invalidate, what to cache, and how 10,000 simultaneous misses can crash a database. The week walks one request path through cache-aside patterns, invalidation ($99 cached while the database says $79), the thundering herd, push vs pull feeds and the CDN → gateway → Redis → database tiers. It ends with Week 3's flow: a price update goes through the outbox and Kafka to an invalidation consumer that deletes the Redis key and purges the CDN.

Weekly Goal#

Cache strategically, not reflexively. Understand invalidation, consistency, and thundering herds.

By the end of this week, you'll be able to:

  • Choose the right caching pattern for any scenario
  • Design invalidation strategies that actually work
  • Prevent thundering herd without complex locking
  • Build multi-tier caching architectures
  • Know when NOT to cache (sometimes the answer is don't!)
Quick check2 questions · +5 XP each

Why caching is hard

  1. Q1/2 You give product prices a very long TTL to maximize the hit ratio. According to the intro, what's the trade-off?

    Answer & explanation

    Answer: C

    Invalidation timing cuts both ways: too early wastes the cache, too late serves stale data. A long TTL on changing data leans hard toward "too late." Memory blow-up comes from caching everything, not from how long each entry lives.

  2. Q2/2 A very popular cache entry expires and 10,000 requests for it arrive at the same moment. What does the intro warn will happen?

    Answer & explanation

    Answer: A

    Without protection, one miss is one database hit, so 10,000 simultaneous misses are 10,000 database hits: a cache miss storm (thundering herd). Having only one request fetch while the rest wait is a protection you have to build; Day 3 covers how.

The Week at a Glance#

WEEK 4: CACHING — BEYOND "JUST ADD REDIS"
  1. Day 1: Caching Patterns
    • Cache-aside vs Read-through vs Write-through vs Write-behind
    • When to use each? What are the trade-offs?
    • System: E-commerce product pages
  2. Day 2: Invalidation Strategies
    • TTL-based vs Event-driven vs Versioned keys
    • The hardest problem in CS — how to actually solve it
    • System: Product catalog with inventory + pricing
  3. Day 3: Thundering Herd
    • What causes it? Locking, probabilistic expiration, coalescing
    • Protecting your database from cache miss storms
    • System: Homepage with 50K requests/second
  4. Day 4: Feed Caching
    • Cache-per-user vs shared cache
    • Push-on-write vs Pull-on-read — the celebrity problem
    • System: Social media feed for 10M users
  5. Day 5: Multi-Tier Caching
    • CDN → API Gateway → App → Database
    • What belongs at each layer? How to invalidate across tiers?
    • System: API serving mobile + web, auth + anonymous
  6. Capstone: Complete Caching Architecture
    • Design caching for a high-traffic e-commerce platform
    • Apply all 5 days to build a production-ready solution

Key Concepts You'll Master · 7 min

Key Concepts You'll Master#

Day 1: Caching Patterns#

DiagramThe four caching patterns
%% title: The four caching patterns
flowchart TD
  subgraph CA["Cache-aside (Lazy Loading)"]
    direction TB
    A1["Application"]:::core --> C1@{ icon: "sd:cache", label: "Cache" }
    C1 -->|"Hit?"| R1(["Return"]):::ok
    A1 -->|"Miss?"| D1@{ icon: "sd:database", label: "Database" }
    D1 --> W1["Write to Cache"]:::cache
    W1 --> R1b(["Return"]):::ok
  end
  subgraph RT["Read-through"]
    direction TB
    A2["Application"]:::core --> C2@{ icon: "sd:cache", label: "Cache" }
    C2 -->|"Hit?"| R2(["Return"]):::ok
    C2 -->|"Miss? Cache fetches from DB"| D2@{ icon: "sd:database", label: "Database" }
  end
  subgraph WT["Write-through"]
    direction TB
    A3["Application"]:::core --> C3@{ icon: "sd:cache", label: "Cache" }
    C3 -->|"Write to DB synchronously"| D3@{ icon: "sd:database", label: "Database" }
    C3 -->|"Return after both complete"| R3(["Return"]):::ok
  end
  subgraph WB["Write-behind (Write-Back)"]
    direction TB
    A4["Application"]:::core --> C4@{ icon: "sd:cache", label: "Cache" }
    C4 -->|"Return immediately"| R4(["Return"]):::ok
    C4 -.->|"Async write to DB later"| D4@{ icon: "sd:database", label: "Database" }
  end
  CA ~~~ RT
  RT ~~~ WT
  WT ~~~ WB
  class C1,C2,C3,C4 cache
  class D1,D2,D3,D4 store
  • Cache-aside (Lazy Loading)
    • Pros: Only caches what's needed, cache failure doesn't break app
    • Cons: First request always slow, potential inconsistency
    • Use when: Read-heavy, can tolerate stale data
  • Read-through
    • Pros: Simpler app logic, cache handles loading
    • Cons: Cache becomes a dependency
    • Use when: Want to simplify application code
  • Write-through
    • Pros: Cache always consistent with DB
    • Cons: Write latency includes cache + DB
    • Use when: Consistency is critical
  • Write-behind (Write-Back)
    • Pros: Lowest write latency, can batch writes
    • Cons: Data loss risk if cache fails before DB write
    • Use when: Performance critical, can accept some data loss risk

Day 2: Invalidation Strategies#

THE INVALIDATION PROBLEM
Sale starts Wait, flip it: sale ended
Cache shows $99 $79 (old sale price)
Database updates to $79 (sale!) now $99 (sale ended)
Customer expects $99 $79
Actually charged $79 $99
Outcome Customer confused (but happy?) Customer angry, support ticket, refund
INVALIDATION STRATEGIES

1. TIME-TO-LIVE (TTL) — cache.set("product:123", data, ttl=300) # 5 minutes

  • ok — Simple to implement
  • ok — Self-healing (bad data expires)
  • no — Stale for up to TTL duration
  • no — All items expire at same rate (inventory vs descriptions)

2. EVENT-DRIVEN — When product updated → Publish event → Invalidate cache

  • ok — Near real-time consistency
  • ok — Only invalidate what changed
  • no — Requires event infrastructure
  • no — What if event is lost?

3. VERSIONED KEYS — cache.set(f"product:123:v{version}", data)

  • ok — Old and new can coexist
  • ok — Atomic switchover
  • no — More complex key management
  • no — Storage overhead

4. HYBRID (Reality) — Event-driven + short TTL as safety net. Best of both worlds.

Day 3: Thundering Herd#

THE THUNDERING HERD PROBLEM
  • Normal operation: Cache has homepage data · 50,000 requests/second served from cache · Database: relaxing, 0 load
  • Cache expires: All 50,000 requests simultaneously hit database · Database: "I'm in danger" · Response time: 10ms → 10,000ms → timeout · Users: "Site is down!"

This is the THUNDERING HERD.

SOLUTIONS

1. MUTEX/LOCKING — First request: Acquires lock, fetches from DB, updates cache. Other requests: Wait for lock, then read from cache. Problem: All requests waiting → high latency

2. PROBABILISTIC EARLY EXPIRATION — Don't expire at exact time. Random chance of refresh before expiration. Some requests refresh early, preventing stampede.

python3 lines
remaining = expires_at - now()          # seconds until expiry
if random() < 1.0 / max(remaining, 1):  # more likely as expiry nears
    refresh_cache()

3. REQUEST COALESCING — Multiple requests for same key → single DB query. All waiters get the same result.

pythonget_with_coalesce · 11 lines
async def get_with_coalesce(key):
    if key in pending_requests:
        return await pending_requests[key]

    # A Task (unlike a bare coroutine) can be awaited by many callers
    task = asyncio.create_task(fetch_from_db(key))
    pending_requests[key] = task
    try:
        return await task
    finally:
        del pending_requests[key]

4. BACKGROUND REFRESH — Never let cache expire. Background job refreshes before TTL. Cache always warm.

Day 4: Feed Caching#

THE FEED PROBLEM

10 million users. Each has personalized feed. User posts → appears in all followers' feeds.

APPROACH 1: PULL ON READ (Compute on request) APPROACH 2: PUSH ON WRITE (Pre-compute feeds)
Steps User opens app: 1. Get user's following list · 2. Fetch recent posts from each followed user · 3. Merge, sort, return User posts: 1. Get user's followers list · 2. Push post to each follower's feed cache
Pros Storage efficient · Always fresh Fast reads (just fetch cached feed) · Predictable read latency
Cons Slow for users following many accounts · High compute at read time Write amplification (1 post → 1M cache writes for celebrities) · Storage heavy
THE CELEBRITY PROBLEM

A celebrity posts → 300 million followers → Push to 300 million feed caches? → That's 300 million writes for 1 post!

SOLUTION: HYBRID
  • Regular users (< 10K followers): Push on write
  • Celebrities (> 10K followers): Pull on read

When building feed:

  1. Fetch pre-computed feed (pushed posts)
  2. Merge with celebrity posts (pulled on demand)
  3. Return combined feed

Day 5: Multi-Tier Caching#

DiagramCaching layers
%% title: Caching layers
flowchart TD
  U@{ icon: "sd:user", label: "User" } --> CDN@{ icon: "sd:cdn", label: "CDN (Edge)<br/>TTL: 60s, Static assets, Public API" }
  CDN -->|"Cache miss"| GW@{ icon: "sd:gateway", label: "API Gateway<br/>Rate limiting, Auth cache" }
  GW --> R@{ icon: "tech:redis", label: "Application Cache (Redis)<br/>TTL: 5-60min, Business objects" }
  R -->|"Cache miss"| QC@{ icon: "sd:cache", label: "Database Cache<br/>Buffer pool: hot pages in memory" }
  QC -->|"Cache miss"| DB@{ icon: "sd:database", label: "Database<br/>The source of truth" }
  class U client
  class CDN,GW edge
  class R,QC cache
  class DB store
WHAT GOES WHERE?
Layer Cache here Don't
CDN Static assets (images, CSS, JS) · Public API responses (product listings) Personalized content · Authenticated endpoints (be careful!)
API Gateway Auth token validation · Rate limit counters Business data
Application (Redis) Session data · User profiles · Product details · Computed aggregates —
Database Buffer pool (MySQL 8.0 removed its query cache; PostgreSQL has none) Rely on this alone

Systems You'll Design#

System 1: Product Catalog Cache (Days 1-2)#

E-COMMERCE PRODUCT CACHING
Challenge
  • 1 million products
  • 100,000 requests/second
  • Prices change daily
  • Inventory changes every second
  • Users must see accurate stock
Key decisions
  • Different TTLs for different data?
  • Event-driven for inventory?
  • How to handle flash sales?

System 2: Homepage Cache (Day 3)#

HIGH-TRAFFIC HOMEPAGE
Challenge
  • 50,000 requests/second
  • Personalized for some users
  • Changes hourly (featured products)
  • Cache miss = database overwhelmed
Key decisions
  • How to prevent thundering herd?
  • Cache per user or shared?
  • Background refresh strategy?

System 3: Social Feed Cache (Day 4)#

PERSONALIZED FEED SYSTEM
Challenge
  • 10 million users
  • Each follows 0-1000 accounts
  • Some accounts have millions of followers
  • Feed must feel "real-time"
Key decisions
  • Push vs pull vs hybrid?
  • How to handle celebrities?
  • Cache per user or compute on read?

System 4: Multi-Tier API Cache (Day 5)#

COMPLETE API CACHING STRATEGY
Challenge
  • Mobile + Web clients
  • Authenticated + Anonymous users
  • CDN in front
  • Multiple data types with different freshness needs
Key decisions
  • What to cache at CDN vs App?
  • How to handle authenticated caching?
  • Cache invalidation across all tiers?

How Week 3 Connects to Week 4#

BUILDING ON WEEK 3 CONCEPTS
Week 3: Messaging Week 4: Caching
Transactional Outbox (Guaranteed delivery) → Event-driven invalidation (Cache updates via events)
Backpressure (Handle overload) → Thundering herd protection (Prevent cache miss storms)
Dead Letter Queue (Handle failures) → Cache failure handling (What if Redis goes down?)
Kafka Consumer Groups (Multiple subscribers) → Cache invalidation consumers (Services listening for changes)
EXAMPLE: Product Update Flow
  1. Admin updates product price (Week 2: Idempotency)
  2. Update written to DB + Outbox (Week 3: Transactional Outbox)
  3. Event published to Kafka (Week 3: Queue vs Stream)
  4. Cache Invalidation Consumer receives event (Week 3: Consumer Groups)
  5. Cache entry invalidated or updated (Week 4: Invalidation)
  6. CDN cache purged via API (Week 4: Multi-tier)
  7. Next request gets fresh data (Week 4: Cache-aside)

Prepare for Week 4#

Pre-Reading (Optional but Helpful)#

  1. Redis Documentation — Commands: GET, SET, SETEX, DEL, KEYS https://redis.io/commands/

  2. CDN Basics — How edge caching works Cloudflare or AWS CloudFront documentation

  3. Phil Karlton's Quote — Why cache invalidation is hard https://martinfowler.com/bliki/TwoHardThings.html

Questions to Ponder#

Before starting Week 4, think about:

  1. In your current systems, what's cached?

    • How do you know when to invalidate?
    • Have you ever served stale data to users?
  2. What would happen if your cache went down?

    • Would your database survive the load?
    • How quickly could you recover?
  3. How do you handle cache warming?

    • After a deploy, is the cache cold?
    • How long until performance is normal?

Week 4 Success Metrics#

By the end of Week 4, you should be able to:

By the end of Week 4, you should be able to0/8 ready
  • Explain cache-aside, read-through, write-through, and write-behind patterns
  • Design invalidation strategies for different consistency requirements
  • Prevent thundering herd with at least 3 different techniques
  • Choose between push-on-write and pull-on-read for feed systems
  • Design multi-tier caching with clear responsibilities per tier
  • Know when NOT to cache (and explain why)
  • Estimate cache hit ratios and memory requirements
  • Handle cache failures gracefully

A Taste of What's Coming#

Here's a problem you'll solve in Day 3:

THE BLACK FRIDAY PROBLEM

It's 12:00:00 AM on Black Friday. Your homepage cache TTL is 60 seconds. You have 100,000 users waiting to refresh.

  • 12:00:00 — the cache expires.
  • 12:00:01 — 100,000 requests hit your database.
  • 12:00:02 — your database is on fire.

How do you prevent this? Day 3 will teach you:

  1. Probabilistic early expiration
  2. Request coalescing
  3. Background refresh
  4. Mutex with timeout
  5. Cache warming strategies

You'll implement each one and know when to use which.

Get Ready#

Week 4 is where performance engineering meets reliability engineering. Caching seems simple until you've been paged at 3 AM because a cache stampede took down your database.

After this week, you'll never think "just add Redis" again. You'll think:

  • What's the invalidation strategy?
  • What's the thundering herd protection?
  • What's the failure mode?
  • What's the consistency guarantee?
  • Should we even cache this?

See you in Week 4.


Week 4 Preview — Caching: Beyond "Just Add Redis"

Coming up: Day 1 will start with caching patterns. We'll design a product catalog cache for an e-commerce platform, handling the trade-offs between freshness and performance.

this lesson in shorts · 1

play them all →

One idea from this lesson in about a minute each, made for your phone.

End of Preview · Caching — Beyond "Just Add Redis"
Finish PreviewStages clear as you read them (0/1 so far). Skipping ahead doesn't count: marking complete unlocks after ~2 more min of reading.

New lessons by email

New lessons, side quests and case files as they ship, at most once a week. No spam, unsubscribe anytime.

Discussion · …

Public: everyone reading this lesson sees comments. Be kind and keep it on the material.

Sign in to ask a question or answer one. Reading stays open to everyone.
loading the discussion…