system-design · beginner

Cache Stampede — When Expiry Melts the Database

Start here

A cache stampede (also called dogpiling or a thundering herd on a cache key) is this failure mode:

A hot piece of data expires (or is missing). Hundreds or thousands of requests arrive at the same moment. Every request misses the cache and independently recomputes or reloads the same value from the database. The origin collapses under duplicate work.

You should care because the cache was supposed to protect the database. A stampede turns a normal expiry into a self-inflicted outage — often right after a deploy, a TTL boundary, or a cache restart.

What you will learn

  1. Define stampede / dogpile in plain English.
  2. Explain hot keys and synchronized expiry.
  3. Walk a timeline of a stampede second by second.
  4. Compare fixes: single-flight, locking, probabilistic early refresh, stale-while-revalidate, background refresh.
  5. See how TTL jitter reduces synchronized expiry.
  6. Work a homepage and product-detail example.
  7. Know metrics and failure modes of the fixes themselves.
  8. Decide when longer TTL alone is not enough.

What you should know first

TopicWhy
Caching 101Hits, misses, TTL
Caching StrategiesCache-aside miss path
Latency vs ThroughputQueues under overload

Words you need before we begin

TermPlain English
Hot keyOne cache key requested far more than others.
MissCache does not have a usable value.
Stampede / dogpileMany concurrent misses regenerate the same value.
Single-flightOnly one loader runs per key; others wait for its result.
Mutex / lockMechanism so only one worker enters a critical section.
Stale-while-revalidateServe slightly old data while refreshing in the background.
TTL jitterRandomize expiry times so keys do not die together.
OriginDatabase or service that produces the real value.
Thundering herdMany waiters wake and rush a resource at once (related idea).

Simple story: the shared answer sheet

A classroom of 200 students may copy the answer to question 1 from the board (cache).

When the teacher erases the board (TTL expiry):

**Where the analogy stops:** software must handle crashes of the “one fetcher” and distributed locks across many servers.

The problem: success that becomes failure

Healthy state

Stampede moment

At second 60.000, the key expires. In the next 50 ms, 5,000 requests arrive (homepage traffic).

Without protection:

  1. 5,000 cache misses
  2. 5,000 identical SQL queries or service calls
  3. Database CPU spikes
  4. Timeouts → retries → even more load
  5. Site appears “randomly down” every minute on the minute
The cache did its job *until* expiry. Expiry without coordination is the bug.

Step-by-step explanation

Step 1 — Why hot keys matter

Not all keys stampede equally. A key hit once per minute can expire safely. A key hit 10,000 times per second will amplify a miss into a crisis.

Examples of hot keys:

Step 2 — Synchronized expiry makes it worse

If thousands of keys share TTL = 3600 and were warmed together at deploy time, they may expire together → many stampedes or one giant origin spike.

Jitter: set TTL to 3600 + random(0..120) so expiries spread out.

Step 3 — Cold cache is a stampede cousin

After Redis restart or FLUSHALL:

Mitigations: gradual traffic shift, pre-warm critical keys, serve degraded modes, rate limits.

Step 4 — Fix A: single-flight (request coalescing)

Idea: for each key, only one in-flight load is allowed.

  1. Miss detected.
  2. Try to acquire “load lock” for key.
  3. Winner loads origin, fills cache, releases lock.
  4. Losers wait (or poll) and then read the filled cache (or share the winner’s result in-process).
In one process, an in-memory map of `CompletableFuture` / promises works. Across many app instances, you need a **distributed lock** (Redis `SET NX PX`, etc.) or accept per-instance single-flight (still multiplies by instance count).

Step 5 — Fix B: stale-while-revalidate

Store value + soft expiry + hard expiry:

Users keep low latency; origin sees fewer synchronized hard misses.

HTTP CDNs use related ideas (stale-while-revalidate cache directives).

Step 6 — Fix C: probabilistic early recomputation

Before expiry, each request has a small probability to refresh early:

No central lock required; works well for very hot keys with careful tuning.

Step 7 — Fix D: background warmers

A worker refreshes known hot keys on a schedule before TTL death.

Pros: predictable.
Cons: must know the hot set; wasteful if traffic disappears; still need jitter.

Step 8 — Fix E: longer TTL alone

Longer TTL reduces stampede frequency but:

Use TTL as one lever, not the only defense.

Step 9 — Locks can stampede too

If 5,000 threads wait on one lock and all wake when it releases, you can get a thundering herd on the lock itself. Good designs:

Visual mental model

Stampede timeline

flowchart TB
  H[Hot key expires] --> M[Many concurrent misses]
  M --> D[Duplicate origin loads]
  D --> O[Origin overload]
  O --> T[Timeouts and retries]
  T --> O

Learning question: Where should you collapse duplicate work?

Caption: Many misses must become one load.

Single-flight

flowchart LR
  R1[Request 1] --> L{Lock key}
  R2[Request 2] --> L
  R3[Request 3] --> L
  L -->|winner| DB[(Origin load)]
  DB --> C[(Fill cache)]
  C --> All[All requests get value]

