In this lesson, you will learn how to prevent "cache stampedes"—where a sudden rush of requests hits your database when a popular cache key expires—by implementing Probabilistic Early Expiration, often called XFetch.
The Problem: The Stampede
Imagine your Node.js application caches a high-traffic product page with a 60-second TTL (Time-to-Live). When that key expires, the very next request—and every request arriving during the time it takes to re-fetch the data from your database—becomes a "cache miss." If your site gets 1,000 requests per second, all 1,000 might try to query your database simultaneously, potentially crashing your backend. This is the cache stampede.
The XFetch Solution
Instead of waiting for the key to expire, XFetch introduces a probabilistic approach. As a key approaches its expiration, we treat it as "expired" for a small, randomized percentage of requests.
By triggering a background update before the hard expiration, we ensure that while one request is off refreshing the data, the remaining requests continue to serve the current (stale) data from the cache.
Implementation
To implement this, we store the delta (a speed constant) and the value along with the time_to_live in our cache. Here is the logic:
const beta = 1.0; // Adjusts how aggressively we refresh early
function shouldRefreshEarly(ttl, delta, startTime) {
const secondsElapsed = (Date.now() - startTime) / 1000;
// XFetch formula: wait time > delta * beta * log(random_value)
return secondsElapsed > (ttl - (delta * beta * Math.log(Math.random())));
}
// Inside your cache retrieval:
if (cache.has(key) && shouldRefreshEarly(ttl, delta, entry.createdAt)) {
// Trigger re-fetch asynchronously to prevent blocking the user
repopulateCache(key);
}
The Math.log(Math.random()) adds the necessary jitter. Because the probability of expiring increases as the key nears its TTL, the background re-fetch is almost guaranteed to complete before the hard expiration occurs. This smoothens out your traffic, turning a synchronized surge into a single, managed background task.
Key Takeaway: By introducing randomized early expiration, you move the burden of re-fetching data from the request-response cycle to a background process, effectively neutralizing cache stampedes.