A cached value expiring under load means every request that arrives in the next few hundred milliseconds recomputes it simultaneously — so the moment of highest cost is triggered by the cache working correctly.
$value = $redis->get($key);
if ($value === false) {
// 200 requests arrive here at once
$value = $this->expensiveQuery();
$redis->setex($key, 300, serialize($value));
}
A lock so that one request recomputes while the others wait is the obvious fix and turns a stampede into a queue, which on a slow computation is its own outage. Serving the stale value to everyone except the one refreshing it is usually better. The pathological case is a key with a fixed TTL set at deploy time, where every instance expires in the same second forever.