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.
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.