2026-07-01
When ten thousand users request the same uncached item at the same instant, the naive cache-miss path spawns ten thousand identical database queries. This is the dogpile (a close cousin of the cache stampede, but focused on the specific problem of duplicate concurrent work rather than expiration timing). The fix is request coalescing: recognize that N in-flight requests for the same key can share one result.
The canonical implementation is single-flight, popularized by Go's golang.org/x/sync/singleflight. It works like this:
K: if K is already in-flight, subscribe to its result. Otherwise, start the work, register the promise, and clean up when done.Real-world example: A product-detail page at an e-commerce site relies on a getProduct(id) call backed by Redis. During a flash sale, product 42's cache entry expires. Within 50ms, 8,000 requests hit the miss path. Without coalescing, that's 8,000 Postgres queries — enough to spike CPU to 100% and cause a cascading timeout. With single-flight, exactly one query runs; the other 7,999 requests await its promise and return in under 10ms. Load on Postgres drops by 99.99%.
Rule of thumb: the coalescing benefit scales with concurrent request count during miss latency. If your backing call takes T ms and you receive R requests per second for the same key, expect up to R × T / 1000 duplicate calls per miss without coalescing. At R=1000/s and T=200ms, that's 200 wasted queries per miss — always worth coalescing.
Where it breaks down:
SETNX with TTL) or a promise proxy layer.Pair single-flight with your cache layer, not instead of it: cache absorbs the steady state, single-flight absorbs the miss spike.
