Changelog#
This release adds a rules-based Pro Pruner, chunk workers for processing jobs in groups, and an Erlang notifier for direct pub/sub without Postgres, alongside fixes for global limits and acks across nodes.
See the v0.7 Upgrade Guide for complete upgrade steps and migration caveats.
Pro Pruner#
The Pro Pruner replaces the standard pruner with rules that retain specific queues, workers, or
states differently, by age or by length. Each job is pruned by the first rule that matches it, and
an implicit "default" rule catches everything else, keeping finished jobs for one day:
[pruner]
schedule = "*/5 * * * *"
[[pruner.rules]]
name = "events"
queue = "events"
max_len = 10_000
[[pruner.rules]]
name = "discarded"
state = "discarded"
max_age = "7 days"
archive = true
Rules run on a cron schedule, set their own limit and timeout, and can archive jobs to
oban_jobs_archive rather than deleting them. Jobs in an active workflow are preserved by
default, so pruning never breaks a running workflow.
Rules are stored in the database and can be inspected and changed at runtime through
oban_pro.pruner, without a restart:
from oban_pro import pruner
await pruner.insert(name="media", queue="media", max_age="1 week")
await pruner.update("media", paused=True)
The tables match Oban Pro for Elixir, so rules can be managed from Oban Web and shared with Elixir nodes.
Chunks#
Chunk workers process jobs together in groups, running when a chunk reaches its size or after a
wait time, e.g. 100 jobs or every second. Declare one with the chunk option and define
process_chunk, which receives the list of jobs:
from oban import worker
from oban_pro import chunk
@worker(queue="messages", chunk={"size": 100, "wait": 1})
class SendMessages:
async def process_chunk(self, jobs):
responses = await messaging.send_batch([job.args for job in jobs])
bad_token = [job for job, resp in zip(jobs, responses) if resp.bad_token]
return chunk.Cancel("bad token", bad_token)
Every job in a chunk completes unless process_chunk says otherwise. Return chunk.Cancel,
chunk.Error, or chunk.Snooze to apply an outcome to particular jobs, or raise to fail the
whole chunk. Chunks are grouped by queue and worker by default, or by args and meta with the by
option, using the same format as queue partitioning.
Erlang Notifier#
The Erlang notifier replaces Postgres LISTEN/NOTIFY with direct pub/sub between processes.
It avoids the 8000 byte payload limit, works through transaction poolers, and keeps notification
traffic off the database:
[notifier]
type = "erlang"
It runs entirely in Python, with no Erlang or Elixir required. Python processes discover each
other through queue producers, and because it speaks the Erlang distribution protocol, it also
joins clusters with Elixir nodes running Oban.Notifiers.PG.
v0.7.0 — 2026-10-22#
Enhancements#
[Core] Support Python 3.15
[Core] Use consistent wording for errors and logs
Validation errors follow a single “x must be y, got z” style, log messages are capitalized, and every logger is named after its module.
[Pruner] Add the rules-based Pro Pruner
Rules prune specific queues, workers, or states by age or length, run on a cron schedule in a configurable timezone, and can archive jobs rather than delete them. Rules are stored in the
oban_prunerstable, reconciled with configuration on start, and managed at runtime throughoban_pro.pruner.[Chunk] Add chunk workers for processing jobs in groups
Chunks run when they reach a size or after a wait time, can be grouped by worker, args, or meta, and apply outcomes to individual jobs with
chunk.Cancel,chunk.Error, andchunk.Snooze.[Notifier] Add an Erlang notifier
A pure Python pub/sub notifier that speaks the Erlang distribution protocol, discovers Python peers automatically, and interoperates with Elixir’s
Oban.Notifiers.PG.
Bug Fixes#
[Producer] Fix global limits being exceeded across nodes
The advisory lock that serializes fetching for queues with a global limit was keyed with Python’s randomized string hash, so each process took a different lock and concurrent fetches across nodes could exceed the limit. The key is now hashed in Postgres, so every node uses the same lock.
[Producer] Retain state when restoring producer records
Producers restored after a database reset keep their global limit, rate limit, and partition state rather than reverting to defaults, preventing duplicate execution and global limit overruns after a crash or failover.
[Query] Prevent stale acks from overwriting current state
Acking a job now requires it to still be executing the same attempt. Previously, a job cancelled while running could be flipped to completed by its ack, and a late ack from a rescued job could overwrite a newer execution on another node.
[Unique] Include suspended jobs in unique states
Suspended jobs didn’t block duplicate inserts. The
incompleteandallgroups now include them, andoban_state_to_bitmatches the function installed by Oban Pro for Elixir.