Request Coalescing
Preventing thundering herds with in-flight deduplication
Key Takeaways
- ✓Request coalescing deduplicates concurrent identical requests — only one hits the backend, all callers share the result
- ✓Use a Map of in-flight Promises keyed by the request identifier — return the existing Promise for duplicate requests
- ✓Delete the key from the map after the Promise resolves (in .finally()) to avoid stale caching
- ✓This is not a cache — it only deduplicates requests that are in-flight simultaneously
What is Request Coalescing?
Request coalescing (also called "request deduplication" or "singleflight") is a technique that combines multiple identical concurrent requests into a single backend call. When ten users request the same resource at the same time, only one request goes to the backend — the other nine wait for that single request to complete and share its response.
This is different from caching: a cache stores results for future requests. Coalescing only affects requests that are *currently in-flight* at the same moment.
Why It Matters
The thundering herd problem occurs when a popular cache entry expires and hundreds of concurrent requests all miss the cache simultaneously. Without coalescing, all of those requests hit the backend at once, potentially overwhelming it.
This pattern is especially critical for:
- CDN origin servers: Where cache misses trigger many simultaneous backend requests
- API gateways: Where concurrent users request the same data
- Database query layers: Where identical queries should share one round-trip
How It Works
The implementation is surprisingly simple using Promises:
- Maintain a
Mapof in-flight requests> - When a request arrives, check if the key is already in the map
- If yes: return the existing Promise (the caller will await the same result)
- If no: create a new Promise by making the backend call, store it in the map, and return it
- When the Promise resolves, delete the key from the map (in a
.finally()handler)
Step 5 is critical — if you don't delete the key, the map becomes a permanent cache, which is not what you want. Coalescing is strictly for in-flight deduplication.
Common Mistakes
- Not cleaning up the map: Forgetting the
.finally()cleanup turns your coalescing map into a stale cache that never refreshes. - Using a cache instead: A cache with a TTL is a different pattern. Coalescing specifically handles the thundering herd problem for concurrent requests.
- Not sharing the Promise: Creating a new Promise for each caller defeats the purpose. All callers must await the *same* Promise object.
Further Reading
The canonical implementation of request coalescing in Go — same concept, different language.
Real-world use of request coalescing to prevent thundering herds at massive scale.
Covers cache stampede prevention including request coalescing as a mitigation strategy.