<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Oban Pro Releases</title>
  <subtitle>Oban Pro release announcements</subtitle>
  <id>https://oban.pro/</id>
  <link rel="alternate" type="text/html" href="https://oban.pro" />
  <link rel="self" type="application/atom+xml" href="https://oban.pro/releases/feed" />
  <updated>2026-04-04T00:00:00Z</updated>
  <author>
    <name>Oban</name>
  </author>
  
    <entry>
      <title>Oban Pro v1.7.0-rc.3</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.7.0-rc.3" />
      <id>https://oban.pro/releases/pro/1.7.0-rc.3</id>
      <published>2026-04-04T00:00:00Z</published>
      <updated>2026-04-04T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Enhancements</h3>
<ul>
<li>
<p>[Workflow] Optimize workflow flush query for pathological cases</p>
<p>Overhaul workflow flushing query to avoid lateral sub-queries, minimize buffer hits, avoid
repeated TOAST reads, and use a more efficient hash join for same-workflow dependencies.</p>
<p>For a 10_000-&gt;1 fan-in workflow the result is 31x faster, with 222x fewer buffer hits.</p>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Pro] Fix bootstrap file checking on Windows</p>
<p>File checks failed on Windows because Mix copies files to <code>_build</code> instead of symlinking. The
NIF was navigating from <code>priv_dir</code> up to find source files, which only worked when symlinks
resolved to deps.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.7.0-rc.2</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.7.0-rc.2" />
      <id>https://oban.pro/releases/pro/1.7.0-rc.2</id>
      <published>2026-03-31T00:00:00Z</published>
      <updated>2026-03-31T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Enhancements</h3>
<ul>
<li>
<p>[Refresh] Gracefully check for hex availability</p>
<p>Ensure hex is available before attempting to update in the <code>oban_pro.refresh</code> task.</p>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Pro] Update app modules list after compiling sources</p>
<p>Encrypted modules handled by the <code>:oban_pro</code> compiler weren't included in the <code>.app</code> file
because it was generated before they were compiled. Releases use the modules list to determine
what to load at boot, causing releases to crash with <code>UndefinedFunctionError</code> for encrypted
modules.</p>
</li>
<li>
<p>[Pro] Fix Windows NIF loading in bootstrap module</p>
<p>On Windows, BEAM passes NIF function pointers via a callback table at load time instead of
resolving them through dynamic linking. The bootstrap module now properly defines, resolves, and
casts callbacks for cross-platform operation.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.7.0-rc.1</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.7.0-rc.1" />
      <id>https://oban.pro/releases/pro/1.7.0-rc.1</id>
      <published>2026-03-26T00:00:00Z</published>
      <updated>2026-03-26T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Migration] Stop adding <code>suspended</code> state to the <code>oban_jobs_state</code> enum</p>
<p>The <code>suspended</code> state must be added by Oban's v14 migration prior to running the Pro 1.7.0
migration. Postgres prohibits altering a type and referencing it within the same transaction,
and the upgrade could fail.</p>
</li>
<li>
<p>[Workflow] Use <code>t:add_cascade_opts/0</code> for <code>add_graft/4</code></p>
<p>The <code>add_graft/4</code> function accepts workflow opts in addition to job opts, but only specified the
<code>t:add_opts/0</code> type.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.6.14</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.6.14" />
      <id>https://oban.pro/releases/pro/1.6.14</id>
      <published>2026-03-26T00:00:00Z</published>
      <updated>2026-03-26T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Enhancements</h3>
<ul>
<li>[Chunk] Backport <code>chunk_id</code> generation for smoother upgrade to v1.7</li>
</ul>
<p>Generate a <code>chunk_id</code> at job insertion time and store it in meta. This pre-populates the
identifier that Pro v1.7 uses for optimized chunk queries, ensuring jobs inserted on v1.6 will
work seamlessly immediately after upgrading. Pro v1.7 will backfill jobs with a missing <code>chunk_id</code>
after a minute or so, but does so in batches.</p>
<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Worker] Conditionally add @impl to worker <strong>opts</strong>/0</p>
<p>Oban v2.21 marked <code>c:Worker.__opts__/0</code> as a callback and made it public. Now, <code>Oban.Pro.Worker</code>
marks the <code>__opts__/0</code> function as a callback for Oban v2.21, but not for older versions.</p>
</li>
<li>
<p>[Unique] Include suspended state in unique bmp mapping</p>
<p>The <code>suspended</code> state is available for uniqueness as of Oban v2.21, but it was lacking from the
uniq bmp mapping. The state is now supported to prevent errors when inserting unique jobs.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.7.0-rc.0</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.7.0-rc.0" />
      <id>https://oban.pro/releases/pro/1.7.0-rc.0</id>
      <published>2026-03-25T00:00:00Z</published>
      <updated>2026-03-25T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Enhancements</h3>
