1

O retry que derrubou o sistema: a importância de circuit breaker, backoff e token bucket na prática

O serviço começa a ficar lento. Os timeouts aparecem. Os clientes fazem o que todo cliente bem-intencionado faz: tentam de novo. A fila cresce, o tempo de resposta piora, e o tempo de resposta pior gera... mais retry.

Alguém grita "sobe mais duas réplicas!". E... o sistema cai de novo.

Se você já viveu isso, você já tomou um retry storm: a tempestade de retentativas que transforma um serviço lento em um serviço morto.

Por que subir réplica não resolve

Quando o throughput encosta no limite, nasce um ciclo vicioso: fila longa de requisições → tempo de resposta maior → mais timeouts → mais retentativas → fila ainda maior.

O detalhe cruel é que reiniciar ou escalar o serviço não quebra o ciclo. A carga não foi embora: ela está lá, enfileirada, esperando. O serviço volta e é sobrecarregado de novo, em instantes. É por isso que a resposta para sobrecarga quase nunca é "mais máquina", e sim controlar o fluxo.

O arsenal contra a sobrecarga

Circuit breaker: pare de insistir. O disjuntor abre, parte das requisições é recusada na entrada, e o serviço ganha espaço para recuperar a saúde aos poucos. Melhor responder erro rápido para alguns do que travar para todos.

Exponential backoff: se a primeira tentativa falhou, esperar o mesmo 1 segundo e tentar de novo só afunda mais o sistema. Dobre o intervalo (1s, 2s, 4s, 8s) ou use tempos aleatórios (jitter), para os clientes não voltarem todos juntos na mesma onda.

Token bucket (rate limiting): pense num balde de fichas por usuário. Cada requisição consome uma ficha; encheu o limite, espera um pouco. Distribui a carga entre os usuários em vez de deixar um só monopolizar o sistema.

Load shedding: o sistema detecta a sobrecarga se aproximando e passa a rejeitar requisições de forma proativa, no estilo "eu sei que não vou conseguir atender isso agora".

Back pressure: em vez de só rejeitar, o sistema avisa o cliente que precisa diminuir a velocidade de envio.

Plot twist: o TCP faz isso desde os anos 80 com maestria

A parte que mais me diverte nessa discussão: tudo isso já existe dentro do TCP. Controle de fluxo, controle de congestionamento, retentativa com recuo. Está tudo embutido no protocolo em que a internet inteira está calcada, decidido lá atrás nos anos 80/90. A gente redescobre na camada de aplicação o que a camada de transporte já resolvia sem ninguém perceber.

O princípio por trás de tudo

Esses mecanismos compartilham uma filosofia: prefira um fault a um failure. Você sabe a diferença?

Fault é o problema que o sistema contorna: recusa uma requisição, degrada um pedaço, vai capengando, mas continua funcionando e preservando os fluxos críticos. Failure é quando o sistema não lida bem com algum problema e cai de vez (503).

As técnicas citadas acima fazem isso: geram um fault de propósito, um erro controlado agora, uma leve degradação, para evitar o failure total do sistema.

Resiliência não é impedir todo erro; é escolher quais erros você vai tolerar para continuar de pé.


Esse papo saiu do nosso clube do livro, em que estamos lendo o Designing Data-Intensive Applications.
A segunda parte do Capítulo 2 é hoje, fica aqui nosso convite a você:
https://craftcodeclub.io/book-club/designing-data-intensive-applications


E vocês, já tomaram um retry storm em produção? Como seguraram a onda? Compartilhem a experiência nos comentários.

Carregando publicação patrocinada...