Integração resiliente

Retry, backoff exponencial e jitter, com uma demo local

Como tentar de novo sem piorar a fila

Quando uma requisição é recusada ou demora demais, tentar de novo na hora costuma piorar a fila. O caminho que funciona é esperar um pouco mais a cada tentativa e espalhar essas esperas, para várias integrações não voltarem todas juntas.

Na Clicksign, passar do limite de requisições devolve 429 Too Many Requests. Os números de cada ambiente e os headers X-Rate-Limit, X-Rate-Limit-Remaining e X-Rate-Limit-Reset estão em Limite de requisições. Para acompanhar um documento ou uma jornada, use webhooks em vez de consultar em loop. O contraste entre polling e webhook está em Introdução a webhooks.

Glossário

TermoO que é
TimeoutTempo máximo de espera de cada chamada. Passou o prazo, a integração desiste daquela tentativa.
RetryNova tentativa depois de uma falha que pode passar, como 429, 5xx, timeout ou queda de rede.
Backoff exponencialEspera que dobra a cada tentativa, com um teto. Por exemplo, 1 s, 2 s e 4 s.
JitterSorteio somado a essa espera, para várias integrações não voltarem no mesmo instante.
Limite de requisiçõesQuantidade de chamadas aceitas numa janela de tempo. Passou, a resposta é 429.
IdempotênciaRepetir a operação sem criar um segundo recurso. Consulta pode ser repetida. Criação e alteração pedem conferir se o recurso já existe.
PollingConsultar de novo, em intervalos, para saber se algo mudou.
WebhookAviso enviado pela Clicksign quando algo muda, no lugar dessa consulta.

O que fazer na integração

  • Defina um tempo máximo de espera em cada chamada. Sem esse prazo, uma resposta lenta fica ocupando a integração por tempo indefinido.
  • Tente de novo em 429, em 5xx, em timeout e em falha de rede. Um 500 pode ser uma instância momentaneamente ruim; a tentativa seguinte pode cair em outra.
  • Não tente de novo um erro de validação ou de autenticação (400, 401, 403, 404, 422). A requisição está errada; repetir não muda o resultado.
  • Aumente a espera a cada tentativa e imponha um teto. É o backoff exponencial: 1 s, 2 s, 4 s, e por aí vai, até um máximo.
  • Some um sorteio a essa espera. É o jitter. Sem ele, quem recebeu 429 no mesmo instante volta a chamar junto, e a rajada só muda de lugar.
  • Se a resposta trouxer X-Rate-Limit-Reset, espere pelo menos até esse horário antes da próxima tentativa.
  • Limite o número de tentativas. Uma queda longa não se resolve insistindo.
  • Consulta pode ser repetida. Criação e alteração pedem cuidado: se o timeout chegou depois que a Clicksign já tinha gravado o recurso, uma nova tentativa pode criar outro. Confira se ele já existe antes de criar de novo.

Se várias tentativas seguidas falharem, pare e retome mais tarde com uma chamada só. Continuar no automático mantém a fila cheia. Em um lote, distribua os envios ao longo do tempo em vez de disparar tudo no mesmo instante.

Uma demo para ver a diferença

📘

A demo roda no seu computador. O servidor dela falha de propósito. Não é a API da Clicksign, e os limites dela não são os da plataforma.

Dez clientes ligam para esse servidor ao mesmo tempo, como uma rotina agendada. Dá para trocar a estratégia e ver o que acontece com a taxa de sucesso e com a quantidade de ligações.

Legenda do mosaico: 200 atendeu, 429 recusou, timeout travou. Na demo, a recusa acontece quando chega mais de 1 requisição a cada 300 ms. O travamento acontece nos segundos terminados em 2, 3, 5 e 7.

A primeira vez leva aprox. 10 minutos.

Baixar a demo

Projeto completo em um zip (27 KB). Só precisa do Python 3.9 ou mais novo e do Chrome ou do Edge.

Baixar demo-retry.zip

Ver o código

Servidor, clientes e laboratório no repositório público, para ler antes de baixar.

Abrir a pasta demo-retry

Windows

  1. Instale o Python. Entre em python.org/downloads e baixe o instalador. Na primeira tela, marque Add python.exe to PATH antes de clicar em Install Now.
  2. Extraia o zip. No Explorador de Arquivos, clique com o botão direito em demo-retry.zip e escolha Extrair tudo…. A pasta demo-retry precisa conter o arquivo server.py.
  3. Abra o PowerShell nessa pasta. Clique na barra de endereço do Explorador, apague o caminho, digite powershell e aperte Enter.
  4. Ligue o servidor.
python --version
python server.py

A janela fica aberta e mostra cada ligação em verde, amarelo ou vermelho. O servidor confirma assim:

Servidor ouvindo em http://localhost:8013
Instabilidade: trava nos segundos terminados em 2, 3, 5 ou 7
Rate limit: 1 requisição a cada 300 ms
  1. Abra o mosaico no Chrome ou no Edge: http://localhost:8013/mosaico.html. Troque a estratégia nos botões do topo. Para desligar, volte ao PowerShell e aperte Ctrl + C.

macOS

  1. Instale o Python pelo arquivo .pkg em python.org/downloads.
  2. Dê dois cliques no zip. Arraste a pasta demo-retry para a Mesa.
  3. No Terminal:
cd ~/Desktop/demo-retry
python3 --version
python3 server.py
  1. Abra http://localhost:8013/mosaico.html no Chrome ou no Edge. O Safari não abre os dez clientes. Deixe o terminal aberto: cada ligação aparece em verde, amarelo ou vermelho, com as mesmas três linhas de confirmação do Windows. Para desligar, Control + C.

