Polls the outbox_events table for undispatched events and fans them
out to subscribers.
Invoked from Outbox.Ticker every ~5s via Task.Supervisor.start_child/2.
Each invocation:
- Opens a transaction
- Selects up to
@batch_sizeundispatched events ordered byidwithFOR UPDATE SKIP LOCKED(multi-node safe; concurrent invocations on the same node are also safe) - For each event, looks up subscribers via
Outbox.Registry.lookup/1and enqueues oneOutbox.SubscriberJobper subscriber into theOutbox.Config.oban/0instance - Marks all processed events
dispatched_at = utc_now()
An event with no registered subscribers is still marked dispatched (and a debug log is emitted). This prevents re-scanning on every tick.
Failures don't auto-retry — the next tick (5s later) is the retry. This is appropriate for a "drain the outbox" loop with no per-tick state to recover.