New in Symfony 4.2: Cache stampede protection

Warning: This post is about an unsupported Symfony version. Some of this information may be out of date. Read the most recent Symfony Docs.

Contributed by
Nicolas Grekas
in #27009.

A cache stampede is a type of cascading failure that can occur when systems come under very high load. Consider a web application that caches some page to improve performance. When the cached version of that page expires and the server is under very heavy load, multiple users may be requiring the same uncached page, which could lead to the collapse of the system via exhausting shared resources.

There are multiple techniques to protect against "cache stampede". In Symfony 4.2 we added one called probabilistic early expiration, to the Cache component (we also added a lock-based one for when the cache is cold.)

This technique solves the stampede problem forcing the regeneration of the cached values before they expire. Whenever a value is read from the cache, the application makes a "probabilistic decision" to decide whether the value must be regenerated or not. Thanks to the formula used, the sooner the expiration of the cached value, the higher the probability of recomputing it.

In order to give better control of this "probabilistic decision", the get() method of CacheInterface has added a third optional argument called beta. It's a float value that defaults to 1.0, which provides optimal stampede protection.

Most of the times you don't have to care about this parameter. Symfony provides the right default value to protect your apps. However, you can set it to an arbitrary value or to 0 to disable this protection or to INF to force an immediate expiration of the cached value:

// this closure will be called when a cache key is missing from the backend
$callback = function () { ... return $computedValue; };

// when undefined, beta = 1.0
$productsCount = $cache->get('stats.products_count', $callback);

// beta = INF to force the immediate expiration of this cached value
$productsCount = $cache->get('stats.products_count', $callback, INF);
Help the Symfony project!

As with any Open-Source project, contributing code or documentation is the most common way to help, but we also have a wide range of sponsoring opportunities.


Wonderful :D thanks!
That's very clever code, nevertheless, I still regret that it's the developer responsibility to hardcode the beta value at cache fetch - this means it cannot be tuned on a per cache pool basis by an admin by configuration depending on the real life behaviour.
@Pierre don't be sad, the default value of beta is implementation depend: you *can* change it via a decorator (doing it via configuration could be possible in the future, upon demand.)
There's a small type: "... a value is read form the cache, ..."
@Thomas fixed! Thanks.

Comments are closed.

To ensure that comments stay relevant, they are closed for old posts.