Stampedes Hot Keys and Cold Cache Recovery

Caching and Failure Recovery · 45 min

System Design · Module 6 · Lesson 4 of 4 · 45 min

Stampedes Hot Keys and Cold Cache Recovery

Protect the origin when many requests miss together, distinguish hot keys from uneven key counts, and plan a bounded cache restart.

The cache failure path needs admission and concurrency limits so a missing copy cannot create unbounded origin work.

01 The idea

The problem this lesson solves

A popular cache entry expires and five hundred requests miss together. If each performs the same expensive database query, the cache has converted one refresh into a burst of duplicate work. After a full restart, many different keys may miss instead. These are related but distinct recovery problems, and they need more than a longer TTL.

You should recognize the application, the database and the request path. Treat the database as the authority unless explicitly stated otherwise.

Single-flightCoalescing concurrent loads for one key into one shared rebuild.
Hot keyA key that receives enough traffic to dominate its serving owner.
TTL jitterVariation in lifetimes that spreads expiration events across different keys.

02 Worked example

Protect the origin during 500 simultaneous misses

Stampedes Hot Keys and Cold Cache Recovery worked diagram01Many missesSame or distinct keys02CoalesceOne loader per key03Origin budgetBound concurrent work04RecoveryWarm + observeStampedes Hot Keys and Cold Cache Recovery compact diagram01Many missesSame or distinct keys02CoalesceOne loader per key03Origin budgetBound concurrent work04RecoveryWarm + observe
Coalescing combines misses for one key. A separate origin concurrency budget is needed when many different keys miss together.
1
Expire one keyFive hundred requests arrive for a missing popular entry.
2
Coalesce duplicatesOne loader fetches while the other requests follow a bounded wait policy.
3
Bound the originA separate concurrency limit protects against many distinct missing keys.
4
Warm graduallyIncrease admitted traffic while observing origin latency, errors and saturation.

03 The method

Understand the mechanism

Identify same-key stampedes and broad cold starts

A stampede occurs when many callers concurrently rebuild the same missing or expired value. The repeated origin work is largely redundant. A cold start across many distinct keys can overwhelm the origin even if every key has only one loader.

Suppose 500 callers request a7 during a 100-ms rebuild. Basic cache-aside can issue up to 500 origin loads if they all observe the miss before a fill becomes visible. A same-key coordination mechanism can reduce that to one active rebuild within its coordination scope.

Now suppose 500 callers request 500 different keys. One loader per key still means 500 origin loads. Protecting the origin therefore requires a global or appropriately partitioned concurrency/admission budget in addition to same-key deduplication.

Measure which pattern you have. Hot-key request concentration, synchronized expiry and a namespace-version rollout produce different miss distributions. The remedy should address the distribution instead of assuming every miss storm is identical.

Coalesce work with a bounded single-flight mechanism

Single-flight associates an in-progress load with a key. The first caller becomes the loader; later callers join its result rather than starting duplicate work. On completion, the result is delivered to waiters and eligible data is cached.

Bound the wait. If the loader hangs, hundreds of callers must not wait forever or retain unbounded memory. Use a deadline, cancellation policy and documented fallback. Remove or transition the in-progress record reliably when the loader fails.

An in-process single-flight map only coordinates callers within that process. With ten app instances, the origin may still receive ten simultaneous rebuilds for the same key. That can be acceptable under the budget, or it can motivate shared coordination.

Shared coordination adds failure questions. A lease can expire while a slow worker is still running, allowing another loader to begin. Ownership-aware release prevents an old worker from deleting a successor’s lock, but preventing stale side effects may require fencing or conditional version checks at the resource being written.

Protect against distinct-key overload

Use a bounded semaphore, queue or admission mechanism around origin rebuild work. If the origin can safely handle only a certain number of concurrent expensive loads, the cache layer should not exceed it merely because keys differ.

The budget should consider all callers and app instances. A local limit of twenty on each of fifty instances permits one thousand concurrent loads. If the origin’s safe budget is one hundred, those local limits do not establish the intended global protection.

