Nicolas Grekas
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:

1
2
3
4
5
6
7
8
// 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);
Published in #Living on the edge