Exactly-once execution guard for at-least-once delivery.
Subscribers receive each event at least once, so a retried job can
run handle_event/3 more than once. Wrap the side-effect in
run_once/3 to make duplicate deliveries a no-op:
def handle_event(name, payload, meta) do
Outbox.Idempotency.run_once(__MODULE__, meta.event_id, fn ->
Repo.insert(%AuditLog{action: name, ...})
end)
endThe guard claims (consumer, event_id) in the outbox_consumed_events
table and runs fun only if the claim was newly inserted. The claim and
fun run in one transaction: if fun returns {:error, _} or raises,
the claim is rolled back so the next delivery re-runs it. This requires
the outbox_consumed_events table (see mix outbox.gen.migration).
Summary
Functions
Run fun at most once per (consumer, event_id).
Functions
Run fun at most once per (consumer, event_id).
Returns:
{:ok, :already_processed}— this(consumer, event_id)was claimed by a prior successful run;funis not called.- whatever
funreturns (:ok,{:ok, value}) — on the first run. {:error, reason}—funreturned an error; the claim is rolled back so a retry re-runs it.
A raise inside fun propagates (after rolling back the claim).