Decide how callers behave when capacity is exhausted. They may wait in a bounded queue, receive a retryable error, or obtain an acceptable stale result. Each choice has latency, availability and freshness consequences. Dropping the limit under pressure defeats its purpose.

Propagate the remaining deadline. Loading a result after every waiting caller has timed out can waste scarce recovery capacity, although some deliberate background warmup may still be useful. Separate that warmup budget from interactive admission.

Spread expiry and rebuild ahead when appropriate

If many entries receive the same TTL during a bulk fill, they can expire together. Adding bounded random jitter to expiration can spread the rebuild load. Jitter changes the expiry distribution, so the resulting lifetime must still fit the freshness policy.

Refresh-ahead starts rebuilding before an entry becomes unusable. It can keep a hot value available, but it consumes origin work and needs coordination so every request does not trigger its own refresh. Refreshing cold entries wastes capacity.

Serving stale while one refresh runs can preserve availability for content that permits it. Apply a maximum stale policy and distinguish load failure from authoritative deletion or revocation. An old authorization result may be unacceptable even when an old article summary is fine.

None of these mechanisms removes the need to test a completely empty cache. A deployment, data loss or key-version change can bypass the steady-state expiry pattern. Recovery capacity needs its own plan.

Handle hot keys without confusing reads and writes

A hot read key can overload one shared-cache owner even when the database is protected by a high hit ratio. Local copies, replicated read serving or an edge cache can spread suitable read work, subject to the freshness contract.

Adding virtual nodes to a sharded cache balances key ranges more finely, but one key still belongs to one logical placement under a simple owner model. Equal key counts do not imply equal request demand.

Hot writes are harder to spread because updates may need a single ordered authority. Sharding a counter into partial counters can distribute increments, but exact reads and reset semantics become a different problem. Do not propose salting keys without explaining how the application combines or locates the resulting values.

Identify the unit of contention: one key’s lookup rate, one large value’s bandwidth, one loader’s compute cost or one ordered update stream. Different units justify different designs.

Make recovery a controlled operating mode

After cache loss, start with a known origin budget and gradually restore admitted load or selected warm keys. Prioritize data with demonstrated value rather than fetching every historical record. Measure whether warmup reduces misses enough to justify its origin cost.

Observe origin concurrency, queue time, timeout rate, rebuild duration, waiter count and cache hit ratio. A rising hit ratio can coexist with an overloaded origin if the remaining misses are expensive. Recovery is successful when user-visible behavior stabilizes within the defined target.

Test loader crashes, expired leases, stale fills and cancellation. The fill must still obey the freshness protocol from the prior lesson; single-flight reduces duplicate work but does not automatically make an old result current.

The final design combines independent protections: per-key coordination for duplicate work, bounded total origin work for broad misses, a deliberate stale-data policy and measured warmup. Each solves a specific part of the recovery problem.

Guided tutorial

  1. Count duplicate rebuilds

    Model 500 simultaneous misses for a7. Without coordination, allow each to start a load. With one shared active rebuild, count one loader and 499 waiters under that scope. Then distribute callers across ten independent local single-flight maps and explain why up to ten loaders may remain.

  2. Change to distinct keys

    Give every caller a different missing key. Show that per-key coalescing no longer lowers the number of loads. Add a total origin-concurrency budget and a bounded queue. Specify what callers receive when both the active budget and queue are full.

  3. Inject a stalled loader

    Pause the first loader until its wait or lease deadline expires. Start a successor. Explain how the old loader avoids releasing the successor’s ownership and how its late result is prevented from violating freshness. Treat these as separate requirements.

  4. Plan a cold-start experiment

    In a disposable environment, empty the cache and replay a representative mix of hot and cold keys. Record origin concurrency, user latency and errors while warming a bounded set of hot entries. Adjust one limit at a time and retain the freshness contract during recovery.

Recovery pseudocode

on miss(key):
  join an existing bounded rebuild for key, if present
  otherwise acquire a safe rebuild responsibility
  enter the bounded origin-concurrency budget
  load and conditionally fill according to freshness policy
  release only the responsibility still owned by this worker

Check your result