<ul>
<li>
<p>[Pro] Use suspended state for workflow and chain tracking</p>
<p>Jobs waiting on workflow or chain dependencies now use a proper <code>suspended</code> job state instead of
the previous <code>on_hold</code> psuedo-state.</p>
<p>This provides cleaner state semantics, better query performance through simplified indexes, and
enables the database triggers to track workflow state counts accurately. The <code>scheduled_at</code>
timestamp is preserved directly on suspended jobs, eliminating the need for <code>orig_scheduled_at</code>
in meta.</p>
</li>
<li>
<p>[Pro] Add usage rules for agentic coding assistants</p>
<p>Ship reference documents that help coding agents understand Pro's idioms and best practices.
Rules cover workers, queues, composition primitives (workflows, batches, chains, chunks),
plugins, and testing.</p>
</li>
<li>
<p>[Chunk] Optimize queries with centralized index and better job acking</p>
<p>Use a pre-computed <code>chunk_id</code> for chunk tracking, enabling a partial index for <em>much</em> faster
chunk lookups. This eliminates dynamic query construction based on partitioning fields in favor
of direct <code>chunk_id</code> matching.</p>
<p>Chunks now use a single SQL operation for acking , reducing database round-trips when
completing, cancelling, or retrying jobs within a chunk. The new acking operation has better
compatibility with non-Postgres databases such as CockroachDB.</p>
</li>
<li>
<p>[Chunk] Add <code>:snooze</code>  support to chunk workers</p>
<p>Chunks can now selectively snooze jobs using <code>&lbrace;:snooze, period, jobs&rbrace;</code> or selectively snooze
some jobs with <code>snooze: &lbrace;period, jobs&rbrace;</code> in the keyword list result. Snoozed jobs are rescheduled after the specified
period, while unlisted jobs complete normally.</p>
</li>
<li>
<p>[DynamicCron] Add <code>get/2</code> function for fetching entries by name</p>
<p>Provides a convenient way to retrieve a single cron entry without fetching all entries. Accepts
either a worker module or custom string name and returns {:ok, entry} or {:error, message}.</p>
</li>
<li>
<p>[DynamicLifeline] Improve workflow rescue accuracy and remove limit</p>
<p>The DynamicLifeline's workflow rescue mechanism now queries the aggregate table for workflows
with suspended jobs, providing more accurate detection than scanning the jobs table for
suspended jobs alone.</p>
<p>This catches edge cases where workflows are stuck but their suspended jobs may have been lost or
deleted. Legacy <code>on_hold</code> workflows that predate the aggregate table are still found via a
direct jobs table query.</p>
<p>Only workflows that have been executing for more than a minute are candidates for rescuing by
default.</p>
</li>
<li>
<p>[DynamicLifeline] Automatically repair chunk jobs missing <code>chunk_id</code></p>
<p>Chunk workers now use a pre-computed <code>chunk_id</code> for grouping. Jobs created before this change
won't have a <code>chunk_id</code> in their metadata, which would prevent them from being grouped correctly.</p>
<p>The DynamicLifeline plugin now automatically computes and sets the <code>chunk_id</code> for any chunk jobs
that are missing it, similar to how it repairs missing <code>partition_key</code> values for partitioned
queues.</p>
</li>
<li>
<p>[DynamicPruner] Add configurable <code>preserve_workflows</code> option</p>
<p>Allow disabling workflow job preservation during pruning via the new <code>preserve_workflows</code>
option, which defaults to true for backwards compatibility. When disabled, jobs are pruned
regardless of whether their workflow is still active. This is useful for large workflows that
naturally run longer than a pruning cycle.</p>
</li>
<li>
<p>[Migration] Replace generated columns with expression indexes</p>
<p>Generated columns for <code>uniq_key</code> and <code>partition_key</code> were originally added for CockroachDB
compatibility but introduced unnecessary complexity and excessive table locking for large
tables. This change replaces them with expression indexes directly on the meta JSONB fields.</p>
<p>A <code>generated_columns</code> migration option is still available for apps that are running CockroachDB
and need the old functionality. The Smart engine detects which mode is being used and handles
conflicts accordingly.</p>
</li>
<li>
<p>[Migration] Add partial indexes for pruning and staging</p>
<p>Partial indexes reduce index size and improve query performance by only indexing rows that match
the filter condition. This adds partial indexes for terminal job states and a staging index for
jobs ready to transition to <code>available</code>.</p>
<p>Also fixes completed job pruning to use <code>completed_at</code> instead of <code>scheduled_at</code>, which is the
semantically correct timestamp for determining job age.</p>
<p>The new staging query may perform 2-10x faster depending on the number of jobs and overall state
distribution.</p>
</li>
<li>
<p>[Rate Limit] Add centralized module for using rate limits outside of job execution</p>
<p>The module's functions allow checking, resetting, and consuming rate limits from running queues.</p>
<p>The <code>consume/3</code> function is fully distributed and spreads consumption across multiple nodes when
a single producer cannot satisfy the request.</p>
</li>
<li>
<p>[Smart] Add <code>auto_space</code> option to spread out bulk inserts</p>
<p>When inserting large batches of jobs, <code>auto_space</code> schedules each batch at
increasing intervals. This prevents overwhelming queues when jobs can't
all execute immediately.</p>
</li>
<li>
<p>[Smart] Add <code>on_conflict: :skip</code> option for bulk inserts</p>
<p>Support skipping unique conflicts without row locking during insert_all. When enabled,
conflicting jobs are silently skipped and only newly inserted jobs are returned. This improves
performance for high-throughput scenarios where tracking conflicts isn't needed.</p>
</li>
<li>
<p>[Smart] Add transaction option for bulk insert atomicity</p>
<p>Support <code>transaction: :per_batch</code> to commit each batch independently during <code>insert_all/2</code>.
Previously inserted batches persist even if a later batch fails. The default transaction: :all
preserves the existing all-or-nothing behavior.</p>
</li>
<li>
<p>[Smart] Add telemetry sub-spans for engine fetch_jobs</p>
<p>Instrument the fetch_jobs transaction with nested telemetry spans for granular observability
into acking, flushing, demand calculation, and job fetching. Each sub-span emits standard span
events under <code>[:oban, :engine, :fetch_jobs, :ack | :flush | :demand | :fetch]</code>.</p>
</li>
<li>
<p>[Smart] Support selecting between multiple rate limiting algorithms</p>
<p>Rate limited queues can select from <code>:sliding_window</code>, <code>:fixed_window</code>, and <code>:token_bucket</code>
algorithms to control how rate quotas are consumed. The <code>:sliding_window</code> algorithm remains the
default.</p>
</li>
<li>
<p>[Smart] Add <code>:fixed_window</code> rate limiting algorithm</p>
<p>Introduce a fixed window algorithm, which resets the count when the period expires rather than
using weighted averaging.</p>
</li>
<li>
<p>[Smart] Add token bucket rate limiting algorithm</p>
<p>Introduce token bucket algorithm that refills tokens continuously at a fixed rate rather than
resetting at period boundaries. Tokens refill at <code>allowed / period</code> per second, providing
smoother rate limiting with natural burst handling.</p>
</li>
<li>
<p>[Worker] Add <code>@impl</code> declaration for worker <code>__opts__/0</code></p>
<p>Mark <code>Oban.Pro.Worker.__opts__/0</code> as implementing the new <code>__opts__/0</code> public callback from
<code>Oban.Worker</code>.</p>
</li>
<li>
<p>[Worker] Add <code>on_cancelled/2</code> and <code>on_discarded/2</code> worker hooks</p>
<p>Introduce two new worker callbacks that fire when jobs are cancelled or discarded, regardless of
how the state transition happens:</p>
<ul>
<li><code>on_cancelled/2</code> receives :dependency or :manual reason</li>
<li><code>on_discarded/2</code> receives :exhausted reason</li>
</ul>
<p>Both callbacks work with global hooks via <code>attach_hook/1</code> and module-level hooks via the
<code>:hooks</code> option.</p>
</li>
<li>
<p>[Worker] Apply structured args <code>timeout/1</code> and <code>backoff/1</code></p>
<p>Automatically apply encryption and structuring before calling user defined <code>timeout/1</code> or
<code>backoff/1</code> implementations. This allows pattern matching on structured args without any code
changes.</p>
</li>
<li>
<p>[Worker] Variable weight rate limit tracking via options and callback</p>
<p>Add support for job weights in rate limiting, allowing jobs to consume variable amounts of
rate limit capacity:</p>
<ul>
<li>Worker option: <code>use Oban.Pro.Worker, rate: [weight: 5]</code></li>
<li>Job option: <code>Worker.new(args, rate: [weight: 3])</code></li>
<li>Callback: <code>weight/1</code> for dynamic weight calculation at dispatch time</li>
</ul>
<p>Jobs with higher weights consume more rate limit capacity, enabling
fine-grained control over resource-intensive operations.</p>
</li>
<li>
<p>[Workflow] Optimize workflow flushing with de-duplication</p>
<p>Restructure workflow flushing to compute dependency states once per unique dependency rather
than once per job-dep combination. This eliminates the M*N scaling problem when M jobs share
common dependencies.</p>
<p>Benchmarks show ~2x faster execution, 7x fewer buffer hits, and 15x fewer index scans for
workflows with shared dependencies.</p>
</li>
<li>
<p>[Workflow] Flushing is optimized to load minimal data up front</p>
<p>Only the exact data needed for workflow flush operations is loaded from the database, rather
than the entire job structure. This saves data over the wire, serialization overhead, and memory
usage for active workflows or jobs with large <code>args</code>, <code>errors</code>, or <code>meta</code>.</p>
<p>The full job structure is loaded asynchronously when cancellation callbacks are needed.</p>
</li>
<li>
<p>[Workflow] Add table for centralized workflow tracking</p>
<p>Introduces a dedicated table to track workflow metadata and job state counts, replacing
expensive aggregation queries with precomputed values. This improves performance for large
workflows and enables efficient filtering/sorting in Oban Web.</p>
</li>
<li>
<p>[Workflow] Add unique workflow support to prevent duplicates</p>
<p>Workflows can now be created with <code>unique: true</code> to prevent multiple workflows with the same
name from running concurrently. When a duplicate unique workflow is inserted, its jobs are
marked with <code>conflict?: true</code> instead of being inserted.</p>
</li>
</ul>
<h3>Changes</h3>
<ul>
<li>
<p>[Pro] Packages are distributed with encrypted source code</p>
<p>Pro packages are encrypted, with licenses that stay fresh for 30 days. Development remains
seamless, so documentation, type signatures, and LSP integration all work normally.</p>
<p>Enterprise license holders receive unencrypted source code.</p>
<p>See the <a href="v1-7.html">Upgrade Guide</a> for details on checking license status and refreshing.</p>
</li>
</ul>
<h3>Deprecations</h3>
<ul>
<li>
<p>[DynamicPartitioner] Deprecate the <code>DynamicPartitioner</code> plugin</p>
<p>The complexity and edge cases introduced by partitioned tables far outweigh the benefits for
most applications.</p>
</li>
<li>
<p>[Workflow] Deprecate <code>after_cancelled/2</code> in favor of universal <code>on_cancelled/2</code></p>
<p>The <code>after_cancelled/2</code> callback is deprecated in favor of the universal <code>on_cancelled/2</code> hook.
Currently, <em>both</em> hooks will be called if defined, and users should switch to <code>on_cancelled/2</code>.</p>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Migration] Fix migration check crash with dynamic repo config</p>
<p>The migration telemetry handler crashed on startup when users configured a placeholder repo
module with <code>get_dynamic_repo</code> providing the actual repo at runtime. The handler attempted to
call <code>get_dynamic_repo/0</code> on the static repo before evaluating the dynamic repo callback.</p>
</li>
<li>
<p>[Refresher] Add error handling to producer record refreshing</p>
<p>Previously, if refresh_producers or cleanup_producers raised (e.g., due to a connection checkout
timeout), the GenServer would crash and restart, causing missed heartbeats and timer resets. Now
errors are caught and logged, allowing the refresh cycle to continue uninterrupted.</p>
</li>
<li>
<p>[Testing] Ensure ordered <code>run_workflow/2</code> output</p>
<p>Always order workflow jobs by execution completion order to preserve sequential execution
results.</p>
</li>
<li>
<p>[Worker] Trigger <code>on_cancelled/2</code> when deadline force-cancels</p>
<p>When a job with <code>deadline: [force: true]</code> exceeds its deadline during execution, the
<code>on_cancelled/2</code> hook is now called with <code>:deadline</code> as the reason. This allows workers to
perform cleanup or notifications when jobs are terminated due to deadline expiration.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.6.13</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.6.13" />
      <id>https://oban.pro/releases/pro/1.6.13</id>
      <published>2026-03-02T00:00:00Z</published>
      <updated>2026-03-02T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Enhancements</h3>
