Voltar para o blog

Timeout, retentativa e circuit breaker

Equipe AltiviaCloud · 5 de setembro de 2026 · 4 min de leitura

fetch sem timeout espera para sempre e trava o seu sistema, não o do fornecedor. Quais falhas merecem retentativa, por que o jitter não é preciosismo e o que fazer com o circuito aberto.

Toda integração externa vai falhar. A pergunta é o que o seu sistema faz nos trinta segundos seguintes — e a resposta padrão, "espera", costuma ser a pior de todas.

O timeout que não existe é o pior de todos

Boa parte dos clientes HTTP não tem timeout padrão razoável. fetch sem AbortSignal espera indefinidamente. Se o outro lado aceitou a conexão e parou de responder, sua requisição fica pendurada até alguém desistir — e ninguém desiste.

const res = await fetch(url, {
  signal: AbortSignal.timeout(10_000),
});

Uma linha. Sem ela, uma lentidão do fornecedor vira exaustão de conexões do seu lado: cada requisição pendurada segura um recurso, e em poucos minutos não sobra nada para atender quem chega.

Escolha o número olhando o percentil, não a média. Se o p95 da chamada é 800ms, um timeout de 10s é folgado e ainda corta o caso patológico. Timeout de 60s não é conservador — é ausência de timeout com passos extras.

Nem toda falha merece retentativa

Repetir uma requisição que vai falhar de novo só multiplica o problema. A regra:

Repita: timeout, erro de conexão, 502, 503, 504. São falhas transitórias.

Não repita: 400, 401, 403, 404, 422. A requisição está errada; repetir não conserta.

Caso especial, 429: repita, mas só depois do tempo que o cabeçalho Retry-After indicar. Repetir antes disso é o que transforma um limite temporário em bloqueio — o assunto merece atenção própria.

Cuidado com POST. Repetir uma criação pode criar duas. Só repita se o outro lado aceitar chave de idempotência, ou se você puder verificar antes se a operação já aconteceu.

Espere de forma crescente, com ruído

Repetir de imediato, três vezes, é quase não repetir. Use intervalo que cresce:

const espera = Math.min(1000 * 2 ** tentativa, 30_000);
const jitter = Math.random() * 0.3 * espera;
await dormir(espera + jitter);

O jitter não é preciosismo. Sem ele, todos os seus processos que falharam juntos vão retentar no mesmo milissegundo, e o serviço que estava se recuperando leva o mesmo pico de novo. Rebanho sincronizado é como uma instabilidade curta vira uma queda longa.

Circuit breaker: parar de bater na porta

Retentativa resolve falha de uma requisição. Não resolve fornecedor fora do ar — nesse caso, cada requisição gasta o timeout inteiro antes de falhar, e a fila cresce.

O circuit breaker corta isso com três estados:

Fechado. Tudo normal, requisições passam. Um contador acompanha as falhas.

Aberto. Passou do limite de falhas, o circuito abre: as próximas requisições falham na hora, sem sair pela rede. É isso que devolve capacidade ao seu sistema — falhar rápido é melhor que falhar devagar.

Meio-aberto. Depois de um tempo, deixa passar uma requisição de teste. Se funcionar, fecha. Se falhar, abre de novo.

if (estado === "aberto") {
  if (Date.now() < abertoAte) throw new Error("Circuito aberto");
  estado = "meio-aberto"; // deixa uma passar
}

O ganho aparece quando o fornecedor volta: sem breaker, você o derruba de novo com toda a fila acumulada. Com breaker, uma requisição testa o terreno primeiro.

O que fazer enquanto o circuito está aberto

A parte que mais se esquece: degradar em vez de simplesmente falhar.

  • Se há mais de um fornecedor, caia para o próximo — é o mesmo mecanismo, e o motivo pelo qual pular quem está falhando importa.
  • Se o dado pode ser antigo, sirva o do cache e avise que é uma leitura defasada.
  • Se a operação pode esperar, enfileire para depois em vez de devolver erro.
  • Se nada disso serve, devolva erro claro e rápido, com indicação de quando tentar de novo.

Erro imediato e honesto é melhor experiência que trinta segundos de carregamento terminando em erro.

Onde ligar tudo

Não espalhe isso por cada chamada. Concentre em um lugar só — um wrapper do cliente HTTP — com timeout, política de retentativa e breaker configurados por integração. Assim cada nova chamada nasce protegida, em vez de depender de quem escreveu lembrar.

E instrumente: quantas vezes o circuito abriu, quantas retentativas aconteceram, qual o tempo real das chamadas. Sem esses números você ajusta os limites por palpite, e medir a integração é o que separa ajuste de chute.

É o mesmo raciocínio do failover que roda por trás dos números virtuais: quando um fornecedor não entrega, o pedido segue para o próximo.