Learning question: What happens if the winner crashes mid-load?

Caption: Locks need timeouts so waiters are not stuck forever.

Complete worked example: viral product page

Setup

Without protection

Every 30 seconds: ~8,000 identical rebuilds → DB connection pool exhausts → checkout also fails (shared pool).

With protection

  1. TTL jitter 30–45 seconds.
  2. Single-flight per instance + Redis lock for cross-instance.
  3. Soft TTL at 20s: serve stale until 45s while one refresher runs.
  4. Alert if origin builds for product:42 exceed N/second.

Outcome

Origin builds drop to roughly one per refresh window; p99 stays flat through the viral spike.

How it works in production

Building blocks

Metrics

Ownership

When a stampede hits, know:

Failure modes

ModeRiskMitigation
Lock never releasedStuck missesLock TTL; safe release
All instances stampede (no distributed lock)N× loadsShared lock or edge cache
Serving forever-staleWrong dataHard max age
Warmer stormsSelf-DDoSRate limit warmers
Retry storms after timeoutAmplificationBudgeted retries + jitter

Trade-offs

TechniqueBenefitCost
Single-flightCollapse duplicate workLock complexity
Stale-while-revalidateSmooth UXTemporary wrongness
Early probabilistic refreshNo central lockExtra refresh work
Background warmerPredictableMust track hot set
Longer TTLFewer expiriesStaleness

Compare with related concepts

TermRelation
Thundering herdGeneral herd wake-up; stampede is herd on cache miss
Hot partitionUneven load on one shard; may coexist with hot keys
Retry stormAmplifies failures after timeouts
Cold startEmpty cache; stampede-like global miss

Common misunderstandings

  1. “Just increase TTL to one day.”
Hides frequency; fails on flush/invalidation; may be too stale.
  1. “Stampede only happens at big tech scale.”
A small app with one homepage key and a traffic spike can stampede a small DB.
  1. “Locks make everything slow.”
One load is faster than one thousand loads.
  1. “CDN means no stampede.”
Origin fetch on edge miss can still dogpile without coalescing.
  1. “Single-flight fixes correctness bugs.”
It fixes duplicate work, not wrong invalidation logic.

Check your understanding

Many concurrent misses reload the same hot key from origin

The cache runs out of disk sectors only

DNS TTL becomes infinite automatically

HTTPS is disabled

It spreads expiry times so fewer keys die at once

It encrypts cache values

It replaces all load balancers

It deletes the need for a database

Practice

Your news homepage key expires every 15 seconds. Traffic is 2,000 rps. Rebuild takes 100 ms and hits 12 queries.

  1. Estimate worst-case origin rebuilds per expiry without protection.
  2. Design single-flight across 20 app instances.
  3. Add soft/hard expiry numbers.
  4. List three metrics for a dashboard panel “stampede risk.”
  5. Decide: serve 5-second-stale homepage during refresh, or show an error page?

Revision summary

  1. Stampede = concurrent hot misses × expensive rebuild.
  2. Synchronized TTL and cold cache make it worse.
  3. Collapse duplicate loads (single-flight).
  4. Prefer serving stale briefly over melting origin.
  5. Jitter TTLs; warm carefully.
  6. Measure concurrent rebuilds per key, not only hit ratio.

Glossary

TermDefinitionExample
Cache stampedeConcurrent miss herd on one keyHomepage expiry
DogpileSynonym for stampedeSame event
Hot keyExtremely popular keyproduct:42
Single-flightOne loader per keyLock + share result
Soft expiryAge when refresh should start20s of 30s TTL
Hard expiryAge when value must not be used45s max
JitterRandomized delay/TTL±10% TTL
OriginBacking store/servicePrimary DB
CoalescingMerging duplicate workOne SQL not 5k
WarmerBackground refresh jobCron top-100 keys

Abbreviations and terminology

ShortFull / note
TTLTime To Live
rpsRequests per second
NX“Not exists” lock pattern (SET NX)
SWRStale-While-Revalidate
CDNContent Delivery Network
OOMOut Of Memory

What to learn next

Primary next lesson: Stale Cache After Write — Read-Your-Writes Failures

Also: CDN for edge-level caching and coalescing behavior.

Track: Data, Storage and Messaging

Previous: Cache Eviction Policies — LRU, LFU, TTL, and Friends

Next: Caching 101 — Memory Offloading and Latency Reduction

Series: Caching

  1. Caching 101 — Memory Offloading and Latency Reduction
  2. Caching Strategies — Aside, Through, Behind, and Refresh-Ahead
  3. Cache Eviction Policies — LRU, LFU, TTL, and Friends
  4. Distributed Caching — Sharding and High-Availability Clusters
  5. Cache Stampede — When Expiry Melts the Database (this guide)
  6. Stale Cache After Write — When Your Own Update Disappears
  7. Content Delivery Networks (CDN) — Edge Acceleration and Caching
  8. Bloom Filters — Probabilistic Set Membership at Scale

All series

By Shubham Jain

All articles · Study paths

Shubham Jain · Learning Lab