O celular envia um lançamento, o servidor grava a despesa e a conexão cai antes da resposta. Para o aplicativo, o resultado é desconhecido. Repetir o POST é necessário para recuperar a operação, mas pode duplicar o lançamento se a API tratar cada tentativa como uma compra diferente.
A chave representa a intenção
Gere uma chave por intenção de salvar e preserve-a até obter uma resposta conclusiva. Use uma restrição única por usuário, operação e chave no PostgreSQL. Associe a chave ao resultado e a uma representação estável dos dados recebidos. A criação da despesa e o registro do resultado precisam participar da mesma transação.
Em um fluxo com INSERT ON CONFLICT DO NOTHING, a tentativa que inseriu a chave pode continuar com a gravação. No nível Read Committed, a outra tentativa pode consultar o resultado com uma instrução separada após o conflito. Se a primeira transação abortar, a inserção concorrente pode assumir a operação. Se a chave já gravada vier acompanhada de dados diferentes, responda com o conflito definido no contrato da API.
O caso dos dois cafés
Duas compras de R$ 8 com a mesma descrição podem ser legítimas. Elas recebem chaves distintas. Já um reenvio da mesma compra mantém a chave original, mesmo se o aplicativo tiver sido reiniciado. Defina por quanto tempo o servidor conserva as chaves e o que o cliente faz quando uma tentativa pendente ultrapassa esse prazo.
No teste de integração, force duas requisições concorrentes e confirme que apenas uma despesa existe e que ambas recuperam o mesmo identificador.
PostgreSQL e o isolamento de transações · Isolamento entre famílias e organizações