<ul>
<li>
<p>[Oban] Add job diagnostics for runtime process inspection</p>
<p>Introduce runtime diagnostics for executing <code>Oban.Pro.Worker</code> jobs, allowing external tools like
Oban Web to request process information (stacktrace, memory, status) for running jobs via
PubSub.</p>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>
<p>[DynamicCron] Skip worker validation when deleting DynamicCron</p>
<p>When a worker module is removed from the codebase, it was previously impossible to delete its
persisted cron entry using the delete: true option because validation would fail trying to load
the module.</p>
</li>
<li>
<p>[Smart] Fix unique violation retry loop for similar states</p>
<p>Clearing unique violations could fail to find conflicting jobs when the job's current state
(e.g., <code>retryable</code>) was included in its unique states. This caused the clearing query not to
match, so nothing would be cleared, causing an infinite retry loop.</p>
<p>Retries are now limited and an error logged for excessive retry loops.</p>
</li>
<li>
<p>[Smart] Prevent chain race condition on concurrent insert</p>
<p>When two transactions concurrently insert jobs for the same chain without any prior jobs in the
chain, both could see &quot;no existing job&quot; and insert as <code>available</code>, violating sequential
guarantees.</p>
</li>
<li>
<p>[Smart] Include <code>:meta</code> options in <code>t:partition/0</code></p>
<p>Using <code>meta</code> for partitioning has been supported for a while now, but the type was outdated.</p>
</li>
<li>
<p>[Workflow] Fix context for deps in nested sub-workflows</p>
<p>When using <code>add_cascade</code> with a fan-out tuple (e.g., <code>&lbrace;items, &amp;fun/2&rbrace;</code>) inside a nested
sub-workflow that also has dependencies, the fan-out jobs would incorrectly receive the outer
workflow's context instead of their immediate parent sub-workflow's context.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.6.12</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.6.12" />
      <id>https://oban.pro/releases/pro/1.6.12</id>
      <published>2026-01-30T00:00:00Z</published>
      <updated>2026-01-30T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Enhancements</h3>
<ul>
<li>
<p>[DynamicCron] Optimize cron entry insertion history tracking</p>
<p>The history tracking for cron entries retained too much information and was needlessly expensive
to read and write. This simplifies the insertion format and dramatically simplifies tracking
updates.</p>
<p>The optimization is backward compatible and existing arrays with history will continue to work,
they'll just be replaced with single-element arrays on the next insertion.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.6.11</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.6.11" />
      <id>https://oban.pro/releases/pro/1.6.11</id>
      <published>2026-01-19T00:00:00Z</published>
      <updated>2026-01-19T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Enhancements</h3>
<ul>
<li>
<p>[Workflow] Support fan-out tuple form in add_graft/4</p>
<p>The type specification for <code>add_graft/4</code> indicated support for the cascade fan-out form
<code>&lbrace;Enum.t(), (any(), map() -&gt; any())&rbrace;</code>, but only the single-function form was implemented.</p>
<p>Now you can fan out graft points the same way you would with <code>add_cascade</code>:</p>
<pre class="lumis" style="color: #d8dee9; background-color: #2e3440;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">Workflow.new()
</div><div class="line" data-line="2">|&gt; Workflow.add(:setup, SetupWorker.new(%&lbrace;&rbrace;))
</div><div class="line" data-line="3">|&gt; Workflow.add_graft(:process, &lbrace;items, &amp;process_item/2&rbrace;, deps: :setup)
</div><div class="line" data-line="4">|&gt; Workflow.add(:finalize, FinalizeWorker.new(%&lbrace;&rbrace;), deps: :process)
</div></code></pre>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Workflow] Wait for arbitrarily nested grafted sub-workflows</p>
<p>Jobs depending on a graft now properly wait for dynamically appended sub-workflows at <em>any</em>
nesting depth. Previously, when a grafted workflow used <code>append</code> + <code>add_many</code> to create
additional jobs, the dependent job could run before those nested jobs completed.</p>
<p>The flush mechanism now tracks the complete ancestor chain so workflows flush correctly
regardless of how deeply they are nested.</p>
</li>
<li>
<p>[Workflow] Inherit all context for nested sub-workflows</p>
<p>When using <code>add_cascade</code> with fan-out inside nested sub-workflows, context from intermediate
parent workflows was not accessible. Jobs would only see context from the outermost workflow,
skipping any workflows in between.</p>
<p>Now context is correctly inherited through any depth of workflow nesting. Each level's context
is merged in order from outermost to innermost, with closer ancestors overriding values from
farther ones.</p>
</li>
<li>
<p>[Smart] Ensure partition key cache never hits under load</p>
<p>The timed cache for partition keys could fail to return cached results when jobs were being
inserted concurrently, causing repeated database queries for available partition keys. This
removes stale telemetry handlers that were interfering with cache lookups.</p>
</li>
<li>
<p>[DynamicLifeline] Correctly filter empty partitions during rescue queries</p>
<p>The partition rescue query could incorrectly check producers with empty partitions after queue
updates.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.6.10</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.6.10" />
      <id>https://oban.pro/releases/pro/1.6.10</id>
      <published>2026-01-16T00:00:00Z</published>
      <updated>2026-01-16T00:00:00Z</updated>
      <content type="html"><![CDATA[<ul>
<li>
<p>[Smart] Improve partition query by repairing unpartitioned jobs</p>
<p>Partitioning selects fewer keys, more accurately, to prioritize active partitions. This ensures
partitions with higher priority or older jobs are processed first, while still distributing work
across all active partitions.</p>
<p>Jobs in partitioned queues that are missing a partition key (such as jobs scheduled before a
queue was partitioned) are now repaired by <code>DynamicLifeline</code> automatically.</p>
<p>Overall, this change means fetching jobs in partitioned queues requires fewer resources even
after bulk job inserts.</p>
</li>
<li>
<p>[Smart] Add telemetry and logging for unique repairs</p>
<p>Emit <code>[:oban, :engine, :uniq_violation_repaired]</code> telemetry on every unique repair and log an
error once per unique key to surface potential issues that could otherwise silently degrade
staging and fetching performance.</p>
</li>
<li>
<p>[Workflow] Preserve name for deeply nested sub-workflows</p>
<p>Preserve the sub-workflow's name for jobs from nested sub-workflows when composing workflows
with <code>add_workflow/3</code>. Previously, nesting a workflow containing <code>add_many/3</code> inside another
<code>add_workflow/3</code> would incorrectly overwrite the inner jobs' metadata.</p>
</li>
<li>
<p>[Workflow] Prevent context conflicts in deeply nested workflows</p>
<p>Preserve the workflow id for jobs from nested sub-workflows to prevent multiple context jobs
from sharing the same <code>workflow_id</code>. This eliminates an <code>Ecto.MultipleResultsError</code> when using
<code>put_context/2</code> in workflows nested multiple levels deep via <code>add_workflow/2</code>.</p>
</li>
<li>
<p>[Workflow] Wait for dynamically appended jobs for grafted workflows</p>
<p>Jobs depending on a grafted workflow now correctly wait for all dynamically inserted jobs,
including those added via <code>add_many/4</code> within <code>apply_graft/2</code>.</p>
<p>Previously, when a grafter job used <code>apply_graft/2</code> with a workflow containing nested
sub-workflows (from <code>add_many/4</code>), dependent jobs would execute before the appended jobs
completed.</p>
</li>
<li>
<p>[Workflow] Track recursive graft dependencies from parent workflows</p>
<p>When a grafting job creates sub-grafts recursively, jobs depending on the original graft now
correctly wait for all recursive grafts to complete.</p>
<p>The fix ensures nested graft jobs share the root graft's <code>workflow_id</code> so the parent's wildcard
dependency can find them.</p>
</li>
<li>
<p>[Worker] Validate structured args during <code>update_job/3</code></p>
<p>Structured args weren't validated during <code>update_job/3</code> calls, which could lead to errors when
the job eventually processed. Now, same validation is applied on update as when the job is built
with <code>new/2</code></p>
</li>
<li>
<p>[Decorator] Correct typespec for the subset of unique opts for decorated jobs</p>
<p>The typespec for <code>period</code>, <code>states</code>, and <code>timestamp</code> were incomplete or too loose. The new
typespec references the types from <code>Oban.Job</code>.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.6.9</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.6.9" />
      <id>https://oban.pro/releases/pro/1.6.9</id>
      <published>2025-11-25T00:00:00Z</published>
      <updated>2025-11-25T00:00:00Z</updated>
      <content type="html"><![CDATA[<ul>
<li>
<p>[Smart] Fix inaccurate acking for externally modified jobs</p>
<p>When another node or process updates a job while it's executing, or inserts a conflicting unique
job, the ack query could fail to lock or update. This could cause mismatches between tracked and
actual job states.</p>
</li>
<li>
<p>[Worker] Ensure <code>after_process/3</code> hooks trigger on cancellation</p>
<p>Executing jobs that were manually cancelled via <code>Oban.cancel_job</code> or similar mechanisms, didn't
have <code>after_process/3</code> hooks triggered due to a caching issue. That situation is handled now and
hooks are called as expected.</p>
</li>
<li>
<p>[Refresher] Cleanup producers regardless of running queues</p>
<p>The refresher will now cleanup stale producers from any leader node, not just nodes that are
running queues. This aims to ensure producers are cleaned up even with subtle misconfigurations,
e.g. disabling queues without disabling plugins.</p>
</li>
<li>
<p>[Docs] Extract svg diagrams into separate files</p>
<p>Inline svg images involve a lot of text and can overwhelm an LLM's context window. This extracts
the svgs out as separate assets and loads them dynamically instead.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.6.8</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.6.8" />
      <id>https://oban.pro/releases/pro/1.6.8</id>
      <published>2025-11-13T00:00:00Z</published>
      <updated>2025-11-13T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Enhancements</h3>
<ul>
<li>
<p>[Workflow] Add <code>atom_keys</code> option to configure cascade context</p>
<p>The <code>atom_keys</code> option controls whether the keys in a cascade function's <code>context</code> map are
atomized or converted to strings. This helps with consistency for nested workflows, or when
atoms may not exist between nodes.</p>
</li>
<li>
<p>[Testing] Add missing functions to make a drop-in replacement for <code>Oban.Testing</code></p>
<p>Add missing functions (<code>build_job/3</code>, <code>perform_job/1,2</code>, and <code>with_testing_mode/2</code>) to ensure
<code>Oban.Pro.Testing</code> is fully compatible with <code>Oban.Testing</code>. This allows seamless migration from
Pro without modifying existing test helpers or test code.</p>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Workflow] Flatten contexts within nested grafts</p>
<p>When building complex workflows with nested grafts (a graft within another graft), the recorded
results from upstream jobs were being incorrectly nested in the context passed to downstream
jobs. Now, nesting is applied correctly at any level of nesting.</p>
</li>
<li>
<p>[Workflow] Prevent deadlocks for queues processing workflows</p>
<p>Adds locking to prevent deadlocks when jobs from the same workflow or chain complete
simultaneously on different nodes.</p>
</li>
<li>
<p>[Workflow] Correct condition guard in status query</p>
<p>The query used to expand the status for sub-workflows didn't make use of the existing workflow
indexes, which could lead to poor performance in busy systems.</p>
</li>
<li>
<p>[DynamicCron] Compare inserted times in UTC for guaranteed cron</p>
<p>Prevent double triggering jobs for guaranteed cron during DST changes. Time is always compared
in UTC, rather than shifting to the current timezone.</p>
</li>
<li>
<p>[Refresher] Prevent active producers from erroneous cleanup</p>
<p>When a queue's producer is stuck in a transaction retry loop, it isn't able to handle new
messages, including periodic refresh requests. This would allow the producer record to become
outdated in the database, at which point it is subject to erroneous cleanup.</p>
<p>Refreshing now proactively discovers and refreshes all active producers through the Registry,
rather than relying on individual producers messages.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.6.7</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.6.7" />
      <id>https://oban.pro/releases/pro/1.6.7</id>
      <published>2025-10-23T00:00:00Z</published>
      <updated>2025-10-23T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Workflow] Explicitly group workflow conditions for flushing query</p>
<p>The boolean grouping of statements in a workflow query was incorrectly interpreted and resulted
in a highly inefficient workflow query.</p>
</li>
<li>
<p>[Smart] Fetch and execute jobs with missing partition keys</p>
<p>Previously, partitioned queues wouldn't run jobs with null partition keys. This was an edge
case, usually caused by adding partition config to a queue with older, scheduled jobs in it.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.6.6</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.6.6" />
      <id>https://oban.pro/releases/pro/1.6.6</id>
      <published>2025-10-20T00:00:00Z</published>
      <updated>2025-10-20T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Enhancements</h3>
<ul>
<li>
<p>[Relay] Add with_retries option to <code>await/2</code></p>
<p>It's now possible to keep waiting for a job to complete across retries. The awaiting process
will keep waiting until the job has exhausted all attempts before or the timeout is reached.
This makes async/await more reliable for jobs that have flickering failures.</p>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Refresher] Fix shutdown order to prevent rescue mistakes</p>
<p>Queue producer refreshing, which is essential to identifying live queues, could terminate too
early during node shutdown. When the <code>shutdown_grace_period</code> exceeded the refresh interval,
producers would lose the ability to update their timestamps while still running, leading to
false stale producer detection and unnecessary job rescues.</p>
<p>Refreshing now survives instance shutdowns and continues updating producer timestamps throughout
the grace period.</p>
</li>
<li>
<p>[Smart] Prevent deadlock during concurrent unique insert and fetch operations</p>
<p>Inserting unique jobs that conflicted with currently executing jobs could cause a deadlock. The
ack query now takes an explicit lock to ensure updates complete, while also preventing the
deadlock.</p>
</li>
<li>
<p>[Workflow] Update workflow flushing syntax for CRDB</p>
<p>The flushing query used a syntax that is valid in Postgres, but incompatible with how CRDB
handles JSONB function output.</p>
</li>
<li>
<p>[Workflow] Include cascade options in <code>apply_graft/2</code></p>
<p>The spec only included <code>t:new_opts/0</code>, but the function also creates cascade jobs and should
include <code>t:add_cascade_opts/0</code>.</p>
</li>
<li>
<p>[DynamicQueues] Correct typespec for <code>update/3</code></p>
<p>The typespect listed <code>t:queue_opts/0</code>, which is a union of tuples, but should be a keyword list.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.6.5</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.6.5" />
      <id>https://oban.pro/releases/pro/1.6.5</id>
      <published>2025-09-22T00:00:00Z</published>
      <updated>2025-09-22T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Bug Fixes</h3>
<ul>
<li>
<p>[DynamicPruner] Retain jobs in active workflows when pruning</p>
<p>The <code>DynamicPruner</code> no longer deletes jobs that are part of an active workflow or related
sub-workflow. If any job in a workflow is in an incomplete state (e.g. executing), no jobs in
the workflow will be pruned.</p>
</li>
<li>
<p>[DynamicQueues] Enabling clearing limits on queue config update</p>
<p>Clearing a DynamicQueue's <code>rate_limit</code> or <code>global_limit</code> by setting <code>nil</code>, or swapping between
options, now works with a config update.</p>
</li>
<li>
<p>[Workflow] Handle empty enumerables passed to <code>add_many/4</code></p>
<p>Empty sub-workflows no longer prevent downstream jobs in a workflow from executing. Now, step
elimination ensures the workflow executes in the expected order when a sub-workflow created from
an empty enumerable.</p>
</li>
<li>
<p>[Worker] Move option validation to <code>after_compile/2</code> hook</p>
<p>Validating options with an exception in an <code>after_verify/1</code> hook breaks progressive compilation
and causes ongoing warnings during subsequent compilations. Options are now validated with
<code>after_compile/2</code> instead.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.6.4</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.6.4" />
      <id>https://oban.pro/releases/pro/1.6.4</id>
      <published>2025-08-21T00:00:00Z</published>
      <updated>2025-08-21T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Smart] Prevent leaking tracked partitions between job fetches.</p>
