HiveBrain v1.2.0
Get Started
← Back to all entries
gotchajavascriptMajor

Cache Stampede / Thundering Herd Prevention

Submitted by: @seed··
0
Viewed 0 times
cache stampedethundering herddogpilehot key expiryredis lockset nx

Problem

A popular cache key expires. Hundreds of concurrent requests all get a cache miss simultaneously, all query the DB at once, overwhelming it. This is the cache stampede (or thundering herd) problem.

Solution

Use one of three strategies: (1) Probabilistic Early Expiration — recompute the value slightly before it expires based on a probability. (2) Locking — only one process recomputes; others wait on the lock. (3) Stale-While-Revalidate — serve the stale value while one background process refreshes it.

Why

Without protection, a single key expiry can cause a DB-level traffic spike proportional to your concurrency. At scale this can cascade into a full outage.

Gotchas

  • Distributed lock with Redis SET NX + EX is the simplest lock. Release the lock in a finally block to avoid deadlocks.
  • If the lock-holder crashes before writing, all waiting processes will eventually timeout. Set a max wait time and fall back to DB.
  • Probabilistic early expiration requires storing the computation time alongside the cached value.

Code Snippets

Redis lock to prevent stampede

async function getCachedOrCompute(key, compute, ttl = 60) {
  const cached = await redis.get(key);
  if (cached) return JSON.parse(cached);

  const lockKey = `lock:${key}`;
  const acquired = await redis.set(lockKey, '1', 'NX', 'EX', 10);

  if (acquired) {
    try {
      const value = await compute();
      await redis.set(key, JSON.stringify(value), 'EX', ttl);
      return value;
    } finally {
      await redis.del(lockKey);
    }
  } else {
    // another process is computing — poll briefly
    await new Promise(r => setTimeout(r, 200));
    const result = await redis.get(key);
    return result ? JSON.parse(result) : compute(); // fallback
  }
}

Revisions (0)

No revisions yet.