01
Anatomia do stampede: o que acontece no milissegundo em que a chave expira
O padrão mais comum de cache é o cache-aside: lê do Redis, e se não estiver lá, consulta o banco e grava o resultado com TTL. O código abaixo é exatamente isso, e ele está correto para qualquer chave com pouco tráfego. O problema aparece quando a mesma chave recebe milhares de leituras por segundo e a consulta que a produz não é trivial, como a vitrine da página inicial de um e-commerce, que agrega estoque, preço promocional e ranking de vendas.
// vitrine.js (versao com problema)
export async function obterVitrine({ redis, carregarVitrine }) {
const emCache = await redis.get('vitrine:home');
if (emCache) return JSON.parse(emCache);
// 400 ms de consulta: toda requisicao que chegar nessa janela tambem erra o cache
const vitrine = await carregarVitrine();
await redis.set('vitrine:home', JSON.stringify(vitrine), 'EX', 60);
return vitrine;
}Entre o instante em que a chave expira e o instante em que a primeira requisição grava o novo valor, todas as outras requisições também erram o cache e também consultam o banco. A quantidade de consultas idênticas em voo é simples de calcular: taxa de requisições vezes tempo de recomputação. Com 2.000 requisições por segundo e uma consulta de 400 ms, são cerca de 800 consultas iguais disparadas em menos de meio segundo, contra um pool de 20 conexões.
Vitrine da home: 2.000 req/s, consulta de 400 ms, TTL de 60 s, pool de 20 conexões.
t = 60,000 s vitrine:home expira no Redis
t = 60,001 s req 1 erra o cache -> consulta ao banco (400 ms em condição normal)
t = 60,002 s req 2, 3, 4 erram o cache -> mais 3 consultas idênticas
... 2.000 req/s x 0,4 s = ~800 consultas idênticas em voo
t = 60,010 s pool de 20 conexões esgotado, o resto espera na fila do pool
t = 60,400 s a consulta passa a levar 3 s por disputa de CPU e I/O
-> a janela sem cache cresce de 0,4 s para 3 s
-> ~6.000 consultas idênticas, timeouts e retries do cliente
t = 63,000 s a primeira consulta termina e grava o cache
o banco ainda leva segundos para drenar o que ficou na fila
Misses simultâneos = taxa de requisições x tempo de recomputação.
O tempo de recomputação cresce com a própria carga: realimentação positiva.O detalhe que transforma um pico em incidente é a realimentação. As 800 consultas disputam CPU, buffer e locks no banco, então cada uma demora mais; como cada uma demora mais, a janela sem cache se alonga; como a janela se alonga, mais requisições erram o cache. Uma consulta de 400 ms vira uma de 3 s, e 800 consultas viram 6.000. O banco que suportava a carga normal com folga cai por uma consulta que ele já tinha respondido um minuto antes, e os retries dos clientes empurram o sistema para o mesmo lugar depois que o cache volta.