How to Stop Cache Stampedes: Mastering the Singleflight Concurrency Pattern


In high-throughput services, caching (Redis/Memcached) is your first defense. But when a cache key expires under heavy load, you encounter a Cache Stampede (Thundering Herd):
The Problem: 1,000 concurrent goroutines/threads see a cache miss at t_0.
The Failure Mode: All 1,000 workers bypass the cache and execute identical expensive SQL queries or third-party API calls simultaneously.
The Result: Connection pool exhaustion, CPU spikes, cascading timeouts, and database failure.


The Solution: Singleflight (Request Coalescing)
Instead of letting duplicate concurrent requests hit the downstream dependency, the Singleflight pattern coalesces duplicate in-flight executions into a single shared execution:
Request Registration: When request A arrives for key user:101, it acquires a mutex-guarded flight record in memory and initiates the expensive fetch.
Concurrent Suppressed Callers: Requests B, C, and D for user:101 arrive while A is executing. Instead of spawning new queries, they subscribe to request A's in-flight completion channel/promise.
Shared Return: When request A completes, its return value and error are broadcast to B, C, and D simultaneously. 1 query executes; 1,000 callers receive the result.


// Go implementation using golang.org/x/sync/singleflight
var g singleflight.Group


func getUserData(userID string) (UserData, error) {
v, err, shared := g.Do(userID, func() (interface{}, error) {
// Only 1 DB hit occurs regardless of concurrent traffic volume
return queryDatabaseForUser(userID)
})
return v.(UserData), err
}


The Developer Takeaway:
Pairing distributed caches with an in-memory singleflight layer guarantees that your backend will never execute duplicate expensive computations concurrently on the same host instance.


Discussion Question & Poll
How does your backend architecture handle Cache Stampedes and Thundering Herd events?
πŸ“Š A) In-memory Request Coalescing (singleflight, Promise deduplication)
πŸ“Š B) Distributed Mutex / Lock with Redis (e.g., Redlock)
πŸ“Š C) Probabilistic Early Expiration (XFetch algorithm)
πŸ“Š D) Background Cron / Proactive Cache Warming
Which language/framework concurrency model do you rely on for high-throughput traffic? Let's discuss below!


Call to Action (CTA)
Want to write cleaner, high-performance concurrent code and master systems-level backend engineering?


πŸ‘‰ Join Developers & Coding to share code patterns, debug complex architectures, and build scalable software with fellow developers.
How to Stop Cache Stampedes: Mastering the Singleflight Concurrency Pattern In high-throughput services, caching (Redis/Memcached) is your first defense. But when a cache key expires under heavy load, you encounter a Cache Stampede (Thundering Herd): The Problem: 1,000 concurrent goroutines/threads see a cache miss at t_0. The Failure Mode: All 1,000 workers bypass the cache and execute identical expensive SQL queries or third-party API calls simultaneously. The Result: Connection pool exhaustion, CPU spikes, cascading timeouts, and database failure. The Solution: Singleflight (Request Coalescing) Instead of letting duplicate concurrent requests hit the downstream dependency, the Singleflight pattern coalesces duplicate in-flight executions into a single shared execution: Request Registration: When request A arrives for key user:101, it acquires a mutex-guarded flight record in memory and initiates the expensive fetch. Concurrent Suppressed Callers: Requests B, C, and D for user:101 arrive while A is executing. Instead of spawning new queries, they subscribe to request A's in-flight completion channel/promise. Shared Return: When request A completes, its return value and error are broadcast to B, C, and D simultaneously. 1 query executes; 1,000 callers receive the result. // Go implementation using golang.org/x/sync/singleflight var g singleflight.Group func getUserData(userID string) (UserData, error) { v, err, shared := g.Do(userID, func() (interface{}, error) { // Only 1 DB hit occurs regardless of concurrent traffic volume return queryDatabaseForUser(userID) }) return v.(UserData), err } The Developer Takeaway: Pairing distributed caches with an in-memory singleflight layer guarantees that your backend will never execute duplicate expensive computations concurrently on the same host instance. Discussion Question & Poll How does your backend architecture handle Cache Stampedes and Thundering Herd events? πŸ“Š A) In-memory Request Coalescing (singleflight, Promise deduplication) πŸ“Š B) Distributed Mutex / Lock with Redis (e.g., Redlock) πŸ“Š C) Probabilistic Early Expiration (XFetch algorithm) πŸ“Š D) Background Cron / Proactive Cache Warming Which language/framework concurrency model do you rely on for high-throughput traffic? Let's discuss below! Call to Action (CTA) Want to write cleaner, high-performance concurrent code and master systems-level backend engineering? πŸ‘‰ Join Developers & Coding to share code patterns, debug complex architectures, and build scalable software with fellow developers.
0 Commenti 0 condivisioni 19 Views 0 Anteprima