Chaves de idempotência: retries seguros em integrações reais
O cliente envia “criar pagamento”, a conexão expira e ninguém sabe se houve cobrança. Repetir sem identificar a operação pode gerar uma segunda cobrança; desistir pode deixar o pedido parado. A chave de idempotência permite ao servidor reconhecer uma tentativa repetida da mesma intenção e devolver o resultado já registrado.
Publicado em 28 de setembro de 202613 min de leituraPadrões confiáveis para mutações de API
Uma intenção, várias tentativas de entrega
A chave se mantém entre retries; uma nova ação do usuário recebe outra
1 / IntençãoCliente cria chave e requisição canônica.
2 / EnviarServidor reserva a chave no escopo da operação.
3 / ExecutarMutação e resultado são persistidos.
4 / RepetirMesma chave devolve o resultado, sem novo efeito.
Idempotência não significa “ignorar HTTP duplicado”. É um contrato: em determinado escopo e prazo, a mesma chave com a mesma entrada produz um efeito lógico e resultado repetível. Não garante entrega de rede exatamente uma vez nem torna efeitos externos atômicos.
Modele a chave em torno da operação
Gere uma chave imprevisível quando usuário ou job criar a intenção e persista antes de enviar. UUID aleatório costuma bastar. Não use só horário, ID do cliente ou número da tentativa. Escopo por conta/cliente e operação para evitar colisões. Nunca permita que entrada não confiável atravesse o limite de tenant.
Guarde fingerprint da requisição canônica. Reutilizar a chave com valor, moeda ou destino diferente deve resultar em conflito, não replay silencioso nem nova mutação. Normalize os valores antes do hash e exclua campos de transporte que não mudam a intenção.
Persista reserva, efeito e resposta de forma coerente
Estado
Significado
Comportamento do retry
Novo
Não existe operação para chave e escopo.
Reservar atomicamente antes de executar.
Em andamento
Uma requisição executa; outra chegou em paralelo.
Conflito, Retry-After ou espera curta documentada.
Concluído
Efeito e resposta estável persistidos.
Retornar resultado anterior para fingerprint igual.
Falha/incerto
Falha definitiva ou efeito externo sem confirmação.
Classificar e reconciliar antes de novo efeito.
Use restrição única em (escopo, chave) para arbitrar concorrência. Persista estado, fingerprint, resposta ou referência ao resultado e horários. Para mutação no mesmo banco, grave registro de negócio e conclusão na mesma transação. Se chamar pagamento ou e-mail externo, use também idempotência do provedor ou padrão outbox/reconciliação: transação local não torna atômico um HTTP remoto.
Pagamento: timeout após sucesso no provedor
O serviço cria a chave `pedido-842:autorizar-v1` e envia. O provedor autoriza, mas a resposta se perde. Repetir com a mesma chave devolve a autorização original; o serviço grava sua referência e conclui. Nova tentativa após recusa deliberada ou valor alterado é outra intenção, com nova chave e auditoria explícita.
Semântica varia entre provedores: alguns guardam a primeira resposta, inclusive certos erros; o prazo de retenção também muda. Leia o contrato e mantenha registro local além da janela máxima de retry/reconciliação. Depois que a chave do provedor expirar, não repita cobrança incerta sem consultar o estado.
Webhook: entrega repetida, uma transição
Persista ID da entrega com unicidade para deduplicar redelivery. Aplique também idempotência de domínio: atualize assinatura só se a versão de origem for mais nova ou transição válida. O provedor pode mandar IDs diferentes sobre o mesmo objeto; deduplicação de entrega não basta. Mantenha tentativas e efeitos de negócio separados.
Job em segundo plano: evite concluir duas vezes
Mensagens da fila podem voltar após crash do worker. Dê ID estável ao job lógico, reserve atomicamente e torne cada ação subsequente idempotente. Em fluxos com etapas, persista estado e use outbox/saga e compensações quando fizer sentido. “Exatamente uma vez” entre sistemas independentes costuma ser ilusão; prefira entrega ao menos uma vez com efeito lógico único e reconciliação.
Defina a expiração com cuidado
Apagar cedo permite que retry atrasado execute de novo; guardar tudo para sempre aumenta custo e obrigações de privacidade. Defina retenção pela soma de retry do cliente, redelivery da fila, dispositivos offline e replay de suporte. Depois de remover a resposta, mantenha tombstone ou restrição de negócio quando duplicidade for cara. Expiração faz parte do contrato da API.
Teste os momentos ambíguos
Teste mesma chave/corpo sequencial e concorrente, chave igual com corpo diferente, crash antes/depois do commit e antes da resposta, timeout após sucesso remoto, expiração e replay. Verifique resposta e quantidade de efeitos. Registre correlação e hash da chave, nunca payload sensível bruto ou credencial de pagamento.
Em resumo
Chave de idempotência nomeia uma mutação pretendida através de entregas instáveis. Gere uma vez, escopo correto, vincule a fingerprint, reserve com unicidade, persista o resultado e entenda a retenção do provedor. Retries viram recuperação controlada em vez de aposta que duplica dinheiro ou trabalho.