O Caso do SQL Fantasma: Como uma Cache REDIS Corrompida por Plugin de Cupom Quase Derrubou um Site de 2 Milhões de Visitas

Você confia no Redis? Eu também confiava. Até o dia em que um plugin de cupom gratuito transformou meu site num zumbi lento. Um erro de 500 por minuto, logs sem nexo, e uma missão de debugging que durou 72 horas. Aqui está o que aconteceu – e como evitar que isso aconteça com você.

O Cenário: Um Black Friday Silencioso

Era outubro de 2023. Um cliente (e-commerce de nicho com 2 milhões de sessões/mês) decidiu ativar um plugin de cupom promocional. O site usava WooCommerce, Redis como cache de objetos e transientes, e um banco MariaDB otimizado. Funcionava perfeitamente. Até que não.

“Os relatórios de performance mostravam picos de cpu em horários aleatórios, mas ninguém ligava os pontos. O time de marketing só queria o cupom ativo.”
— Engenheiro anônimo, em fórum privado

O plugin de cupom, gratuito, usava transientes para armazenar sessões de desconto. Nada incomum. Mas, ao invés de usar set_transient() com expiração adequada, ele criava transientes com chaves baseadas em user_id + random_hash. Isso gerou centenas de milhares de transientes únicos, todos armazenados no Redis. A cache encheu.

O Erro: Cache Corrompida e Queries Fantasmas

Quando o Redis atingiu o limite de memória, começou a fazer evicções. Mas o plugin não verificava se o transiente existia de fato. Ao tentar recuperar um transiente evictado, o código do plugin disparava uma query SQL para regenerá-lo. A query era algo como:

SELECT * FROM wp_postmeta WHERE meta_key = '_coupon_code' AND meta_value = 'DESCONTO10'

Em grande escala, isso se repetia milhares de vezes por minuto. O banco começou a acumular deadlocks. O pior: como o transiente sumia, o plugin tentava recriá-lo a cada request, gerando um ciclo vicioso de queries e evicções.

Sintomas Físicos (No Servidor)

  • MySQL com 400+ conexões ativas – um pico de inatividade nunca visto;
  • CPU do servidor web a 95% – porém sem tráfego real maior que o normal;
  • Logs de erro com “Too many connections” – mas o pool estava configurado para 500;
  • Redis com 3.2GB de uso – para um site que antes usava 800MB;
  • Tempo de execução do WordPress: 45 segundos – para uma página simples.

O Diagnóstico: Como um Erro de Lógica Quase Matou o Site

Ao abrir os transientes no Redis, vi algo assustador: mais de 2 milhões de chaves com prefixo wpc_coupon_session_. Era o plugin. Ele criava um transiente para cada visitante anônimo, sem expiração. Sim, sem expiração. Aí estava o erro.

A solução imediata foi resetar o Redis com FLUSHALL e desativar o plugin. Em minutos, o site voltou ao normal. Mas o aprendizado ficou: cache não é salvação, é responsabilidade.

A Solução Definitiva: Regras de Negócio e Monitoramento

Depois do incidente, implementamos:

1. Limpeza Programada de Cache

Um script de cron que rodava a cada 30 minutos e deletava transientes com padrão wpc_coupon_session_* mais velhos que 1 hora. Nunca mais tivemos acúmulo.

2. Monitoramento de Cache no New Relic

Alertas para quando o número de chaves no Redis ultrapassasse 500 mil. Aviso antes do caos.

3. Substituição do Plugin

Um plugin pago e bem codificado, com uso correto de transientes expiráveis e cachê local com verificação de existência.

O erro foi corrigido. Mas a cicatriz ficou.

Lições para o Mundo WordPress

  • Nunca confie em plugins gratuitos sem auditar o código de cache;
  • Transientes sem expiração são uma bomba-relógio – principalmente em sites com alto tráfego;
  • Redis não é infinito – monitore o uso e configure políticas de evicção (allkeys-lru é seu amigo);
  • Logs mentem – o erro que vi foi “conexão perdida com o banco”. A causa real estava na cache.

A próxima vez que você ver um pico de CPU sem explicação, antes de culpar o servidor, olhe para o Redis. Pode ser que um plugin gratuito esteja fazendo um SQL fantasma.

Rolar para cima