Distributed in-memory caches such as Redis and Memcached are vital shields protecting the relational data tier. However, without defensive cache architecture, platforms remain vulnerable to three classic failure modes capable of crashing production databases in seconds.
1. Cache Avalanche (TTL Jittering)
An avalanche occurs when thousands of keys share identical expiration timestamps and expire simultaneously (e.g. midnight). Incoming traffic bypasses the cache en masse, crushing the database.
Solution: Always inject a randomized jitter offset into key TTLs:
actualTTL = baseTTL + rand.Duration(0, 300*time.Second)
2. Cache Stampede Mitigation with Go Singleflight
When a hot key expires, thousands of concurrent goroutines encounter a cache miss at the same millisecond and execute redundant database queries. Go's standard golang.org/x/sync/singleflight collapses duplicate concurrent calls into exactly one execution:
package service
import (
"golang.org/x/sync/singleflight"
"time"
)
var requestGroup singleflight.Group
func GetUserProfile(userID string) (string, error) {
if val, ok := getFromCache(userID); ok {
return val, nil
}
// Singleflight guarantees only 1 DB query executes across 10,000 concurrent callers
data, err, _ := requestGroup.Do(userID, func() (interface{}, error) {
res, err := fetchFromDatabase(userID)
if err == nil {
setCache(userID, res, 10*time.Minute)
}
return res, err
})
if err != nil {
return "", err
}
return data.(string), nil
}