Oban — Reliable and Observable Job Processing

will it be available next month ? when it goes opensource?

Oban Web will be open source, Oban Pro will not be. Worker aliases are only part of Pro.

2 Likes

expected date?

See this thread for more details about Oban Web being open sourced: Oban Web to be open sourced

6 Likes

Oban v2.19 is out!

The minimum Elixir version is now v1.15. The official policy is to only support the three latest versions of Elixir.

:dolphin: MySQL Support

Oban officially supports MySQL with the new Dolphin engine. Oban supports modern (read “with full JSON support”) MySQL versions from 8.4 on, and has been tested on the highly scalable Plantescale database.

Running on MySQL is as simple as specifying the Dolphin engine in your configuration:

config :my_app, Oban,
  engine: Oban.Engines.Dolphin,
  queues: [default: 10],
  repo: MyApp.Repo

With this addition, Oban can run in estimated 10% more Elixir applications!

:alembic: Automated Installer

Installing Oban into a new application is simplified with a new igniter powered mix task. The new oban.install task handles installing and configuring a standard Oban installation, and it will deduce the correct engine and notifier automatically based on the database adapter.

mix igniter.install oban

This oban.install task is currently the recommended way to install Oban. As a bonus, the task composes together with other igniter installers, making it possible to install phoenix, ash, oban, and other packages with a single command:

mix igniter.install phoenix ash_phoenix ash_postgres ash_oban

Look at the Mix.Oban.Install docs for full usage and options.

:notebook_with_decorative_cover: Logging Enhancements

Logging in a busy system may be noisy due to job events, but there are other events that are particularly useful for diagnosing issues. A new events option for attach_default_logger/1 allows selective event logging, so it’s possible to receive important notices such as notifier connectivity issues, without logging all job activity:

Oban.Telemetry.attach_default_logger(events: ~w(notifier peer stager)a)

Along with filtering, there are new events to make diagnosing operational problems easier.

A peer:election events logs leadership changes to indicate when nodes gain or lose leadership. Leadership issues are rare, but insidious, and make diagnosing production problems especially tricky.

[
  message: "peer became leader",
  source: "oban",
  event: "peer:election",
  node: "worker.1",
  leader: true,
  was_leader: false
]

Helpfully, plugin:stop events are now logged for all core plugins via an optional callback, and plugin:exception events are logged for all plugins regardless of whether they implement the callback. Runtime information is logged for Cron, Lifeline, Pruner, Stager, and Reindexer plugins.

For example, every time Cron runs successfully it will output details about the execution time and all of the inserted job ids:

[
  source: "oban",
  duration: 103,
  event: "plugin:stop",
  plugin: "Oban.Plugins.Cron",
  jobs: [1, 2, 3]
]

:sailboat: Official JSON

Oban will default to using the official JSON module built into Elixir v1.18+ when available.

A new Oban.JSON module detects whether the official Elixir JSON module is available at compile time. If it isn’t available, then it falls back to Jason, and if Jason isn’t available (which is extremely rare) then it warns about a missing module.

This approach was chosen over a config option for backward compatibility because Oban will only support the JSON module once the minimum supported Elixir version is v1.18.

v2.19.0 — 2025-01-16

