Packages
oban
0.6.0
2.23.0
2.22.1
2.22.0
2.21.1
2.21.0
2.20.3
2.20.2
2.20.1
2.20.0
2.19.4
2.19.3
2.19.2
2.19.1
2.19.0
2.18.3
2.18.2
2.18.1
2.18.0
2.17.12
2.17.11
2.17.10
2.17.9
2.17.8
2.17.7
2.17.6
2.17.5
2.17.4
2.17.3
2.17.2
2.17.1
2.17.0
2.16.3
2.16.2
2.16.1
2.16.0
2.15.4
2.15.3
2.15.2
2.15.1
2.15.0
2.14.2
2.14.1
2.14.0
2.13.6
2.13.5
2.13.4
2.13.3
2.13.2
2.13.1
2.13.0
2.12.1
2.12.0
2.11.3
2.11.2
2.11.1
2.11.0
2.10.1
2.10.0
retired
2.9.2
2.9.1
2.9.0
2.8.0
2.7.2
2.7.1
2.7.0
2.6.1
2.6.0
2.5.0
2.4.3
2.4.2
2.4.1
2.4.0
2.3.4
2.3.3
2.3.2
2.3.1
2.3.0
2.2.0
2.1.0
2.0.0
2.0.0-rc.3
2.0.0-rc.2
2.0.0-rc.1
2.0.0-rc.0
1.2.0
1.1.0
1.0.0
1.0.0-rc.2
1.0.0-rc.1
0.12.1
0.12.0
0.11.1
0.11.0
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.1
0.7.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
Robust job processing, backed by modern PostgreSQL, SQLite3, and MySQL.
Current section
Files
Jump to
Current section
Files
lib/oban/worker.ex
defmodule Oban.Worker do
@moduledoc """
Defines a behavior and macro to guide the creation of worker modules.
Worker modules do the work of processing a job. At a minimum they must define a `perform/1`
function, which will be called with an `args` map.
## Defining Workers
Define a worker to process jobs in the `events` queue:
defmodule MyApp.Workers.Business do
use Oban.Worker, queue: "events", max_attempts: 10
@impl true
def perform(%Oban.Job{attempt: attempt}) when attempt > 3 do
IO.inspect(attempt)
end
def perform(args) do
IO.inspect(args)
end
end
The `perform/1` function receives an `Oban.Job` struct as the sole argument. If no clause
matches on `%Oban.Job{}` then the `args` are extracted and `perform/1` is called again with the
args. This allows workers to change the behavior of `perform/1` based on attributes of the Job,
e.g. the number of attempts or when it was inserted.
A job is considered complete if `perform/1` returns a non-error value, and it doesn't raise an
exception or have an unhandled exit.
Any of these return values or error events will fail the job:
* return `{:error, error}`
* return `:error`
* an unhandled exception
* an unhandled exit or throw
As an example of error tuple handling, this worker may return an error tuple when the value is
less than one:
defmodule MyApp.Workers.ErrorExample do
use Oban.Worker
@impl true
def perform(%{value: value}) do
if value > 1 do
:ok
else
{:error, "invalid value given: " <> inspect(value)"}
end
end
end
## Enqueuing Jobs
All workers implement a `new/2` function that converts an args map into a job changeset
suitable for inserting into the database for later execution:
%{in_the: "business", of_doing: "business"}
|> MyApp.Workers.Business.new()
|> MyApp.Repo.insert()
The worker's defaults may be overridden by passing options:
%{vote_for: "none of the above"}
|> MyApp.Workers.Business.new(queue: "special", max_attempts: 5)
|> MyApp.Repo.insert()
See `Oban.Job` for all available options.
## Customizing Backoff
When jobs fail they may be retried again in the future using a backoff algorithm. By default
the backoff is exponential with a fixed padding of 15 seconds. This may be too aggressive for
jobs that are resource intensive or need more time between retries. To make backoff scheduling
flexible a worker module may define a custom backoff function.
This worker defines a backoff function that delays retries using a variant of the historic
Resque/Sidekiq algorithm:
defmodule MyApp.SidekiqBackoffWorker do
use Oban.Worker
@impl true
def backoff(attempt) do
:math.pow(attempt, 4) + 15 + :rand.uniform(30) * attempt
end
@impl true
def perform(args) do
:do_business
end
end
Here are some alternative backoff strategies to consider:
* **constant** — delay by a fixed number of seconds, e.g. 1→15, 2→15, 3→15
* **linear** — delay for the same number of seconds as the current attempt, e.g. 1→1, 2→2, 3→3
* **squared** — delay by attempt number squared, e.g. 1→1, 2→4, 3→9
* **sidekiq** — delay by a base amount plus some jitter, e.g. 1→32, 2→61, 3→135
"""
@moduledoc since: "0.1.0"
alias Oban.Job
@doc """
Build a job changeset for this worker with optional overrides.
See `Oban.Job.new/2` for the available options.
"""
@callback new(args :: Job.args(), opts :: [Job.option()]) :: Ecto.Changeset.t()
@doc """
Calculate the execution backoff, or the number of seconds to wait before retrying a failed job.
"""
@callback backoff(attempt :: pos_integer()) :: pos_integer()
@doc """
The `perform/1` function is called when the job is executed.
The function is passed a job's args, which is always a map with string keys.
The return value is not important. If the function executes without raising an exception it is
considered a success. If the job raises an exception it is a failure and the job may be
scheduled for a retry.
"""
@callback perform(job_or_args :: Job.t() | Job.args()) :: term()
@doc false
defmacro __before_compile__(_env) do
quote do
@impl true
def perform(%Job{args: args}), do: perform(args)
def perform(args) when is_map(args), do: :ok
end
end
@doc false
defmacro __using__(opts) do
quote location: :keep do
alias Oban.{Job, Worker}
@before_compile Worker
@behaviour Worker
@opts unquote(opts)
|> Keyword.take([:queue, :max_attempts])
|> Keyword.put(:worker, to_string(__MODULE__))
@impl Worker
def new(args, opts \\ []) when is_map(args) do
Job.new(args, Keyword.merge(@opts, opts))
end
@impl Worker
def backoff(attempt) when is_integer(attempt) do
Worker.default_backoff(attempt)
end
defoverridable Worker
end
end
@doc false
@spec default_backoff(pos_integer(), non_neg_integer()) :: pos_integer()
def default_backoff(attempt, base_backoff \\ 15) when is_integer(attempt) do
trunc(:math.pow(2, attempt) + base_backoff)
end
end