Same-key callers join one bounded rebuild. Different keys still compete for a separate origin budget. Specify a timeout or fallback for waiting callers so protection does not create unbounded queues.

04 Cheat sheet

Review the decisions

Use this table to explain the mechanism and its limit without memorizing a product name.

Choice or conceptRememberCheck
Single-flightDuplicate work for one keyWaiters need bounds
TTL jitterSynchronized expiry of different keysNot a same-key lock
Origin concurrency limitTotal rebuild demandNeeds rejection or waiting policy
Hot-key local copyDistributes eligible readsMore freshness coordination

05 Where & why

Check the primary references

Use official references for protocol and implementation details. The questions here are teaching prompts.

Primary reference
Redis lock ownership and expiry considerations

Read Redis lock ownership and expiry considerations

Primary reference
Google SRE: handling overload

Read Google SRE: handling overload

State the implementation version and the workload assumptions when defending your own design.

06 Interview questions

Explain the reasoning out loud

Answer before opening the explanation. The questions progress from vocabulary to decisions and failure behavior.

What does Single-flight mean?
Coalescing concurrent loads for one key into one shared rebuild.
What does Hot key mean?
A key that receives enough traffic to dominate its serving owner.
What does TTL jitter mean?
Variation in lifetimes that spreads expiration events across different keys.
Why is single-flight not enough for every cold cache?
It coalesces duplicates for one key. Many distinct misses can still produce too much total origin work, requiring a separate concurrency and admission budget.
What does TTL jitter fail to solve?
It does not merge callers already missing the same key. It spreads expiry times across different entries.
Why not blindly delete a rebuild lock on completion?
The original lease may have expired and a new owner may hold the key. Release must verify ownership so an old worker cannot remove the new lock.
Does adding shards split a hot key’s reads?
Not under simple single-owner key partitioning. More ranges can balance key counts without dividing one key’s demand.
What should a warming plan monitor?
Origin latency and saturation, cache hit/miss behavior, rebuild concurrency, timeouts and rejected or stale-served requests. Increase traffic only within the tested budget.

07 Practice problems

Practice a new case

Solve the new cases before revealing hints. Complete worked solutions follow the set.

Hit rate

Easy
Compute the hit rate for 18 hits and 6 misses.
Follow-up
Use total lookups as the denominator.
Show the hint
Hits plus misses equals the total.

Control matching

Easy
Match jitter, coalescing and concurrency limits to their main targets.
Show the hint
Different controls reduce different kinds of synchronization.

Average latency

Medium
Estimate mean latency with 80 percent hits, a 1 ms cache lookup and a 30 ms additional database miss cost.
Follow-up
Every miss also paid the cache lookup.
Show the hint
Weight the two complete paths.

Stampede defense

Medium
Choose two controls for 500 concurrent misses on the same key and explain why TTL jitter alone is insufficient.
Follow-up
All requests concern one value.
Show the hint
Separate duplicate work from total origin load.

Expired lease

Medium
Loader A finishes after its lease expires and B acquires a new lease; explain unsafe unconditional release.
Show the hint
Ownership may have changed.

Cold restart plan

Hard
Design cache restart behavior for a database that cannot serve full external read QPS.
Show the hint
A cache miss must not mean unlimited bypass.

Worked solutions

1. Hit rate

18 / (18 + 6) = 0.75, or 75 percent.

2. Control matching

Jitter spreads different-key expiry; coalescing reduces duplicate same-key loads; a concurrency limit bounds total origin work.

3. Average latency

Mean = 0.8 × 1 + 0.2 × 31 = 7 ms under the fixed-cost model. It says nothing by itself about p99 or queueing under load.

4. Stampede defense

Use single-flight/coalescing for one rebuild and an origin concurrency limit or bounded wait to prevent overload. Jitter spreads different keys' expiry times, but these 500 requests already missed one key together.

5. Expired lease

A could delete B’s valid lock. Release must verify A still owns the responsibility, and stale fills need their own freshness checks.

6. Cold restart plan

Use controlled warming, per-key coalescing, a total rebuild budget and bounded admission. Serve eligible bounded stale data or reject according to the contract, while observing database saturation before increasing traffic.