Outboxer.Relay.Oban (Outboxer v0.1.0)

Copy Markdown View Source

Relays messages into Oban as jobs.

This module only exists when :oban is in your dependencies.

{Outboxer,
 repo: MyApp.Repo,
 outboxes: [
   emails: [relay: {Outboxer.Relay.Oban, worker: MyApp.Workers.SendEmail, job_opts: [queue: :mailers]}]
 ]}

Options

  • :worker - Required. The Oban.Worker module that handles the message, or a function taking the envelope and returning one.

  • :oban (atom/0) - Name of the Oban instance to insert into. The default value is Oban.

  • :payload - How the payload is placed in the job args under "payload". :json decodes it (use with the default message encoding), :raw passes the string through, :base64 encodes binary payloads. The default value is :json.

  • :args - A function taking the envelope and returning the job args map. Replaces the default args entirely. The default value is nil.

  • :job_opts - Options for Oban.Worker.new/2 (:queue, :priority, :max_attempts, ...), or a function taking the envelope and returning them. The default value is [].

Job args

Unless you pass :args, a job gets:

%{
  "id" => "emails-42",            # Outboxer.Envelope.dedup_id/1
  "type" => "MyApp.Events.Welcome",
  "group_key" => "user:7",        # or nil
  "headers" => %{},
  "payload" => %{"user_id" => 7}  # see the :payload option
}

Do you need an outbox in front of Oban?

Often you do not. Oban.insert/2 inside your own transaction is already transactional when Oban uses the same repo, and it is one moving part fewer. Reach for this relay when:

  • Oban lives in a different database than the business data, so the job insert cannot join your transaction.
  • You want jobs enqueued in order per group (see below).
  • Your code publishes domain messages and should not know which of them end up as jobs, in Kafka, or both.

Guarantees

When Outboxer and Oban use the same Postgres repo, the job insert joins the batch transaction: jobs are inserted and outbox rows removed atomically, so a message becomes exactly one job. In every other setup (different repos, SQLite) the handoff is at-least-once and a job may be inserted twice; add an Oban unique constraint on the "id" arg through :job_opts if that matters.

Ordering

Outboxer inserts jobs in order, so job ids follow message order within a group. Oban executes them in that order only when the queue runs one job at a time (queues: [mailers: 1]) on a single node, or with Oban Pro's partitioned queues. With ordinary concurrency, jobs run in parallel.