Adds a job querying API, migration checking in test mode, smarter notifier ping cadence, and a handful of bug fixes around recovery and resilience.
๐ Job Querying
Two new functions make it easier to load jobs without hand-rolling Ecto queries. Oban.Job.query/1 builds a composable query from a keyword list of field filters, and Oban.all_jobs/2 runs any queryable through the configured repo.
For example, to fetch every available job for a worker with account_id: 1:
[args: %{account_id: 1}, worker: MyApp.Worker, state: :available]
|> Oban.Job.query()
|> Oban.all_jobs()The result is an Ecto.Queryable, so it composes with further Ecto.Query calls, and pairs naturally with with Oban.cancel_all_jobs/2 and Oban.delete_all_jobs/2:
[state: :available, queue: :media]
|> Oban.Job.query()
|> limit(10)
|> Oban.cancel_all_jobs()๐ก Smarter Sonar Pings
Sonar, the notifier monitor, used to ping every 5 seconds from every node. That resulted in 120 messages a minute to confirm connectivity, even in a stable environment.
Pings are now status-aware and back off when the cluster is quiet. A solitary node settles to one ping a minute. A clustered group scales the per-node interval with peer count, so aggregate traffic stays roughly constant as the cluster grows.
There is no configuration required. When a node joins, drops, or recovers, pings snap back to the fast cadence immediately.
๐งช Migration Checks at Startup
When Oban boots in a testing mode, it now verifies that the oban_jobs table exists and that the migration version is current. A missing or outdated migration fails fast with an actionable message instead of surfacing later as cryptic database errors mid-test.
For example, forgetting to run the v2.21 migration after upgrading would previously fail somewhere deep in a test with an enum mismatch on the new suspended state. Now Oban refuses to start:
** (RuntimeError) Oban migrations are outdated. Found version 12, but version 13 is required.
Run migrations to update:
mix ecto.migrate
The check is limited to testing mode and geared toward catching migration requirements before they hit production.
v2.22.0 โ 2026-04-27
Enhancements
-
[Oban] Add
Oban.all_jobs/2andOban.Job.query/1Introduce
Oban.Job.query/1, a keyword-based builder that composes Ecto queryable from a small set of field filters. Scalar values become equality matches, lists becomeINmatches, atoms are coerced, andargsormetaare compiled to a containment check.That pairs with
Oban.all_jobs/2, a thin function that runs any queryable through the configured repo. -
[Oban] Verify migrations at startup in testing mode
When Oban starts in a testing mode, it now verifies that migrations have been run and are up to date. This catches migration issues early in CI rather than failing with confusing database errors during test execution or worse, in production.
For Postgres, the check verifies the migration version is current, while for SQLite and MySQL, the check verifies the
oban_jobstable exists. -
[Sonar] Reduce ping rate with status-aware intervals
Sonar previously pinged every 5s regardless of cluster state, which is aggressive for systems where nothing is changing. It now walks between a min and max interval, resetting on any status change and otherwise backing off toward a status driven target:
:clusteredscales with peer count so aggregate traffic stays ~1 ping permin_intervalregardless of cluster size:solitarydrifts to the max interval, since its only job is verifying the notifier channel:isolatedand:unknownstay atmin_intervalto keep recovery probes
Stale-node pruning now uses an absolute window (default 120s) instead of a multiple of the current interval, and scheduled pings are jittered to avoid synchronizing nodes.
-
[Repo] The Repo retry behavior is now compile-time configurable, partially for testing purposes, but also to allow tweaking the internal retry behavior based on system requirements.
-
[Repo] Allow disabling
transaction/3retriesPass
retry: 0orretry: falseto skip retries entirely, including for expected conflicts like deadlocks and serialization failures. This is intended for callers invoking queries likeOban.insert/2from within an existing transaction, where a retry inside a savepoint would mask the real error from the outer transaction.
Bug Fixes
-
[Stager] Notify queues regardless of staging success
When
stage_jobs/3raises a non-recoverable exception (e.g. a unique constraint violation), the wrapping transaction rolls back and the queue notification never fires. Pre-existingavailablejobs would then sit until the stager either recovered or the cluster was restarted.The stager now falls back to notifying queues outside the failed transaction, preferring the configured global path so the whole cluster is reached, and dropping to a local registry notify if the database itself is unreachable.
-
[Testing] Include suspended jobs in test helper queries
The query used by test helpers like
all_enqueued/0,1now includessuspendedjobs in addition toavailableandscheduledstates. -
[Repo] Tolerate unavailable Repo modules from all operations
In development using containers, where recompiles take several seconds, the repo module can be purged and not-yet-reloaded for long enough that multiple
Stagerticks occur. Each tick may crash withUndefinedFunctionError, blowing through the supervisor's restart quota.The Repo now rescues
UndefinedFunctionErroralongside the existing connection errors. -
[Notifier] Fix duplicate pid accumulation in Postgres notifier
Registering from the same process multiple times would accumulate duplicate pids in the Postgres notifier. Listening was deduplicated, but process registration wasn't.