Distributed architectures depend on caching layers to maintain low latency and protect backend storage from excessive load. Despite their benefits, caching strategies introduce specific failure scenarios that can compromise system stability. Three critical issues often arise: cache penetration, cache breakdown, and cache avalanche. Understanding the distinct characteristics of each failure mode is essential for implementing effective countermeasures.
Failure Mode Analysis
Cache Penetration
This scenario occurs when clients repeatedly query for keys that do not exist in either the cache or the underlying database. Since the data is missing from both layers, every request bypasses the cache and hits the database directly. Malicious actors often exploit this behavior to overwhelm storage systems.
Cache Breakdown
Breakdown happens when a specific hot key expires precisely during a period of high concurrency. The sudden invalidation causes a spike in requests that all miss the cache simultaneously, forcing them to query the database at once. This creates a transient but severe load spike on the storage layer.
Cache Avalanche
Unlike breakdown, which affects a single key, avalanche involves the simultaneous expiration of a large number of cache entries. This often results from setting uniform expiration times across many keys. When the time window arrives, the cache hit rate drops to near zero, redirecting massive traffic to the database and potentially causing a total system outage.
Mitigation Strategies
Preventing Penetration
Probabilistic Filtering Implementing a Bloom Filter before the cache layer allows the system to quickly determine if a key might exist. If the filter indicates the key is absent, the request is rejected immmediately without querying the database.
Tombstone Records For keys confirmed to be non-existent in the database, store a null value or a specific placeholder in the cache with a short time-to-live (TTL). This prevents subsequent requests for the same invalid key from reaching the database during the TTL window.
Preventing Breakdown
Logical Expiration Instead of relying on physical TTL, store an expiration timestamp within the cached value itself. A background thread or async process updates the cache before the logical expiration time is reached, ensuring the data remains available during high traffic.
Distributed Mutual Exclusion When a cache miss occurs, use a distributed lock to ensure only one thread queries the database. Other threads wait and retry until the cache is repopulated.
public String fetchItemDetails(String resourceId) {
String content = cacheStore.get(resourceId);
if (content != null) {
return content;
}
String lockKey = "mutex:" + resourceId;
int retries = 3;
while (retries > 0) {
boolean isLocked = cacheStore.setIfAbsent(lockKey, "LOCKED", 30);
if (isLocked) {
try {
content = database.queryItem(resourceId);
cacheStore.put(resourceId, content, 3600);
return content;
} finally {
cacheStore.delete(lockKey);
}
}
try {
Thread.sleep(150);
content = cacheStore.get(resourceId);
if (content != null) return content;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
retries--;
}
return content;
}
Preventing Avalanche
TTL Jitter Add a random variance to the expiration time of each cache entry. Instead of a fixed duration, calculate the TTL as a base value plus a random offset. This distributes expiration events over a wider time window.
int baseTtl = 3600;
int jitter = (int)(Math.random() * 600);
cache.expire(key, baseTtl + jitter);
Cluster Sharding Distribute hot keys across different nodes in a Redis Cluster. This ensures that if one node experiences issues or high load, the failure is isolated and does not affect the entire cache layer.
Multi-Tier Caching Employ a two-layer caching strategy where a secondary cache (Layer B) retains data even if the primary cache (Layer A) expires. When Layer A misses, data is served from Layer B while Layer A is updated asynchronously.
Comparative Overview
| Failure Mode | Trigger Condition | Impact Scope | Primary Mitigation |
|---|---|---|---|
| Penetration | Queries for non-existent keys | Data base load increase | Bloom Filters, Null Caching |
| Breakdown | Hot key expiration under load | Single key spike | Mutual Exclusion, Logical Expiration |
| Avalanche | Mass key expiration | System-wide outage | TTL Jitter, Sharding, Multi-Tier |