Enhancements

  • [Oban] Start all queues in parallel on initialization.

    The midwife now starts queues using an async stream to parallelize startup and minimize boot time for applications with many queues. Previously,

  • [Oban] Safely return nil from check_queue/2 when checking queues that aren’t running.

    Checking on a queue that wasn’t currently running on the local node now returns nil rather than causing a crash. This makes it safer to check the whether a queue is running at all without a try/catch clause.

  • [Oban] Add check_all_queues/1 to gather all queue status in a single function.

    This new helper gathers the “check” details from all running queues on the local node. While it was previously possible to pull the queues list from config and call check_queue/2 on each entry, this more accurately pulls from the registry and checks each producer concurrently.

  • [Oban] Add delete_job/2 and delete_all_jobs/2 operations.

    This adds Oban.delete_job/2, Oban.delete_all_jobs/2, Engine callbacks, and associated operations for all native engines. Deleting jobs is now easier and safer, due to automatic state protections.

  • [Engine] Record when a queue starts shutting down

    Queue producer metadata now includes a shutdown_started_at field to indicate that a queue isn’t just paused, but is actually shutting down as well.

  • [Engine] Add rescue_jobs/3 callback for all engines.

    The Lifeline plugin formerly used two queries to rescue jobs—one to mark jobs with remaining attempts as available and another that discarded the remaining stuck jobs. Those are now combined into a single callback, with the base definition in the Basic engine.

    MySQL won’t accept a select in an update statement. The Dolphin implementation of rescue_jobs/3 uses multiple queries to return the relevant telemetry data and make multiple updates.

  • [Cron] Introduce Oban.Cron with schedule_interval/4

    The new Cron module allows processes, namely plugins, to get cron-like scheduled functionality with a single function call. This will allow plugins to removes boilerplate around parsing, scheduling, and evaluating for cron behavior.

  • [Registry] Add select/1 to simplify querying for registered modules.

  • [Testing] Add build_job/3 helper for easier testing.

    Extract the mechanism for verifying and building jobs out of perform_job/3 so that it’s usable in isolation. This also introduces perform_job/2 for executing built jobs.

  • [Telemetry] Add information on leadership changes to oban.peer.election event.

    An additional was_leader? field is included in [:oban, :peer, :election | _] event metadata to make hooking into leadership change events simpler.

  • [Telemetry] Add callback powered logging for plugin events.

    Events are now logged for plugins that implement the a new optional callback, and exceptions are logged for all plugins regardless of whether they implement the callback.

    This adds logging for Cron, Lifeline, Pruner, Stager, and Reindexer.

  • [Telemetry] Add peer election logging to default logger.

    The default logger now includes leadership events to make identifying the leader, and leadership changes between nodes, easier.

  • [Telemetry] Add option to restrict logging to certain events.

    Logging in a busy system may be noisy due to job events, but there are other events that are particularly useful for diagnosing issues. This adds an events option to attach_default_logger/1 to allow selective event logging.

  • [Telemetry] Expose default_handler_id/0 for telemetry testing.

    Simplifies testing whether the default logger is attached or detached in application code.

Chores

  • [Peer] The default database-backed peer was renamed from Postgres to Database because it is also used for MySQL databases.