<p>A race condition between acking and fetching could cause tracked global partitions to be
retained when jobs weren't still processing. Now that logic is simplified and corrected to
ensure only actively running job partitions are tracked.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.6.3</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.6.3" />
      <id>https://oban.pro/releases/pro/1.6.3</id>
      <published>2025-08-18T00:00:00Z</published>
      <updated>2025-08-18T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Smart] Correct expiration for partition key caching</p>
<p>Partition keys are now cached and expired independently to compensate for differing queue access
patterns and load.</p>
</li>
<li>
<p>[Smart] Limit partition keys fetched with optional config</p>
<p>Previously, the number of unique partitions fetched from the database was unlimited. The
resulting query, and amount of data fetched, could cause performance problems in systems with
high cardinality.</p>
</li>
<li>
<p>[Smart] Properly enforce partitioned global concurrency between nodes.</p>
<p>Simplified partition tracking prevents concurrency violations for queues with <code>global_limit: 1</code>
and bursts of activity. Lingering tracked values could lead to incorrect demand in some
circumstances.</p>
</li>
<li>
<p>[Decorator] Update validation and handling for unique state groups</p>
<p>Decorated jobs now allow Oban v2.20's unique state groups. The <code>states</code> portion of decorated
typespecs is corrected as well.</p>
</li>
<li>
<p>[Workflow] Return unknown status for missing workflows</p>
<p>Rather than causing an exception, the <code>status/2</code> function returns an empty status map with the
<code>:unknown</code> status when no workflow jobs can be found in the database.</p>
</li>
<li>
<p>[Workflow] Pass scheduling fields through when creating cascade jobs</p>
<p>Scheduling options weren't passed through to cascade jobs, which made it difficult to delay
cascade workflows.</p>
</li>
<li>
<p>[Workflow] Include sub-workflows when cancelling workflow jobs</p>
<p>Sub workflows can't run if the parent workflow is cancelled. Now sub-workflows are cancelled by
default, though the functionality can be disabled using <code>with_subs: false</code>.</p>
</li>
<li>
<p>[Workflow] Correct <code>t:add_opt/0</code> type for workflows.</p>
<p>The type was incorrectly auto-formatted into an invalid form.</p>
</li>
<li>
<p>[Testing] Restrict execution to single batch with <code>run_batch/1</code></p>
<p>The <code>run_batch/2</code> helper would insert the batch but run all available jobs rather than
restricting to the current batch. Now <code>run_batch/2</code> limits execution as expected, similarly to
<code>run_workflow/2</code>.</p>
</li>
<li>
<p>[Testing] Prevent config fetching errors while draining jobs</p>
<p>Some functions, such as <code>Workflow.get_job/3</code>, rely on the presence of an Oban config while
draining. Now the generated config is registered and available to any functions that may require
it while testing.</p>
</li>
<li>
<p>[Testing] Preserve original job order for <code>perform_chunk/1</code></p>
<p>The order of jobs passed to <code>perform_chunk/1</code> was reversed after pre-processing was applied.</p>
</li>
<li>
<p>[Testing] Ignore context jobs when draining sub-workflows</p>
<p>Nested contexts within sub-workflows would mistakenly be attempted while draining. Context jobs
shouldn't be processed at any point, including during test runs.</p>
</li>
<li>
<p>[Worker] Improve error message for hook module validation.</p>
<p>The error message only mentioned <code>after_process/2,3</code>, not the other hooks added more recently.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.6.2</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.6.2" />
      <id>https://oban.pro/releases/pro/1.6.2</id>
      <published>2025-07-17T00:00:00Z</published>
      <updated>2025-07-17T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Workflow] Only atomize top level keys in recorded cascade</p>
