DEV Community

Mohamed Ismail
Mohamed Ismail

Posted on AI-assisted

Your Cache Expires. 10,000 Requests Hit Your Database. There's a Smarter Fix.

Imagine your application has a popular API endpoint.

You cache its response in Redis for 5 minutes to avoid hitting the database repeatedly.

Everything works perfectly.

Until the cache expires.

Suddenly, 10,000 requests arrive at the same time. They all find an empty cache and start querying your database.

Congratulations. Your cache just caused a database traffic spike.

This problem is called a Cache Stampede, also known as the Thundering Herd.

The Usual Solution: Distributed Locks

A common solution is to use a distributed lock.

When the cache expires:

  1. The first request acquires a lock.
  2. It fetches fresh data from the database.
  3. Other requests wait or use stale data.
  4. The cache gets updated.

It works, but now you're managing locks, timeouts, and failure cases.

What if we could refresh the cache before it expires?

Meet XFetch

XFetch is a clever algorithm based on probabilistic early expiration.

Instead of waiting until the cache expires, it randomly decides whether to refresh it early.

Here's the interesting part:

  • When the cache is fresh, refreshing is unlikely.
  • As expiration approaches, refreshing becomes more likely.
  • Slower database queries encourage earlier refreshes.

Think of it like replacing a phone battery before it dies instead of waiting for it to shut down.

No distributed lock is required to make the refresh decision.

The Tiny Formula Behind It

now - β × delta × ln(random()) >= expiry
Enter fullscreen mode Exit fullscreen mode

Looks complicated? It's actually simple.

  • now — Current time
  • expiry — When the cache expires
  • delta — How long the last refresh took
  • random() — A random number between 0 and 1
  • β — Controls how aggressively we refresh

The closer we get to expiration, the more likely the condition becomes true.

That's the core idea.

A Simple JavaScript Example

Here's the early-expiration decision in JavaScript:

function shouldRefresh(expiry, delta, beta = 1) {
  const random = Math.max(Math.random(), Number.MIN_VALUE);

  return (
    Date.now() - beta * delta * Math.log(random) >= expiry
  );
}
Enter fullscreen mode Exit fullscreen mode

That's it!

Let's say:

const expiry = Date.now() + 5000;
const delta = 200;

console.log(shouldRefresh(expiry, delta));
Enter fullscreen mode Exit fullscreen mode

The function returns true or false.

If it returns true, the application can refresh the cached value early.

The important detail is that delta and expiry use the same time unit. Here, both are in milliseconds.

But There's a Catch

XFetch is smart, but it isn't magic.

Multiple requests can still decide to refresh simultaneously. It reduces the risk of a stampede; it doesn't guarantee exactly one refresh.

Also, if you delete the old cache entry before refreshing, other requests can still experience cache misses.

A practical implementation should keep serving the existing value while refreshing, where stale data is acceptable.

For stronger protection, you can combine early expiration with a single-flight mechanism or a short-lived refresh lock.

When Should You Use It?

XFetch is particularly useful for:

  • Popular API responses
  • Homepage feeds
  • Trending products
  • Expensive database queries
  • Frequently accessed cached data

For applications where a little stale data is acceptable, probabilistic early refresh can be a simple way to reduce sudden database load.

For strict freshness requirements, consider additional coordination.

Final Thoughts

Most caching strategies ask:

"Has the cache expired?"

XFetch asks a smarter question:

"Should we refresh it before it expires?"

That small change can help prevent thousands of requests from rushing to your database at once.

Sometimes, a little randomness is exactly what your system needs.

Top comments (0)