Hard20 minDistributed Systems
UpdatedAug 5, 2026
Edit

Preventing cache stampedes

CONCEPTS:Cache Stampede PreventionDistributed Caching

Question Variations

  • "What is the difference between a stampede and a cache avalanche?"
  • "How do you prevent a lock holder failure from blocking all requests?"
  • "When is serving stale data better than waiting for a refresh?"

Why This Is Asked

A hot key expiring can turn one database query into thousands of concurrent origin requests. This question tests whether a candidate can protect the origin with request coalescing, distributed coordination, stale-value strategies, jittered expiry, and safe degraded behavior.

Key Concepts

  • Single-flight: Coalesce concurrent refreshes in one process.
  • Distributed lock: Coordinate refresh across multiple instances with a bounded lease.
  • Stale while revalidate: Serve a known stale value while one request refreshes it.
  • TTL jitter: Spread expirations so keys do not all refresh at once.

Question Variations

  • “What is the difference between a stampede and a cache avalanche?”
  • “How do you prevent a lock holder failure from blocking all requests?”
  • “When is serving stale data better than waiting for a refresh?”

Answers by Technology

+ Add Variant
System DesignImprove this answer ✏️

Expected Answer (Distributed Caching)

A stampede occurs when a hot key expires or disappears and many concurrent requests all regenerate it. Use single-flight request coalescing within one process so only one request loads the origin. Across many instances, use a distributed lock with a short lease, or use stale-while-revalidate: one request refreshes while other callers receive a recently expired value. Add randomized TTL jitter so related keys do not expire in the same instant, and impose timeouts and load shedding so a cache outage does not exhaust the database.

Coordination must fail safely. A lock needs an expiry to avoid permanent blocking if its holder crashes, but its holder should renew only when ownership is clear. Do not make every caller wait indefinitely for the refresh: where product semantics allow it, bounded staleness is often safer than turning an availability problem into an origin overload.

Why It Matters

Stampedes turn a normal expiration into a cascading failure. Protecting the origin keeps the system available during cache churn, deployments, or partial cache outages.

Example Code

const inflight = new Map<string, Promise<string>>();

export function singleFlight(key: string, load: () => Promise<string>): Promise<string> {
  const existing = inflight.get(key);
  if (existing) return existing;
  const pending = load().finally(() => inflight.delete(key));
  inflight.set(key, pending);
  return pending;
}

Common Mistakes

  • Using a distributed lock without an expiry: A crashed lock holder can block refreshes forever.
  • Refreshing every expired key synchronously: A traffic burst can make callers wait and overload the origin simultaneously.

Follow-up Questions

  • What is stale-while-revalidate? (Answer: Serve a bounded stale value while one request refreshes it asynchronously.)
  • Why add TTL jitter? (Answer: It spreads refresh work over time instead of synchronizing expiration across many keys.)

References