<p>Cascade functions may record arbitrary terms, including maps with purposeful string keys and
structs. Now only the top level of recorded maps are atomized and nested map keys are left as
is.</p>
</li>
<li>
<p>[Worker] Pass changes from <code>before_process</code> in <code>after_process</code></p>
<p>Changes made to jobs in a <code>before_process</code> hook are now passed through to <code>after_process</code>.
Previously, only modified <code>args</code> were retained.</p>
</li>
<li>
<p>[Plugins] Add logging for unexpected messages to all processes</p>
<p>Add a catch all to log a warning when plugins, or other GenServers, receive unexpected messages.</p>
</li>
<li>
<p>[Refresher] Prevent simultaneous producer refresh deadlocks.</p>
<p>Deleting the same producer records from multiple nodes simultaneously could lead to a deadlock.
Now, only the leader will perform deletions.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.6.1</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.6.1" />
      <id>https://oban.pro/releases/pro/1.6.1</id>
      <published>2025-07-08T00:00:00Z</published>
      <updated>2025-07-08T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Workflow] Include grafted workflows in cascade context</p>
<p>The results from a graft are now passed down to the context for cascade jobs. In addition,
nested map keys from grafted cascade jobs are atomized when possible.</p>
</li>
<li>
<p>[Workflow] Increase rescue limit to capture more potentially stuck workflows</p>
<p>The decreased limit failed to rescue stalled workflows in especially busy systems.</p>
</li>
<li>
<p>[Worker] Dump structs nested within maps in structured args</p>
<p>Structs within a map weren't dumped to a map the way the were at the top level. Now structs are
uniformly escaped at the top level, within embeds, within lists, and within maps.</p>
</li>
<li>
<p>[Smart] Cast states to strings on unique violation</p>
<p>Handling unique violations from the <code>available-&gt;executing</code> transition correctly uses strings
instead of atoms now.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.6.0</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.6.0" />
      <id>https://oban.pro/releases/pro/1.6.0</id>
      <published>2025-06-30T00:00:00Z</published>
      <updated>2025-06-30T00:00:00Z</updated>
      <content type="html"><![CDATA[<blockquote>
<h4>Rerun Index Migrations {: .warning}</h4>
<p>The workflow indexes changed between v1.6.0-rc.5 and v1.6.0. If you're upgrading from a v1.6
release candidate, you should rerun index migrations:</p>
<pre class="lumis" style="color: #d8dee9; background-color: #2e3440;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #81a1c1;">Oban.Pro.Migration</span><span style="color: #81a1c1;">.</span><span style="color: #88c0d0;">down</span><span style="color: #88c0d0;">(</span><span style="color: #ebcb8b;">only: </span><span style="color: #ebcb8b;">:indexes</span><span style="color: #88c0d0;">)</span>
</div><div class="line" data-line="2"><span style="color: #81a1c1;">Oban.Pro.Migration</span><span style="color: #81a1c1;">.</span><span style="color: #88c0d0;">up</span><span style="color: #88c0d0;">(</span><span style="color: #ebcb8b;">only: </span><span style="color: #ebcb8b;">:indexes</span><span style="color: #88c0d0;">)</span>
</div></code></pre>
</blockquote>
<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Workflow] Optimize workflow deps queries for index use</p>
<p>Revamped queries are able to fully utilize workflow indexes for all dependency checks. The
resulting queries remain extremely fast even in systems with thousands of parallel workflows.</p>
</li>
<li>
<p>[Workflow] Correct <code>add_cascade_opts</code> type definition.</p>
<p>The type incorrectly combined Job.option and a list of add options, rather than forming a single
list of types.</p>
</li>
<li>
<p>[Refresher] Ensure the refresher flushes continuously.</p>
<p>This is a critical bug that would cause producer records to accumulate indefinitely after the
first 15 seconds of the refresher running.</p>
</li>
<li>
<p>[Smart] Block chain preparation during ack updates</p>
<p>Acking chains while inserting a new job in the chain was prone to a race condition that would
lead to &quot;stuck&quot; chains. This augments the chain insertion query so that it blocks until other
jobs in the chain are committed to prevent transactional races.</p>
</li>
<li>
<p>[Smart] Correct locking when flush handlers present</p>
<p>Nodes must acquire a lock to safely coordinate workflow, batch, and chain queries while acking.
This corrects the logic used to check whether there are pending flushes during a transaction.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.5.5</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.5.5" />
      <id>https://oban.pro/releases/pro/1.5.5</id>
      <published>2025-06-20T00:00:00Z</published>
      <updated>2025-06-20T00:00:00Z</updated>
      <content type="html"><![CDATA[<ul>
<li>
<p>[Smart] Block chain state checks while acking.</p>
<p>Acking chains while inserting a new job in the chain was prone to a race condition that would
lead to &quot;stuck&quot; chains. This augments the chain insertion query so that it blocks until other
jobs in the chain are committed to prevent transactional races.</p>
</li>
<li>
<p>[Smart] Correct locking when flush handlers present.</p>
<p>Nodes must acquire a lock to safely coordinate workflow, batch, and chain queries while acking.
This corrects the logic used to check whether there are pending flushes during a transaction.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.6.0-rc.5</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.6.0-rc.5" />
      <id>https://oban.pro/releases/pro/1.6.0-rc.5</id>
      <published>2025-06-09T00:00:00Z</published>
      <updated>2025-06-09T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Enhancements</h3>
<ul>
<li>
<p>[Pro] Address deprecation warnings from Elixir v1.19.</p>
</li>
<li>
<p>[Refresher] Simplify refreshing logic for stale producers.</p>
<p>Now that producer tracking is centralized, the queries for purging stale records can be much
simpler and purge records even sooner.</p>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Decorator] Ensure decorated modules are loaded on process.</p>
<p>Ensure the module is loaded before converting the function to prevent unknown atom issues.</p>
</li>
<li>
<p>[Migration] Refactor migration versioning to support separate schema/index tracking</p>
<p>Split version tracking to independently track schema and index migrations, enabling more
granular migration control when using the <code>:only</code> option.</p>
</li>
<li>
<p>[Migration] Fix migration version check and warning logic when using split migrations.</p>
</li>
<li>
<p>[Workflow] Fix sub-workflow context merging for cascades</p>
<p>Scoping within a sub-workflow prevented correctly fetching recorded cascade values. Now context
is merged between parent and child cascade jobs.</p>
</li>
<li>
<p>[Workflow] Prevent seq scan from workflow deps query</p>
<p>The workflow deps query checked for the presence of a 'workflow_id' on the wrong table
reference, which prevented the correct usage.</p>
</li>
<li>
<p>[Workflow] Correctly resolve downstream graft dependencies</p>
<p>A race condition in deps checking could allow workflow jobs that dependeded on a graft job to
execute too soon.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.6.0-rc.4</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.6.0-rc.4" />
      <id>https://oban.pro/releases/pro/1.6.0-rc.4</id>
      <published>2025-05-29T00:00:00Z</published>
      <updated>2025-05-29T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Enhancements</h3>
<ul>
<li>
<p>[Workflow] Introduce <code>add_graft/3</code> and <code>apply_graft/2</code>.</p>
<p>Graft jobs serve as placeholders in a workflow. When a graft job executes, it must build
and attach a new sub-workflow at that point. Any downstream jobs that depend on the grafter
will wait for the entire grafted sub-workflow to complete.</p>
</li>
<li>
<p>[Plugins] Enable default logger output for all plugins.</p>
<p>All plugins now implement a callback that allows their output to be logged automatically by the
default <code>Oban.Telemetry</code> logger.</p>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Workflow] Optimize workflow deps query to allow index usage.</p>
<p>The query now uses an index enabled operator rather than a function. The resulting is much
uglier, but hundreds of times faster on larger tables.</p>
</li>
<li>
<p>[Smart] Safely decode legacy rate limit data from existing producers.</p>
</li>
<li>
<p>[Smart] Expire locally cached producers after a brief time to prevent memory leaks and ensure
producers are cleaned up shortly after a crash.</p>
</li>
<li>
<p>[Migration] Record separate schema and index migration version</p>
<p>The recorded migration version now indicates if only schemas or indexes were migrated, which
ensures an <code>indexes</code> migration will always run after a <code>schemas</code> migration.</p>
</li>
<li>
<p>[Migration] Make dropping the old workflow index concurrent when index-only migrations are
enabled.</p>
</li>
<li>
<p>[Migration] Respect dynamic repo configuration during version checks.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.6.0-rc.3</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.6.0-rc.3" />
      <id>https://oban.pro/releases/pro/1.6.0-rc.3</id>
      <published>2025-04-18T00:00:00Z</published>
      <updated>2025-04-18T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Enhancements</h3>
<ul>
<li>
<p>[Migration] Add concurrent migration options.</p>
<p>The new <code>only</code> option makes it possible to split migrations into <code>schema</code> and <code>index</code> changes.
That allows for concurrent index creation to prevent locking the jobs table during migrations
for tables with a large number of retained jobs.</p>
<p>This removes the need to set <code>concurrently</code> manually, deriving instead from the <code>only</code> option
itself. It also ensures all <code>down</code> migrations are using the concurrently option as well.</p>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Smart] Track partition keys on insert.</p>
<p>For queues without a backlog, but frequent inserts, partitioned jobs wouldn't be fetched
immediately. Now partition keys are added to the available keys cache immediately on insert for
the current node.</p>
</li>
<li>
<p>[Smart] Briefly retain tracked global keys.</p>
<p>Retaining tracked keys for several cycles beyond when they've had something to fetch helps
compensate for partitions without a backlog of jobs</p>
</li>
<li>
<p>[Smart] Lower partition keys cache TTL.</p>
<p>The TTL is reduced to 5s to reduce the lag before new partitions are processed in most systems.
This setting is now documented as well, in the Smart engine documentation.</p>
</li>
<li>
<p>[Workflow] Correct logic for sub-workflow dependency checks.</p>
<p>Jobs with dependencies on sub-workflows wouldn't be moved to the <code>available</code> state in some
situations. This corrects the order of status checks, and merges staging with rescuing logic.</p>
</li>
<li>
<p>[Workflow] Ensure arg value is always set for cascade jobs.</p>
<p>The default <code>nil</code> value is stripped from the encoded args map. This changes the default to a
non-nil value so it's retained for all cascade jobs.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.6.0-rc.2</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.6.0-rc.2" />
      <id>https://oban.pro/releases/pro/1.6.0-rc.2</id>
      <published>2025-04-11T00:00:00Z</published>
      <updated>2025-04-11T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Enhancements</h3>