Bug Fixes

  • [Oban] Allow overwriting all insert/* functions arities after use Oban.

  • [Node] Correctly handle :node option for scale_queue/2

    Scoping scale_queue/2 calls to a single node didn’t work as advertised due to some extra validation for producer meta compatibility.

  • [Migration] Fix version query for databases with non-unique oid

    Use pg_catalog.obj_description(object_oid, catalog_name), introduced in PostgreSQL 7.2, to specify the pg_class catalog so only the oban_jobs description is returned.

  • [Pruner] Use state specific fields when querying for prunable jobs.

    Using scheduled_at is not correct in all situations. Depending on job state, one of cancelled_at, discarded_at, or scheduled_at should be used.

  • [Peer] Conditionally return the current node as leader for isolated peers.

    Prevents returning the current node name when leadership is disabled.

  • [Testing] Retain time as microseconds for scheduled_at tests.

    Include microseconds in the begin and until times used for scheduled_at tests with a delta. The prior version would truncate, which rounded the until down and broke microsecond level checks.

  • [Telemetry] Correct spelling of “elapsed” in oban.queue.shutdown metadata.

9 Likes

Oban v2.20.0 is out!

This release brings a fantastic new helper function, an optional migration to aid pruning, some stability improvements, and a bevy of documentation updates.

:butterfly: Update Job

This introduces the Oban.update_job/2,3 function to simplify updating existing jobs while ensuring data consistency and safety. Previously, updating jobs required manually constructing change operations or complex queries that could lead to race conditions or invalid state changes.

Only a curated subset of job fields, e.g. :args, :max_attempts, :meta, etc. may be updated and they use the same validation rules as insertion to prevent invalid data. Updates are also wrapped in a transaction with locking clauses to prevent concurrent modifications.

The function supports direct map changes:

Oban.update_job(job, %{priority: 0, tags: ["urgent"]})

It also has a convenient function-based mode for dynamic changes:

Oban.update_job(job, fn job -> 
  %{meta: Map.put(job.meta, "processed_by", current_node())} 
end)

:snowflake: Unique State Groups

There are now named unique state groups to replace custom state lists for unique jobs, promoting better uniqueness design and reducing configuration errors.

Previously, developers had to manually specify lists of job states for uniqueness, which was error-prone and could lead to subtle bugs when states were omitted or incorrectly combined. The new predefined groups ensure correctness and consistency across applications.

The new state groups are:

  • :all - All job states
  • :incomplete - Jobs that haven’t finished (~w(available scheduled executing retryable)a)
  • :scheduled - Only scheduled jobs ([:scheduled])
  • :successful - Jobs that completed successfully (~w(available scheduled executing retryable completed)a)

These groups eliminate the risk of accidentally creating incomplete or incorrect state lists that could allow duplicate jobs to be created when they shouldn’t be, or prevent valid job creation when duplicates should be allowed.

:nest_with_eggs: Nested Plugin Supervision

Plugins and the internal Stager are now nested within a secondary supervision tree to improve system resilience and stability.

Previously, plugins were supervised directly under the main Oban supervisor alongside core process. This meant that plugin failures could potentially impact the entire Oban system, and frequent plugin restarts could trigger cascading failures in the primary supervision tree.

The new supervisor has more lenient restart limits to allow for more plugin restart attempts before giving up. This change makes Oban more robust in production environments where plugins may experience transient failures due to database or connectivity issues.

v2.20.0 — 2025-08-13

Enhancements

  • Migration Add V13 migration for indexing cancelled and discarded states.

    A new V13 migration adds compound indexes to significantly improve Oban.Plugins.Pruner performance when cleaning up discarded and cancelled jobs. This is especially beneficial for applications that process large volumes of jobs and retain them for extended periods.

  • Repo Expose dynamic repo switching as with_dynamic_repo/2

    The function was previously internal, which made impossible to use in external modules or extend upon. Now custom plugins and extensions can use Repo.with_dynamic_repo/2 to use the configured dynamic repo options.

Bug Fixes

  • [Oban] Allow insert_all/1,3 via Oban facade

    The insert_all/1 and insert_all/3 function variants were missing from the generated Oban facade functions when using a named instance.

  • [Testing] Generate correct perform_job/1,2,3 clauses.

    The perform_job/2,3 clauses generated by use Oban.Testing didn’t handle the perform_job/2 variant designed to run jobs created with build_job/3. This caused test failures when trying to execute jobs built using the build_job/3 helper function.

    The fix generates the missing perform_job/2 clause along with a convenient perform_job/1 variant, ensuring all testing scenarios work seamlessly regardless of how jobs are constructed.

  • [Testing] Restrict inline execution to available and scheduled states.

    Jobs in the completed state or other non-runnable states were incorrectly attempted by the inline engine, potentially causing errors or unexpected behavior during testing.

  • [Worker] Disallow :keys when :fields doesn’t contain :args or :meta

    Unique job configurations using :keys were allowed even when :fields didn’t include :args or :meta, which would result in runtime errors since keys can only extract values from these keyable fields.

  • [Cron] Fix error message when the crontab has an invalid range.

    Cron validation errors for invalid ranges were returning exception structs instead of readable error messages, making it difficult to understand and fix crontab configuration issues.

4 Likes

Will oban be waiting for stable release of 1.19 elixir, or there will be a support for 1.19 rc?
I am facing deadlock during 1.19rc compilation with oban. :slight_smile:

There won’t be any changes to Oban for the Elixir v1.19 because the deadlock was caused by a compiler issue, and not anything Oban was doing. They’re working on it still, and it should be fixed with the next RC.

3 Likes

the deadlock was caused by a compiler issue, and not anything Oban was doing. They’re working on it still, and it should be fixed with the next RC.

You are very active in elixir community. :sweat_smile: :smiley:
Thanks , kudos :+1:

A post was split to a new topic: ArithmeticError from globally limited queue

hey, loving the changes. already used update_job.

the Unique State Groups got me confused though. here’s what i thought when i saw these groups initially, this is just for your reference on how a users thought process went:

  • all: all (not confusing)
  • incomplete: executing, maybe retryable
  • scheduled: scheduled (not confusing)
  • successful: completed (how is ‘available’ or ‘scheduled’ or ‘executing’ completed?)

the confusion is one issue, but actually i found myself still defining by specific states, because these groups don’t cover what i actually want to use this for:

  • run all jobs again that had trouble (lets say i fixed a bug): retryable, discarded.
  • simple unique: everything but completed and discarded. this is what i use most often actually. i want jobs to keep running (uniquely), unless there’s a problem.
  • replace related: i have a job that runs periodically, but the user can run manually also. i don’t want them to be able to replace a job that’s executing or completed, in those cases scheduling a new one is fine. i mean ideal would be that it schedules when completed only and not even schedule when executing, but for my usecase either is fine.

just thought i’d let you know how a laycoder thinks :slight_smile:

The description for successful is “Jobs that aren’t cancelled or discarded (the default)”, which isn’t the most descriptive name, but it’s meant to be succinct (unlike :everything_but_cancelled_or_discarded).

It’s also the default, and doesn’t need to be explicitly stated in unique config.

That’s fine, provided you understand how uniqueness works, e.g. concurrency vs uniqueness.