Em algum lugar de todos os contratos de integração existe uma frase do tipo «cada encomenda será transmitida exatamente uma vez». Nunca foi verdade, nem uma única vez. A rede particionou a meio de um pedido e o remetente tentou novamente; a fila reentregou a mensagem após uma falha do consumidor; um operador de armazém carregou duas vezes no botão de exportação. Os duplicados não são um modo de falha - são meteorologia.
Chaves de idempotência, escolhidas com cuidado
A solução é antiga e pouco glamorosa: cada mensagem transporta uma chave, e o processamento não faz nada quando essa chave já foi vista. As decisões interessantes estão todas na escolha da chave.
- As chaves naturais superam as geradas. Uma sincronização de encomendas com chave
order_id + statussobrevive à regeneração da outbox pelo remetente; um UUID aleatório gerado no momento do envio não sobrevive. - Delimite a chave ao efeito, não à mensagem. Se uma mensagem cria uma fatura e reserva stock, são dois efeitos com duas chaves - caso contrário, uma falha parcial deixa-o incapaz de repetir com segurança qualquer um dos dois.
- Faça expirar as chaves deliberadamente. Um repositório de deduplicação que cresce indefinidamente é uma bomba-relógio; um que expira demasiado cedo reintroduz duplicados. Por defeito, usamos 3× o horizonte máximo de nova tentativa do remetente.
O teste que importa
Todas as integrações que entregamos passam pelo que internamente chamamos o teste do duplo toque: reproduzir todo o registo de mensagens de ontem contra o sistema de hoje, duas vezes, e comparar o estado resultante. Se algo mudou na segunda passagem, o pipeline não está pronto.
É um teste brutal e falha constantemente durante o desenvolvimento - em timestamps, em campos de auditoria «criado por», em contadores de sequência. Cada falha é um ponto onde uma reentrega às 3 da manhã teria silenciosamente corrompido dados. Sai muito mais barato detetá-lo em desenvolvimento do que em produção.