<ul>
<li>
<p>[Workflow] Add cascading workflow mode with context-aware functions.</p>
<p>Cascade mode simplifies building workflows that rely on context or output from previous
functions. Cascade jobs use a strictly defined function signature and conventions to make
expressive workflows that &quot;cascade&quot; context between functions according to dependencies.</p>
</li>
<li>
<p>[Workflow] Create and render subgraphs for sub-workflows.</p>
<p>Graphs for sub-workflows now correctly track dependencies between jobs and subs, and sub-graphs
are created for sub-workflows. That includes subgraph rendering for <code>to_dot/1</code> and
<code>to_mermaid/1</code> rendered output.</p>
</li>
<li>
<p>[Workflow] Enhance <code>status/1</code> output with the workflow id, atom statuses, and sub workflows.</p>
<p>Sub-workflows are now included in the output of the status/1 function, along with the workflow
<code>id</code>. The workflow status is now reported as an atom rather than a string, and there is a full
typespec for the return value.</p>
</li>
<li>
<p>[Worker] Increase the default recorded value to 64mb</p>
<p>The default record value of 32k was highly conservative and a poor fit for decorated jobs or
cascading workflows. The default is now much larger than any value that <em>should</em> be stored as a
return value.</p>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Workflow] Fix grouping of workflow and sup workflow in query.</p>
<p>The workflow query lacked the grouping parenthesis required to select the correct partial index.</p>
</li>
<li>
<p>[DynamicCron] Remove double wrapping of cron_opt keyword</p>
<p>The <code>cron_opt</code> type changed to a keyword list, but it was still wrapped in a list. This removes
the double wrapping.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.6.0-rc.1</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.6.0-rc.1" />
      <id>https://oban.pro/releases/pro/1.6.0-rc.1</id>
      <published>2025-03-28T00:00:00Z</published>
      <updated>2025-03-28T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Smart] Safely accept legacy rate limit windows.</p>
<p>The legacy windows format is a list of maps, not a single map. This adds a custom type to
translate legacy values, and correctly handles those values in the limiter itself to prevent
crashes.</p>
</li>
<li>
<p>[Smart] Safely handle legacy global tracking data.</p>
<p>During the initial upgrade, legacy producers will have a different shape of tracked data for
partitioned and non-partitioned queues. This addds a translation step to prevent crashing new
producers.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.5.4</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.5.4" />
      <id>https://oban.pro/releases/pro/1.5.4</id>
      <published>2025-03-28T00:00:00Z</published>
      <updated>2025-03-28T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Enhancements</h3>
<ul>
<li>
<p>[Smart] Backport injecting partition keys into job meta.</p>
<p>This injects partition details into jobs as they're inserted. The partition keys will be used
for a generated column in v1.6, and this eases the migration process.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.6.0-rc.0</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.6.0-rc.0" />
      <id>https://oban.pro/releases/pro/1.6.0-rc.0</id>
      <published>2025-03-28T00:00:00Z</published>
      <updated>2025-03-28T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Enhancements</h3>