Linux

python3 --version
cd ~ && unzip ~/Downloads/demo-retry.zip
cd ~/demo-retry && python3 server.py

Se python3 não existir: sudo apt install python3 (Ubuntu/Debian) ou sudo dnf install python3 (Fedora). Abra http://localhost:8013/mosaico.html no Chrome ou no Chromium. Deixe o terminal aberto: cada ligação aparece em verde, amarelo ou vermelho, com as mesmas três linhas de confirmação do Windows.

Se algo der errado

  • "python não é reconhecido como nome de cmdlet" (Windows). O instalador rodou sem Add python.exe to PATH. Tente py server.py. Se não funcionar, rode o instalador de novo, escolha Modify e marque Add Python to environment variables. Feche e abra o PowerShell.
  • Digitei python e abriu a Microsoft Store (Windows). Use py server.py, ou desligue os aliases python em Configurações → Aplicativos → Configurações avançadas de aplicativos → Aliases de execução de aplicativo.
  • "command not found: python" (macOS). O comando é python3.
  • "No such file or directory: server.py". O terminal está em outra pasta. Às vezes o zip cria demo-retry/demo-retry. Entre até ver o server.py. No Windows, se a Área de Trabalho estiver no OneDrive, o caminho é OneDrive\Área de Trabalho.
  • "Address already in use" ou "Só é permitido um uso de cada endereço". Já existe um servidor nessa porta. Feche a outra janela com Ctrl + C e tente de novo.
  • O mosaico abre e os quadros ficam em branco. Cada cliente usa um endereço próprio (c1.localhost, c2.localhost…) para o navegador não enfileirar as ligações. Chrome, Edge e Firefox entendem esses endereços. O Safari não.
  • O terminal mostra códigos como [92m. São as cores do log num terminal antigo. Não atrapalham a demo. No Windows 11, o app Terminal mostra as cores.

O que observar

Os dez clientes fazem um pedido a cada 10 segundos, todos no mesmo instante (:08, :18, :28…). Deixe cada estratégia rodar uns 2 minutos.

O servidor da demo aceita 1 requisição a cada 300 ms. Nos segundos terminados em 2, 3, 5 e 7 ele trava, e o cliente desiste em 2 segundos. Esses números são da demo, não da Clicksign.

EstratégiaAtendidosLigações por pedidoO que dá para ver
Sem retry10%1,0Um cliente é atendido por rodada. Os outros nove levam 429.
Retry imediato53%7,6A taxa sobe, mas todo mundo liga de novo na hora e o servidor recebe uma rajada.
Backoff exponencial90%4,4A espera é 1, 2 e 4 s, mas todos juntos. A rajada só muda de lugar.
Backoff + jitter~100%3,0Cada cliente sorteia a espera. As ligações se espalham e quase todos são atendidos.

Os números variam um pouco a cada rodada. A ordem se repete. Em http://localhost:8013/laboratorio/ dá para mudar tentativas, espera e jitter com controles deslizantes.

O código de cada estratégia

Cada estratégia é um arquivo curto em web/clientes/. O arquivo comum.js faz a chamada, com timeout de 2 segundos, e trata 429 e timeout como falha que a estratégia pode tentar de novo.

Sem retry

import { tentar } from "./comum.js";

export async function fazerPedido() {
  return tentar();
}

Retry imediato

import { tentar } from "./comum.js";

const MAXIMO_DE_TENTATIVAS = 10;

export async function fazerPedido() {
  for (let tentativa = 1; tentativa <= MAXIMO_DE_TENTATIVAS; tentativa++) {
    try {
      return await tentar();
    } catch (erro) {
      if (tentativa === MAXIMO_DE_TENTATIVAS) throw erro;
    }
  }
}

Backoff exponencial

import { tentar, esperar } from "./comum.js";

const MAXIMO_DE_TENTATIVAS = 10;
const ESPERA_INICIAL_MS = 1000;
const ESPERA_MAXIMA_MS = 4000;

export async function fazerPedido() {
  for (let tentativa = 1; tentativa <= MAXIMO_DE_TENTATIVAS; tentativa++) {
    try {
      return await tentar();
    } catch (erro) {
      if (tentativa === MAXIMO_DE_TENTATIVAS) throw erro;

      const espera = ESPERA_INICIAL_MS * 2 ** (tentativa - 1);
      await esperar(Math.min(espera, ESPERA_MAXIMA_MS));
    }
  }
}

Backoff exponencial com jitter

import { tentar, esperar } from "./comum.js";

const MAXIMO_DE_TENTATIVAS = 10;
const ESPERA_INICIAL_MS = 1000;
const ESPERA_MAXIMA_MS = 4000;

export async function fazerPedido() {
  for (let tentativa = 1; tentativa <= MAXIMO_DE_TENTATIVAS; tentativa++) {
    try {
      return await tentar();
    } catch (erro) {
      if (tentativa === MAXIMO_DE_TENTATIVAS) throw erro;

      const espera = ESPERA_INICIAL_MS * 2 ** (tentativa - 1);
      const jitter = 0.5 + Math.random();
      await esperar(Math.min(espera, ESPERA_MAXIMA_MS) * jitter);
    }
  }
}

O jitter aqui sorteia entre 0,5 e 1,5 vezes a espera. Com espera de 2 segundos, a próxima tentativa sai entre 1 e 3 segundos, e cada cliente num instante diferente.

Para aprofundar




❓Precisa de ajuda? Entre em contato com o Suporte

💰Dúvida sobre planos e preços? Veja o comparativo

🔍Não sabe qual versão está usando? Descubra a sua versão

📚Respostas rápidas? Visite nosso FAQ


Did this page help you?