Toda integração que tira algo do seu sistema tem o mesmo ponto fraco, e é
não o caminho feliz. É a segunda tentativa.
Seu código publica um documento em um provedor. A conexão cai antes que a resposta chegue. Agora você não tem nenhuma identificação de trabalho, nenhuma confirmação e nenhuma ideia se alguma coisa saiu do prédio. O tempo limite informou sobre sua rede. Não lhe disse nada sobre o deles.
Então você tenta novamente e envia a coisa duas vezes ou nunca envia. Escolha um, porque
sem a ajuda da API você não pode ter os dois.
Por que as soluções usuais não resolvem
O primeiro instinto é um guarda do lado do cliente. Faça hash da carga útil, mantenha uma tabela do que você já tem
enviado, verifique-o antes de cada tentativa.
Isso funciona até que a tentativa de duplicação venha de um processo diferente, de um contêiner diferente ou de um
trabalhador de fila iniciado após uma implantação. Sua mesa é o estado local defendendo-se contra uma distribuição
problema.
O segundo instinto é perguntar ao fornecedor se o emprego existe. Isso significa listar empregos recentes
e combinando algo, geralmente o destinatário e uma janela de carimbo de data/hora. É uma pesquisa e
pesquisas têm falsos positivos. Dois envios legítimos para o mesmo destinatário com um minuto de diferença são exatamente iguais a um envio e uma nova tentativa.
O terceiro instinto é tornar a operação naturalmente idempotente. Isso funciona perfeitamente para um upsert de banco de dados e falha completamente para qualquer coisa que saia do seu controle. Um documento que foi transmitido não pode ser destransmitido por uma segunda chamada.
O que realmente resolve
Uma chave de idempotência, gerada pelo chamador, anexada à solicitação e honrada pelo
servidor para uma janela definida.
O contrato é simples. O chamador inventa uma string exclusiva por operação lógica. Cada tentativa
carrega a mesma corda. A primeira solicitação a chegar faz o trabalho e registra o resultado nessa chave. Cada solicitação posterior que transporta essa string obtém o resultado registrado de volta em vez de fazer o trabalho novamente.
A parte importante é onde o estado mora. Está no servidor, que é o único lugar que sabe
se o trabalho existe. Seu loop de nova tentativa deixa de precisar ser inteligente. Só precisa reutilizar a chave.
Stripe popularizou esse padrão e a razão pela qual ele se espalhou é que a alternativa é ilimitada.
Sem ele, cada chamador grava mal sua própria desduplicação, e a falha só aparece sob carga.
APIs de documentos e mensagens são onde mais importa, porque o efeito colateral é visível para
alguém fora da sua empresa. Fax para Agentes é um exemplo atual do padrão adotado
seriamente. Requer uma Chave de Idempotência em cada chamada mutante, portanto, uma nova tentativa devolve o
mesmo trabalho em vez de criar um segundo.
Os estados que ninguém modela adequadamente
A idempotência resolve a duplicata. Não resolve a outra metade, que é que você ainda não sabe o que aconteceu.
A maioria das integrações modela isso como um booleano. Enviado ou não enviado. Isso está errado, e você descobre isso
a primeira vez que uma transmissão é concluída pela metade.
Uma máquina de estado útil para um envio tem mais espaço. Fax for Agents publica seis estados:
enfileirado, ativo, parcial, com falha, desconhecido e entregue. Dois deles são os interessantes.
desconhecido significa que o provedor não disse. Não falhou, não foi entregue. A resposta correta para
desconhecido é esperar. Um loop de nova tentativa que trata o desconhecido como falha precisa ser reescrito, porque isso é
como você produz duplicatas mesmo com idempotência em vigor.
parcial significa que parte dele chegou. Reenviar o documento inteiro entregaria as primeiras páginas
duas vezes, então parcial é um estado que nunca deve ser retransmitido automaticamente. Precisa de uma pessoa, ou de um
política escrita antecipadamente por uma pessoa.
E entregue deve ser a palavra mais restrita do vocabulário. Nessa API é escrito apenas quando
as evidências do provedor para a tentativa a apoiam, nunca quando a solicitação foi meramente
aceito. A lacuna entre “aceitamos seu trabalho” e “ele chegou” é onde a maioria dos falsos
a confiança vive.
O que perguntar a qualquer API da qual você está prestes a depender
Quatro perguntas, respondidas na documentação antes de você escrever uma linha de código.
Ele aceita uma string de idempotência fornecida pelo chamador em cada chamada mutante e por quanto tempo
essa corda aguenta?
Quantos estados terminais um trabalho possui? Se a resposta for dois, a API está escondendo algo
de você.
Existe um estado que significa “ainda não sabemos” e a documentação diz o que você deve fazer quando o vir?
Quando um webhook chega, você pode verificar se ele veio deles? A entrega assinada com um ID de evento é
a diferença entre um manipulador de webhook e um endpoint aberto.
Nada disso é exótico. É a diferença entre uma integração que degrada e outra que
duplica silenciosamente.