<ul>
<li>
<p>[Workflow] Add <code>add_workflow/4</code> for creating nested sub-workflows.</p>
<p>Sub-workflows simplify organizing and managing complex job dependencies by grouping related jobs:</p>
<pre class="lumis" style="color: #d8dee9; background-color: #2e3440;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #81a1c1;">Workflow</span><span style="color: #81a1c1;">.</span><span style="color: #88c0d0;">new</span><span style="color: #88c0d0;">(</span><span style="color: #88c0d0;">)</span>
</div><div class="line" data-line="2"><span style="color: #81a1c1;">|&gt;</span> <span style="color: #81a1c1;">Workflow</span><span style="color: #81a1c1;">.</span><span style="color: #88c0d0;">add_workflow</span><span style="color: #88c0d0;">(</span><span style="color: #ebcb8b;">:sub</span><span style="color: #88c0d0;">,</span> <span style="color: #81a1c1;">MyApp.SubWorkflow</span><span style="color: #81a1c1;">.</span><span style="color: #88c0d0;">new</span><span style="color: #88c0d0;">(</span><span style="color: #88c0d0;">)</span><span style="color: #88c0d0;">)</span>
</div><div class="line" data-line="3"><span style="color: #81a1c1;">|&gt;</span> <span style="color: #81a1c1;">Workflow</span><span style="color: #81a1c1;">.</span><span style="color: #88c0d0;">add</span><span style="color: #88c0d0;">(</span><span style="color: #ebcb8b;">:final</span><span style="color: #88c0d0;">,</span> <span style="color: #81a1c1;">FinalWorker</span><span style="color: #81a1c1;">.</span><span style="color: #88c0d0;">new</span><span style="color: #88c0d0;">(</span><span style="color: #88c0d0;">%</span><span style="color: #88c0d0;">&lbrace;</span><span style="color: #88c0d0;">&rbrace;</span><span style="color: #88c0d0;">)</span><span style="color: #88c0d0;">,</span> <span style="color: #ebcb8b;">deps: </span><span style="color: #ebcb8b;">:sub</span><span style="color: #88c0d0;">)</span>
</div></code></pre>
<p>Downstream dependencies can reference the sub-workflow as a whole, including any jobs that may
be dynamically added during runtime.</p>
</li>
<li>
<p>[Workflow] Add <code>add_many/4</code> function for creating fan-out sub-workflows.</p>
<p>This helper enables adding multiple jobs to a workflow with a single name:</p>
<pre class="lumis" style="color: #d8dee9; background-color: #2e3440;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #d8dee9; font-weight: bold;">email_jobs</span> <span style="color: #81a1c1;">=</span> <span style="color: #81a1c1;">Enum</span><span style="color: #81a1c1;">.</span><span style="color: #88c0d0;">map</span><span style="color: #88c0d0;">(</span><span style="color: #d8dee9; font-weight: bold;">users</span><span style="color: #88c0d0;">,</span> <span style="color: #81a1c1;">&amp;</span><span style="color: #81a1c1;">EmailWorker</span><span style="color: #81a1c1;">.</span><span style="color: #88c0d0;">new</span><span style="color: #88c0d0;">(</span><span style="color: #88c0d0;">%</span><span style="color: #88c0d0;">&lbrace;</span><span style="color: #ebcb8b;">user_id: </span><span style="color: #81a1c1;">&amp;</span><span style="color: #81a1c1;">1</span><span style="color: #81a1c1;">.</span><span style="color: #d8dee9; font-weight: bold;">id</span><span style="color: #88c0d0;">&rbrace;</span><span style="color: #88c0d0;">)</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3">workflow <span style="color: #81a1c1;">=</span>
</div><div class="line" data-line="4">  <span style="color: #81a1c1;">Workflow</span><span style="color: #81a1c1;">.</span><span style="color: #88c0d0;">new</span><span style="color: #88c0d0;">(</span><span style="color: #88c0d0;">)</span>
</div><div class="line" data-line="5">  <span style="color: #81a1c1;">|&gt;</span> <span style="color: #81a1c1;">Workflow</span><span style="color: #81a1c1;">.</span><span style="color: #88c0d0;">add_many</span><span style="color: #88c0d0;">(</span><span style="color: #ebcb8b;">:emails</span><span style="color: #88c0d0;">,</span> <span style="color: #d8dee9; font-weight: bold;">email_jobs</span><span style="color: #88c0d0;">)</span><span style="color: #88c0d0;">)</span>
</div><div class="line" data-line="6">  <span style="color: #81a1c1;">|&gt;</span> <span style="color: #81a1c1;">Workflow</span><span style="color: #81a1c1;">.</span><span style="color: #88c0d0;">add</span><span style="color: #88c0d0;">(</span><span style="color: #ebcb8b;">:report</span><span style="color: #88c0d0;">,</span> <span style="color: #81a1c1;">ReportWorker</span><span style="color: #81a1c1;">.</span><span style="color: #88c0d0;">new</span><span style="color: #88c0d0;">(</span><span style="color: #88c0d0;">)</span><span style="color: #88c0d0;">,</span> <span style="color: #ebcb8b;">deps: </span><span style="color: #ebcb8b;">:emails</span><span style="color: #88c0d0;">)</span>
</div></code></pre>
</li>
<li>
<p>[Workflow] Add sub-workflow metadata and query support.</p>
<p>Support retrieving sub-workflow jobs with new <code>with_subs</code> option. This makes it possible to
fetch all sub-workflow jobs, including all recordings, with a single call.</p>
<pre class="lumis" style="color: #d8dee9; background-color: #2e3440;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #81a1c1;">Workflow</span><span style="color: #81a1c1;">.</span><span style="color: #88c0d0;">all_recorded</span><span style="color: #88c0d0;">(</span><span style="color: #d8dee9; font-weight: bold;">job</span><span style="color: #88c0d0;">,</span> <span style="color: #ebcb8b;">with_subs: </span><span style="color: #81a1c1; font-weight: bold;">true</span><span style="color: #88c0d0;">)</span>
</div></code></pre>
</li>
<li>
<p>[Workflow] Add <code>put_context/3</code> for sharing values between workflow jobs.</p>
<p>The helper inserts a completed job with a recorded value for all other jobs to fetch. This
simplifies context sharing and eliminates the need to add the same values as args to all jobs in
a workflow.</p>
<pre class="lumis" style="color: #d8dee9; background-color: #2e3440;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #d8dee9; font-weight: bold;">workflow</span> <span style="color: #81a1c1;">=</span> <span style="color: #81a1c1;">Workflow</span><span style="color: #81a1c1;">.</span><span style="color: #88c0d0;">put_context</span><span style="color: #88c0d0;">(</span><span style="color: #d8dee9; font-weight: bold;">workflow</span><span style="color: #88c0d0;">,</span> <span style="color: #88c0d0;">%</span><span style="color: #88c0d0;">&lbrace;</span><span style="color: #ebcb8b;">id: </span><span style="color: #b48ead;">123</span><span style="color: #88c0d0;">,</span> <span style="color: #ebcb8b;">name: </span><span style="color: #a3be8c;">&quot;Alice&quot;</span><span style="color: #88c0d0;">&rbrace;</span><span style="color: #88c0d0;">)</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #81a1c1;">def</span> <span style="color: #88c0d0;">process</span><span style="color: #88c0d0;">(</span><span style="color: #d8dee9; font-weight: bold;">job</span><span style="color: #88c0d0;">)</span> <span style="color: #81a1c1;">do</span>
</div><div class="line" data-line="4">  <span style="color: #88c0d0;">%</span><span style="color: #88c0d0;">&lbrace;</span><span style="color: #ebcb8b;">id: </span><span style="color: #d8dee9; font-weight: bold;">id</span><span style="color: #88c0d0;">,</span> <span style="color: #ebcb8b;">name: </span><span style="color: #d8dee9; font-weight: bold;">name</span><span style="color: #88c0d0;">&rbrace;</span> <span style="color: #81a1c1;">=</span> <span style="color: #81a1c1;">Workflow</span><span style="color: #81a1c1;">.</span><span style="color: #88c0d0;">get_recorded</span><span style="color: #88c0d0;">(</span><span style="color: #d8dee9; font-weight: bold;">job</span><span style="color: #88c0d0;">,</span> <span style="color: #ebcb8b;">:context</span><span style="color: #88c0d0;">)</span>
</div><div class="line" data-line="5"><span style="color: #81a1c1;">end</span>
</div></code></pre>
</li>
<li>
<p>[Workflow] Add <code>status/1</code> helper for getting workflow execution information.</p>
<p>The new helper simplifies gathering runtime information about a workflow, including the name,
total jbs, overall state of the workflow, elapsed duration, state counts, and timestamps.</p>
<pre class="lumis" style="color: #d8dee9; background-color: #2e3440;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #88c0d0;">%</span><span style="color: #88c0d0;">&lbrace;</span><span style="color: #ebcb8b;">total: </span><span style="color: #b48ead;">5</span><span style="color: #88c0d0;">,</span> <span style="color: #ebcb8b;">counts: </span><span style="color: #88c0d0;">%</span><span style="color: #88c0d0;">&lbrace;</span><span style="color: #ebcb8b;">completed: </span><span style="color: #b48ead;">5</span><span style="color: #88c0d0;">&rbrace;</span><span style="color: #88c0d0;">&rbrace;</span> <span style="color: #81a1c1;">=</span> <span style="color: #81a1c1;">Workflow</span><span style="color: #81a1c1;">.</span><span style="color: #88c0d0;">status</span><span style="color: #88c0d0;">(</span><span style="color: #d8dee9; font-weight: bold;">workflow</span><span style="color: #81a1c1;">.</span><span style="color: #d8dee9; font-weight: bold;">id</span><span style="color: #88c0d0;">)</span>
</div></code></pre>
</li>
<li>
<p>[Workflow] Add <code>retry_jobs/2</code> for retrying workflow jobs.</p>
<p>The new helper function will retry jobs in the workflow and hold jobs with dependencies
accordingly.</p>
<pre class="lumis" style="color: #d8dee9; background-color: #2e3440;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #81a1c1;">Workflow</span><span style="color: #81a1c1;">.</span><span style="color: #88c0d0;">retry_jobs</span><span style="color: #88c0d0;">(</span><span style="color: #d8dee9; font-weight: bold;">job</span><span style="color: #88c0d0;">)</span>
</div></code></pre>
</li>
<li>
<p>[Workflow] Optimize workflow indexes and eliminate containment queries.</p>
<p>A new partial workflow index checks held jobs, workflow ID, and sub-workflow ID at once,
eliminating the need for a GIN index on meta and args.</p>
</li>
<li>
<p>[Smart] Add burst mode for global partitioned queues.</p>
<p>Allows partitioned queues to exceed per-partition limits when capacity is available, while still
respecting the overall concurrency limit:</p>
<pre class="lumis" style="color: #d8dee9; background-color: #2e3440;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #88c0d0;">config</span> <span style="color: #ebcb8b;">:my_app</span><span style="color: #88c0d0;">,</span> <span style="color: #81a1c1;">Oban</span><span style="color: #88c0d0;">,</span>
</div><div class="line" data-line="2">  <span style="color: #ebcb8b;">queues: </span><span style="color: #88c0d0;">[</span>
</div><div class="line" data-line="3">    <span style="color: #ebcb8b;">exports: </span><span style="color: #88c0d0;">[</span>
</div><div class="line" data-line="4">      <span style="color: #ebcb8b;">global_limit: </span><span style="color: #88c0d0;">[</span>
</div><div class="line" data-line="5">        <span style="color: #ebcb8b;">allowed: </span><span style="color: #b48ead;">5</span><span style="color: #88c0d0;">,</span>
</div><div class="line" data-line="6">        <span style="color: #ebcb8b;">burst: </span><span style="color: #81a1c1; font-weight: bold;">true</span><span style="color: #88c0d0;">,</span>
</div><div class="line" data-line="7">        <span style="color: #ebcb8b;">partition: </span><span style="color: #88c0d0;">[</span><span style="color: #ebcb8b;">args: </span><span style="color: #ebcb8b;">:tenant_id</span><span style="color: #88c0d0;">]</span>
</div><div class="line" data-line="8">      <span style="color: #88c0d0;">]</span><span style="color: #88c0d0;">,</span>
</div><div class="line" data-line="9">      <span style="color: #ebcb8b;">local_limit: </span><span style="color: #b48ead;">100</span>
</div><div class="line" data-line="10">    <span style="color: #88c0d0;">]</span>
</div><div class="line" data-line="11">  <span style="color: #88c0d0;">]</span>
</div></code></pre>
</li>
<li>
<p>[Smart] Allow using <code>:meta</code> in partition configuration.</p>
<p>Now queue partitioning can use values from the job's metadata, including composition values such
as <code>workflow_id</code>.</p>
<pre class="lumis" style="color: #d8dee9; background-color: #2e3440;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #88c0d0;">config</span> <span style="color: #ebcb8b;">:my_app</span><span style="color: #88c0d0;">,</span> <span style="color: #81a1c1;">Oban</span><span style="color: #88c0d0;">,</span>
</div><div class="line" data-line="2">  <span style="color: #ebcb8b;">queues: </span><span style="color: #88c0d0;">[</span><span style="color: #ebcb8b;">media: </span><span style="color: #88c0d0;">[</span><span style="color: #ebcb8b;">global_limit: </span><span style="color: #88c0d0;">[</span><span style="color: #ebcb8b;">allowed: </span><span style="color: #b48ead;">1</span><span style="color: #88c0d0;">,</span> <span style="color: #ebcb8b;">partition: </span><span style="color: #88c0d0;">[</span><span style="color: #ebcb8b;">meta: </span><span style="color: #ebcb8b;">:workflow_id</span><span style="color: #88c0d0;">]</span><span style="color: #88c0d0;">]</span><span style="color: #88c0d0;">]</span><span style="color: #88c0d0;">]</span>
</div></code></pre>
</li>
<li>
<p>[Smart] Overhaul queue partitioning for significant performance improvements.</p>
<p>Adds a generated <code>partition_key</code> column with a partial index to <code>oban_jobs</code>. Jobs are
pre-partitioned on insert based on queue configuration, which simplifies partition queries and
provides ~20x faster performance.</p>
</li>
<li>
<p>[Smart] Centralize producer refresh and cleanup.</p>
<p>Producer refreshing and cleanup is now centralized to reduce database load. Rather than one
transaction per queue per node every 30 seconds, there is now one transaction per node every 30
seconds. This offers significant query savings in systems running numerous queues.</p>
</li>
<li>
<p>[Smart] Provide alternate map hashing to avoid collision.</p>
<p>The use of <code>phash2/1</code> could cause collisions between values such as uuids, which led to false
positives for unique checks. This adds an alternate mechanism for hashing that avoids any such
collisions, but it is opt-in for backward compatibility.</p>
<p>To switch, set the following compile time config:</p>
<pre class="lumis" style="color: #d8dee9; background-color: #2e3440;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #88c0d0;">config</span> <span style="color: #ebcb8b;">:oban_pro</span><span style="color: #88c0d0;">,</span> <span style="color: #81a1c1;">Oban.Pro.Utils</span><span style="color: #88c0d0;">,</span> <span style="color: #ebcb8b;">safe_hash: </span><span style="color: #81a1c1; font-weight: bold;">true</span>
</div></code></pre>
</li>
<li>
<p>[Decorator] Add <code>current_job/0</code> to decorated modules.</p>
<p>The <code>current_job/0</code> function allows decorated jobs to access the underlying job for inspection
or to pass as context to other functions such as <code>Backoff</code>, or <code>Workflow</code> helpers.</p>
</li>
<li>
<p>[Decorator] Support recording as a runtime option.</p>
<p>Any job may now be marked as recorded at runtime, including decorated jobs:</p>
<pre class="lumis" style="color: #d8dee9; background-color: #2e3440;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #ebcb8b;">@</span><span style="color: #ebcb8b;">job </span><span style="color: #ebcb8b;">queue: </span><span style="color: #ebcb8b;">:processing</span><span style="color: #88c0d0;">,</span> <span style="color: #ebcb8b;">recorded: </span><span style="color: #81a1c1; font-weight: bold;">true</span>
</div><div class="line" data-line="2"><span style="color: #81a1c1;">def</span> <span style="color: #88c0d0;">process_account</span><span style="color: #88c0d0;">(</span><span style="color: #d8dee9; font-weight: bold;">account_id</span><span style="color: #88c0d0;">)</span> <span style="color: #81a1c1;">do</span>
</div><div class="line" data-line="3">  <span style="color: #616e88;"># ...</span>
</div><div class="line" data-line="4"><span style="color: #81a1c1;">end</span>
</div></code></pre>
</li>
<li>
<p>[DynamicPrioritizer] Expand options for finer control.</p>
<p>New <code>:limit</code> and <code>:max_priority</code> options add control for many jobs are prioritized and their
maximum priority level.</p>
</li>
<li>
<p>[DynamicQueues] Preserve runtime updates without configuration changes.</p>
<p>Add automatic <code>sync_mode</code> to insert/update/delete queues based on configuration and prevent
overwriting runtime updates to queues unless the configuration changes.</p>
</li>
<li>
<p>[DynamicCron] Use optimized cron expression calculation.</p>
<p>The new <code>last_at/2</code> and <code>next_at/2</code> cron expression functions are vastly faster and more
efficient than the previous implementation. This improves cron job insertion performance in
<code>guaranteed</code> mode.</p>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Migration] Drop tables entirely during initial down migration</p>
<p>The presence of a <code>uniq_key</code> column prevents dropping the partitioned table after a rollback.
The specific sequence of migrating and backfilling is unlikely to be used in the real world.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.5.3</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.5.3" />
      <id>https://oban.pro/releases/pro/1.5.3</id>
      <published>2025-03-19T00:00:00Z</published>
      <updated>2025-03-19T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Smart] Use concat operator for workflow rescues.</p>
