Absolutely! Here’s a condensed and comprehensive list of the breaking changes, changes, additions, fixes and removals between 1.2 and 2.0:
-
[Oban.Worker] The perform/2 callback is replaced with perform/1, where the only argument is an Oban.Job struct. This unifies the interface for all Oban.Worker callbacks and helps to eliminate confusion around pattern matching on arguments.
To migrate change all worker definitions from accepting an args map and a job struct:
def perform(%{"id" => id}, _job), do: IO.inspect(id)
To accept a single job struct and match on the args key directly:
def perform(%Job{args: %{"id" => id}}), do: IO.inspect(id)
-
[Oban.Worker] The backoff/1 callback now expects a job struct instead of an integer. That allows applications to finely control backoff based on more than just the current attempt number. Use of backoff/1 with an integer is no longer supported.
To migrate change any worker definitions that used a raw attempt like this:
def backoff(attempt), do: attempt * 60
To match on a job struct instead, like this:
def backoff(%Job{attempt: attempt}), do: attempt * 60
-
[Oban.Config] The :verbose setting is renamed to :log. The setting started off as a simple boolean, but it has morphed to align with the log values accepted by calls to Ecto.Repo.
To migrate, replace any :verbose declarations:
config :my_app, Oban,
verbose: false,
...
With use of :log instead:
config :my_app, Oban,
log: false,
...
-
[Oban] The interface for start_queue/3 is replaced with start_queue/2 and stop_queue/2 no longer accepts a queue name as the second argument. Instead, both functions now accept a keyword list of options. This enables the new local_only flag, which allows you to dynamically start and stop queues only for the local node.
Where you previously called start_queue/2,3 or stop_queue/2 like this:
:ok = Oban.start_queue(:myqueue, 10)
:ok = Oban.stop_queue(:myqueue)
You’ll now them with options, like this:
:ok = Oban.start_queue(queue: :myqueue, limit: 10)
:ok = Oban.stop_queue(queue: :myqueue)
Or, to only control the queue locally:
:ok = Oban.start_queue(queue: :myqueue, limit: 10, local_only: true)
:ok = Oban.stop_queue(queue: :myqueue, local_only: true)
-
[Oban] Replace drain_queue/3 with drain_queue/2, which now has an interface consistent with the other *_queue/2 operations.
Where you previously called drain_queue/2,3 like this:
Oban.drain_queue(:myqueue, with_safety: false)
You’ll now it with options, like this:
Oban.drain_queue(queue: :myqueue, with_safety: false)
-
[Oban] The interface for pause_queue/2, resume_queue/2 and scale_queue/3 now matches the recently changed start_queue/2 and stop_queue/2. All queue manipulation functions now have a consistent interface, including the ability to work in :local_only mode.
-
[Oban.Telemetry] The format for telemetry events has changed to match the new telemetry span convention. This listing maps the old event to the new one:
-
[:oban, :started] -> [:oban, :job, :start]
-
[:oban, :success] -> [:oban, :job, :stop]
-
[:oban, :failure] -> [:oban, :job, :exception]
-
[:oban, :trip_circuit] -> [:oban, :circuit, :trip]
-
[:oban, :open_circuit] -> [:oban, :circuit, :open]
In addition, for exceptions the stacktrace meta key has changed from :stack to the standardized :stacktrace.
-
[Oban.Beat] Pulse tracking and periodic job rescue are no longer available. Pulse tracking and rescuing will be handled by an external plugin. This is primarily an implementation detail, but it means that jobs may be left in the executing state after a crash or forced shutdown.
Remove any :beats_maxage, :rescue_after or :rescue_interval settings from your config.
-
[Oban.Plugins.Pruner] Built in pruning is handled by the new plugin system. A fixed period pruning module is enabled as a default plugin. The plugin allows light configuration through a max_age value. For customizable per-queue, per-worker or per-state pruning see the DynamicPruner available in Oban Pro.
Remove any :prune, :prune_interval or prune_limit settings from your config. To disable the pruning plugin in test mode set plugins: false instead.
Replace any use of :prune, :prune_interval or :prune_limit in your config and pass a max_age value to the plugin:
config :my_app, Oban,
plugins: [{Oban.Plugins.Pruner, max_age: 60}]
...
-
[Oban.Scheduler] Ensure isolation between transaction locks in different prefixes. A node with multiple prefix-isolated instances (i.e. “public” and “private”) would always attempt to schedule cron jobs at the same moment. The first scheduler would acquire a lock and block out the second, preventing the second scheduler from ever scheduling jobs.
-
[Oban.Query] Correctly prefix unprepared unique queries. Unique queries always targeted the “public” prefix, which either caused incorrect results when there were both “public” and an alternate prefix. In situations where there wasn’t a public oban_jobs table at all it would cause cryptic transaction errors.
-
[Oban.Query] Wrap all job fetching in an explicit transaction to enforce FOR UPDATE SKIP LOCKED semantics. Prior to this it was possible to run the same job at the same time on multiple nodes.
-
[Oban.Crontab] Fix weekday matching for Sunday, which is represented as 0 in crontabs.
-
[Oban.Crontab.Cron] Do not raise an ArgumentError exception when the crontab configuration includes a step of 1, which is a valid step value.
-
[Oban.Breaker] Prevent connection bomb when the Notifier experiences repeated disconnections.
-
[Oban.Telemetry] Correctly record timings using native time units, but log them using microseconds. Previously they used a mixture of native and microseconds, which yielded inconsistent values.
-
[Oban.Telemetry] Stop logging the :error value for circuit trip events. The error is a struct that isn’t JSON encodable. We include the normalized Postgrex / DBConnection message already, so the error is redundant.
-
[Oban.Worker] Support returning {:snooze, seconds} from perform/1 to re-schedule a job some number of seconds in the future. This is useful for recycling jobs that aren’t ready to run yet, e.g. because of rate limiting.
-
[Oban.Worker] Support returning :discard from perform/1 to immediately discard a job. This is useful when a job encounters an error that won’t resolve with time, e.g. invalid arguments or a missing record.
-
[Oban.Job] Introduce a virtual unsaved_error field, which is populated with an error map after failed execution. The unsaved_error field is set before any calls to the worker’s backoff/1 callback, allowing workers to calculate a custom backoff depending on the error that failed the job.
-
[Oban.Worker] Add :infinity option for unique period.
-
[Oban] Bubble up errors and exits when draining queues by passing with_safety: false as an option to Oban.drain_queue/3.
-
[Oban] Add Oban.cancel_job/2 for safely discarding scheduled jobs or killing executing jobs. This deprecates kill_job/2, which isn’t as flexible.
-
[Oban.Telemetry] Add span/3 for reporting normalized :start, :stop and :exception events with timing information.
-
[Oban.Telemetry] Include the configured prefix in all event metadata. This makes it possible to identify which schema prefix a job ran with, which is useful for differentiating errors in a multi-tenant system.
-
[Oban.Telemetry] Include queue_time as a measurement with stop and exception events. This is a measurement in milliseconds of the amount of time between when a job was scheduled to run and when it was last attempted.
-
[Oban.Testing] Add perform_job/2,3 helper to automate validating, normalizing and performing jobs while unit testing. This is now the preferred way to unit test workers.
To update your tests replace any calls to perform/1,2 with the new Oban.Testing.perform_job/2,3 helper:
defmodule MyApp.WorkerTest do
use MyApp.DataCase, async: true
use Oban.Testing, repo: MyApp.Repo
alias MyApp.Worker
test "doing business in my worker" do
assert :ok = perform_job(Worker, %{id: 1})
end
end
The perform_job/2,3 helper will verify the worker, the arguments and any provided options. It will then verify that your worker returns a valid result and return the value for you to assert on.
-
[Oban.Crontab] Add support for non-standard expressions such as @daily, @hourly, @midnight, and @reboot
-
[Oban.Crontab] Add support for using step values in conjunction with ranges, enabling expressions like 10-30/2, 15-45/3, etc.
-
[Oban.Telemetry] Include job queue_time in the default logger output.
-
[Oban.Telemetry] Add new :producer events for descheduling and dispatching jobs from queue producers.