Cache stampede: Quando diminuir o tempo de cache causa mais problemas

Yuna Kisaragi Por Yuna Kisaragi
Rolando Bonaccorsi

Um time de e-commerce percebe que o estoque exibido na página de produtos está desatualizado com frequência incômoda. A solução parece óbvia: reduzir o tempo de vida do cache, forçando atualizações mais frequentes contra o banco de dados. Rolando Bonaccorsi, diretor de operações da Vert Analytics, já viu essa decisão aparentemente sensata piorar drasticamente a estabilidade de um sistema, em vez de resolver o problema original que motivou a mudança.

O cache existe para poupar o banco de dados de responder repetidamente à mesma pergunta, mas gerenciar quando essa cópia expira e precisa ser renovada é, segundo uma piada clássica de engenharia de software, um dos problemas mais difíceis da computação, ao lado de nomear variáveis corretamente.

O sistema não caiu, só está mentindo com confiança

O problema mais insidioso de cache mal gerenciado não é lentidão nem indisponibilidade visível; é o sistema continuar respondendo rapidamente, parecendo saudável, enquanto entrega informação desatualizada sem qualquer sinal de alerta para quem está observando os painéis de monitoramento naquele momento. O sistema não está fora do ar, não está lento, apenas mentindo com total confiança sobre o estado real dos dados.

Rolando Bonaccorsi destaca que essa característica torna bugs de cache particularmente difíceis de diagnosticar: nenhum erro é lançado, nenhuma métrica de disponibilidade cai, apenas dados errados circulando com total confiança pelo sistema, até que alguém perceba a inconsistência, geralmente um cliente reclamando de algo que já deveria ter sido corrigido há tempo, muito depois de a falha original ter acontecido silenciosamente.

O efeito manada: quando todo mundo bate no banco ao mesmo tempo

Quando um item de cache popular expira, múltiplas requisições simultâneas descobrem essa ausência ao mesmo tempo, e cada uma delas, sem coordenação entre si, dispara sua própria consulta ao banco de dados para reconstruir aquele mesmo valor específico que acabou de expirar. Nenhuma dessas requisições sabe que as outras estão fazendo exatamente a mesma coisa naquele exato instante.

Esse fenômeno, chamado de cache stampede ou efeito manada, pode derrubar um banco de dados que suportaria tranquilamente o tráfego normal, simplesmente porque centenas de requisições redundantes chegam simultaneamente pedindo exatamente a mesma informação, multiplicando artificialmente uma carga que deveria ser única. Empresas como Twitter, Reddit e Instagram já documentaram publicamente incidentes desse tipo, prova de que o problema afeta até sistemas operados por equipes altamente experientes.

Por que baixar o TTL piora o problema, não resolve

Reduzir o tempo de vida do cache para manter dados mais atualizados parece solução razoável, mas produz efeito colateral contraintuitivo: tempos de vida mais curtos geram mais eventos de expiração por hora, e cada expiração é uma nova oportunidade para o efeito manada se repetir.

Um caso documentado de equipe de e-commerce que reduziu o tempo de cache de dez minutos para sessenta segundos, buscando mostrar estoque mais atualizado, viu incidentes de sobrecarga aumentarem dez vezes em uma semana. Rolando Bonaccorsi considera esse resultado o exemplo mais claro de como uma correção intuitiva pode multiplicar exatamente o problema que deveria resolver.

Travar a reconstrução, não apenas a expiração

A solução mais eficaz não mexe primeiro no tempo de expiração, mas garante que apenas uma requisição reconstrua o valor expirado por vez, enquanto as demais aguardam ou recebem temporariamente a versão anterior, ligeiramente desatualizada, em vez de todas baterem simultaneamente no banco de dados.

Rolando Bonaccorsi recomenda combinar esse mecanismo de trava com pequena variação aleatória no tempo de expiração de cada chave, evitando que múltiplos itens populares expirem exatamente no mesmo instante, uma combinação de técnicas que resolve o problema na raiz, sem sacrificar a atualização razoável dos dados que a equipe originalmente buscava. Servir uma versão levemente desatualizada por alguns segundos, enquanto uma única requisição reconstrói o valor em segundo plano, custa infinitamente menos do que derrubar o banco de dados inteiro na tentativa de manter cada leitura perfeitamente atualizada.

 

Compartilhe esse artigo