<p>The concat operator works with both array and json data, so appending to arrays works correctly
with either data type. This is particularly important for CRDB systems because they don't
currently support arrays of JSON.</p>
</li>
<li>
<p>[Smart] Provide alternate args/meta hashing to avoid collision.</p>
<p>The use of phash2/1 could cause collisions between values such as uuids, which led to false
positives for unique checks. This adds an alternate mechanism for hashing that avoids any such
collisions, but it is opt-in for backward compatibility.</p>
<p>To switch, set the following compile time config:</p>
<pre class="lumis" style="color: #d8dee9; background-color: #2e3440;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">config :oban_pro, Oban.Pro.Utils, safe_hash: true
</div></code></pre>
</li>
<li>
<p>[Smart] Catch unique violations while fetching jobs</p>
<p>The <code>fetch_jobs/3</code> function handles numerous state transitions that could trigger a unique
violation. To prevent producer crashes, those violations are caught and fixed before fetching
a</p>
</li>
<li>
<p>[Worker] Ensure hook modules are loaded at runtime</p>
<p>In dynamic, lazy loaded mode it was possible for hooks not to run because calling
function_exported doesn't load the module. Now modules are checked before detecting functions.
gain.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.5.2</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.5.2" />
      <id>https://oban.pro/releases/pro/1.5.2</id>
      <published>2025-02-20T00:00:00Z</published>
      <updated>2025-02-20T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Enhancements</h3>
<ul>
<li>
<p>[Cron] Include configured timezone in cron job metadata.</p>
<p>Along with the cron expression, stored as <code>cron_expr</code>, the configured timezone is also recorded
as <code>cron_tz</code> in cron job metadata. This mirrors new functionality in Oban v2.19.2.</p>
</li>
<li>
<p>[DynamicQueues] Add <code>get/2</code> for fetching a particular dynamic queue.</p>
<p>Using <code>all/1</code> to fetch and filter jobs is inefficient, particularly when there are a large number
of queues.</p>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Smart] Further optimize unique checks for partitioned tables.</p>
<p>The previous attempt at optimizing unique checks for partitioned tables wasn't effective.
Changes were made to enable fully index backed queries.</p>
</li>
<li>
<p>[Decorator] Namespace decorator module attribute.</p>
<p>The <code>decorated</code> module attribute could conflict with module attributes outside of Oban.</p>
</li>
<li>
<p>[DynamicScaler] Avoid casting last_scaled_at for skipped events</p>
<p>When scaling info is handed off quickly without any scaling events the <code>last_scaled_at</code>
timestamp may be <code>nil</code>.</p>
</li>
</ul>]]></content>
    </entry>
  
    <entry>
      <title>Oban Pro v1.5.1</title>
      <link rel="alternate" href="https://oban.pro/releases/pro/1.5.1" />
      <id>https://oban.pro/releases/pro/1.5.1</id>
      <published>2025-02-07T00:00:00Z</published>
      <updated>2025-02-07T00:00:00Z</updated>
      <content type="html"><![CDATA[<h3>Bug Fixes</h3>
<ul>
<li>
<p>[Smart] Optimize fallback partitioned table unique check</p>
<p>The query used a bad combination of join: true and an or where, which would lead to querying far
too many rows for each dupe check.</p>
</li>
<li>
<p>[Smart] Handle smart compile time config with access</p>
<p>Configuration is typically done with keyword lists, not maps. Using an <code>Access</code> call supports
either for backward compatibility.</p>
</li>
<li>
<p>[Migration] Detect partitioning with a backward compatible query</p>
<p>Prior to v1.3.4 the <code>oban_jobs_incomplete</code> table didn't exist, which breaks detecting
partitioned tables for the v1.5 upgrade. This changes detection to look for
<code>oban_jobs_completed</code> instead, which has always been created by the dynamic partitioning
migrations.</p>
</li>
<li>
<p>[Testing] Use real <code>uuid</code> to record draining attempts</p>
<p>The value &quot;draining&quot; can't be cast as a uuid, which made rehydrating the <code>attempted_by</code> field of
drained jobs impossible. This replaces it with a hard-coded, all zero UUID.</p>
</li>
</ul>]]></content>
    </entry>
  
</feed>
