Estratégias de repetição e idempotação
1) Por que é necessário
As falhas nas redes são normais: temporizações, erros transiens, flappings de rede, sobrecarga. Os retais só melhoram a confiabilidade se:1. a repetição é segura (idempotental),
2. os extratos entre as repetições são respeitados,
3. os limites/quotas e a «saúde» das dependências são respeitados.
O objetivo é efectively-once comportamento no nível de transações de negócios sem falsas duplicações e corridas.
2) Taxonomia semântica de entrega
At-matt-once: sem repetição, risco de perda (loging, fire-and-forget).
At-least-once: pode duplicar → precisa de idempotação do consumidor (a maioria das filas, webhooks).
Effectively-once: Duplicados são possíveis, mas são deduzidos corretamente (chaves, transações, outbox).
3) Quando retrabalhar e quando não
Retranquear faz sentido: '408', '429' (respeitando 'Retry-After'), '425' (Too Early), '499' (cliente closed no perímetro), '5xx', '504', 'temporizadores de rede/quebras,' 502 'no porteiro,' connect reset '.
Não retraim sem alterar a solicitação: '400/ 401/403/404/422'.
Malas controversas: '409 Conflict' (normalmente não retraim; primeiro lemos o status da operação/reconfirmamos a intenção).
4) Temporizadores, backoff e jitter
4. 1 Regras
Primeiro o tempo, depois as retrações, cada pedido tem de ter um deadline.
Exponential backoff: 'delay _ n = base 2 ^ n', limitando 'max _ delay'.
Jitter é obrigatório: adicione um acidente para «ondas sincronizadas estúpidas».
4. 2 Modelos de jitter
Full jitter: 'sleep = rand (0, base2 ^ n)' é a melhor escolha geral.
Decorrated jitter: 'sleep = min (max _ delay, rand (base, sleep _ prev3)' - para longos diálogos.
Equal jitter: 'sleep = base2 ^ n/2 + rand (0, base2 ^ n/2)' é uma variação suave.
4. 3 Retry-budget
Limite a proporção de retais:- `retry_budget_per_min = max(α success_rps, floor β)`; normalmente ' = 0. 1–0. 2`.
- Quando o orçamento estiver esgotado, passe para fail-fast/circuito breaker «open».
5) Interação com rate limiting e Circuito Breaker
Respeite «Retry-After», «RateLimit-Reset» e considere-o no back-off.
Em «5xx» altos/temporais - reduza a frequência dos retais e o paralelismo geral.
- Half-open: permite uma amostra limitada.
- Open: Rejeita instantaneamente (economizando o recurso).
- Closed: trabalho normal.
- Nas operações write, é preferível voltar 409/503 com uma dica clara do que rodar retais agressivos.
6) Idempotidade de operações write
6. 1 Ideia geral
As mesmas intenções → um resultado. A base é a chave de idempotação e o armazenamento de registros de execução.
6. Contrato 2 HTTP
O cliente envia o título:
Idempotency-Key: 7a6b7f9e-2a46-4d0b-9c3a-2b30e1c3c9e3
Idempotency-Key-Expiry: 24h # optional
Servidor:
- no primeiro sucesso, salva (chave, resultado, status, hash corporal);
- quando repetido, devolve a resposta anterior e o título 'Idempotency-Replay: true';
- em conflito corporal (mesma chave, mas outra payload) - '409 Conflict'.
6. 3 Armazenamento e TTL
Tabela/chave-valor: 'idempotency _ key', 'request _ hash', 'result',' status ',' expedy _ at '.
TTL = janela de possíveis repetições e entregas tardias (geralmente 24-72 h para pagamentos).
Índices de 'idempotency _ key'; para alta carga - charding por hashtag.
6. 4 Exemplo de esquema (SQL)
sql
CREATE TABLE idempo_store (
key UUID PRIMARY KEY,
req_hash BYTEA NOT NULL,
status INT NOT NULL,
response JSONB NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
expiry_at TIMESTAMPTZ NOT NULL
);
6. 5 Pseudo-código do processador
pseudo handle_write(req):
k = req. headers["Idempotency-Key"]
h = hash(req. body)
rec = idempo_store. get(k)
if rec and rec. req_hash == h:
return rec. status, rec. response, {"Idempotency-Replay": "true"}
if rec and rec. req_hash!= h:
return 409, problem("IDEMPOTENT_CONFLICT")
begin tx result = apply_business_mutation (req) # change status upsert once (idempo_store, key = k, req_hash=h, status = 201, response = result, expiry = now () + 2d)
commit
return 201, result
7) Pattern «effectively-once»
Transactional Outbox: gravar um evento de negócios e enviar uma mensagem da mesma transação BD através de um relayer de fundo; o consumidor é idêntico.
Inbox/Processado-mesa do consumidor: Preservamos 'event _ id' para ignorar as duplicações.
Exactly-once em Kafka ≠ exactly-once no negócio: mesmo com o EOS produtor/consoante, a lógica aplicada ainda deve ser idimpotente.
Transações Compensadoras (Saga): Se os passos forem retocados e causarem efeitos secundários, voltaremos o sistema ao invariante.
8) Casos privados: pagamentos e transações financeiras
Strong idempotency: a chave está ligada à lógica da operação (por exemplo, 'external _ payment _ id').
Deduplicar para PSP: guarde 'merchant _ reference' → quando o PSP for repetido, devolverá o resultado anterior.
Retrai «do cliente»: Só é permitido com «Idempotency-Key», senão risco de duplo cancelamento.
Competição: bloqueios «para conta/ferramenta/contrato» durante a execução; Quando voltar, devolva 409/423.
Observabilidade: métricas 'idempo _ replay _ total', 'idempo _ conflict _ total'.
9) Webhooks e chamadas externas
Assinaturas HMAC e janela de tempo; Primeiro o teste, depois o processamento.
Retraí do remetente: backoff exponencial + jitter, 'max _ attempts' e DLQ.
Consumidor - Idempotental: 'event _ id' → tabela/in-memory cache; a ordem «cuidadosa» não está garantida.
Códigos: 2xx = sucesso, 4xx = não repetir, 5xx/tempo = repetir.
10) Filas e tarefas de fundo
At-least-once padrão → duplicados são inevitáveis.
Guarde 'task _ id '/' event _ id' e o status de execução; A dupla é um atalho «replay».
DLQ e poison-mensagens: contador de tentativas, quarentena, anistia manual.
Limites competitivos (semafores) e workers idempotentes.
11) Versionização e chaves «naturais»
Chaves naturais (número de conta + data + número de documento) aumentam a resistência às repetições.
Quando você mudar de esquema/versão, inclua a chave de versão em 'Idempotency-Key' ou no hash da consulta.
12) cabeçalhos HTTP e dicas ao cliente
'Idempotency-Key', 'Idempotency-Replay', 'Retry-After', 'Preferer: wait = <sec>', 'If-Match '/' ETag' (bloqueios otimistas).
409 em conflito de chave, 425/429/503 com a valida 'Retry-After'.
Para operações «longas», aceita status asincrônico ('202 Aceited' + 'Location' por recurso de status).
13) Testes e cenários de caos
Testes negativos, duplo envio, repetição corporal, relógio relógio.
«t2» vem antes de «t1».
Injeção de temporizadores/' RST '/' EOF ', solicitação de meia-entrada (slow-POST).
O armazenamento idempotency tombado → o comportamento fail-closed (melhor falha do que duplo cancelamento).
14) Métricas e alertas
`retries_total{reason}`, `retry_budget_used{route}`, `backoff_seconds_bucket`.
`idempo_replay_total`, `idempo_conflict_total`, `duplicate_detected_total`.
Participação 409/425/429/5xx nas rotas; p95/p99 «tempo antes do sucesso» com retraias.
Alerts: burn-rate orçamento de retrações, aumento de conflitos de idempotação, crescimento do DLQ.
15) Antipattern
Retrabalhar todos os erros consecutivos.
Falta de jitter → ondas sincronizadas de retrações.
Chaves de longa vida sem TTL e limpeza.
Salvar o resultado após o efeito colateral (violação do outbox).
O logi sem 'trace _ id '/' idempotency _ key' → não pode ser forense.
Retraias paralelas agressivas em operações write.
16) Folha de cheque pró-prontidão
- Política unificada: o que é retrátil, o que não é; códigos e dicas para o cliente.
- Backoff exponencial + full jitter; especificado por 'retry _ budget'.
- Contrato 'Idempotency-Key' + armazenamento de resultados com TTL.
- Outbox/Inbox para eventos; DLQ; limites de competição.
- Integração com circuito breaker, respect 'Retry-After'.
- Métricas/alertas por retratos/duplicados/conflitos.
- Conjunto de testes de caos e emulação de falhas de rede.
- Documentação para clientes: exemplos de back-offs e estatais.
17) TL; DR
Os retais só são úteis juntamente com a idimpotência. Digite 'Idempotency-Key' e armazenamento de resultados, aplique backoff exponencial com jitter e retry-boodget, respeite 'Retry-After', integre-se ao circuito breaker. Para eventos - outbox/inbox; para pagamentos - Deduplicação rigorosa e bloqueios. Mede retais e conflitos, teste duplicados e temporizadores.