Um convite familiar é gravado no banco e a API tenta enviar o e-mail em seguida. Se o processo cair entre essas etapas, o convite fica pendente sem que o destinatário receba a mensagem. Inverter a ordem pode enviar um convite que nunca chegou a existir no banco.

Grave a intenção junto com o estado

No padrão outbox, a transação que cria o convite também insere um evento com ID próprio em uma tabela de saída. Um worker busca eventos pendentes e entrega cada um ao destino. Se o commit do convite falhar, o evento também desaparece. Se a API cair após o commit, o worker ainda encontra a tarefa.

Com vários workers, uma consulta com FOR UPDATE SKIP LOCKED pode coordenar a reserva de lotes. Se o envio ocorre fora da transação de reserva, registre uma concessão temporária com prazo de expiração. Assim, um segundo worker pode recuperar o evento se o primeiro cair depois de reservá-lo. Use tentativas com atraso e registre o motivo da última falha sem incluir dados sensíveis.

A falha mais importante acontece depois do envio

Se o provedor recebe a mensagem e o worker cai antes de gravar a confirmação, o evento pode ser entregue de novo. Use o ID do evento como chave de idempotência se o destino oferecer esse recurso. Quando o destino não deduplica, a operação precisa tolerar a possibilidade de duplicata.

O teste deve simular a queda depois de enviar e antes de confirmar. Verifique se o evento volta a ficar elegível e se a segunda tentativa conserva a mesma identidade.

PostgreSQL SELECT e SKIP LOCKED · Idempotência em APIs financeiras