Upgrading to v1.7

This release introduces database-backed workflow tracking, replaces generated columns with expression indexes, and adds new partial indexes for improved performance. The guide covers running the required migration, optional cleanup steps for reclaiming storage, switching deprecated callbacks, and enabling usage rules for AI coding assistants.

If you're upgrading from Oban to Pro for the first time, you can jump straight to v1.7 without any concern about table locking from generated columns.

Bump Your Deps

Update Oban and Pro to the latest versions:

{:oban_pro, "~> 1.7.0-rc.0", repo: "oban"},

Run Oban.Pro.Migration (Required)

Generate a new migration:

mix ecto.gen.migration upgrade_oban_pro_to_1_7

Within the generated migration module:

use Ecto.Migration

def up, do: Oban.Pro.Migration.up(version: "1.7.0")

def down, do: Oban.Pro.Migration.down(version: "1.7.0")

For large tables, split the schemas and indexes migrations so indexes can be created concurrently:

mix ecto.gen.migration upgrade_oban_pro_schemas_to_1_7
mix ecto.gen.migration upgrade_oban_pro_indexes_to_1_7
defmodule MyApp.Repo.Migrations.UpgradeObanProSchemasTo17 do
  use Ecto.Migration

  def up, do: Oban.Pro.Migration.up(version: "1.7.0", only: :schemas)
  def down, do: Oban.Pro.Migration.down(version: "1.7.0", only: :schemas)
end

defmodule MyApp.Repo.Migrations.UpgradeObanProIndexesTo17 do
  use Ecto.Migration

  @disable_migration_lock true
  @disable_ddl_transaction true

  def up, do: Oban.Pro.Migration.up(version: "1.7.0", only: :indexes)
  def down, do: Oban.Pro.Migration.down(version: "1.7.0", only: :indexes)
end

See the Oban.Pro.Migration module docs for additional options.

The Oban.Pro.Workflow.after_cancelled/2 callback is deprecated in favor of the universal Oban.Pro.Worker.on_cancelled/2 hook. Currently, both hooks will be called if defined, so you should switch to on_cancelled/2 to avoid duplicate execution:

defmodule MyApp.MyWorker do
  use Oban.Pro.Worker, queue: :default

-  @impl Oban.Pro.Workflow
-  def after_cancelled(%Oban.Job{} = job, reason) do
+  @impl Oban.Pro.Worker
+  def on_cancelled(%Oban.Job{} = job, reason) do
    Logger.info("Job #{job.id} cancelled: #{inspect(reason)}")
    :ok
  end
end

After upgrading to v1.7, the uniq_key and partition_key generated columns are no longer used. You can optionally drop them to simplify your schema and reclaim storage space.

Generate a migration:

mix ecto.gen.migration drop_oban_generated_columns

Then add the column removals:

defmodule MyApp.Repo.Migrations.DropObanGeneratedColumns do
  use Ecto.Migration

  def change do
    alter table(:oban_jobs) do
      remove_if_exists :uniq_key, :text
      remove_if_exists :partition_key, :text
    end
  end
end

CockroachDB Users

CockroachDB does not support expression indexes with function calls in the WHERE clause. If you are using CockroachDB, you must keep the generated columns and use the generated_columns: true option when running migrations:

def up, do: Oban.Pro.Migration.up(version: "1.7.0", generated_columns: true)

The v1.7 migration creates new workflow, sub-workflow, and chain indexes that use the suspended state instead of the old on_hold pseudo-state. The old indexes are renamed with an _old suffix and can be dropped after the migration completes.

Generate a migration:

mix ecto.gen.migration drop_oban_legacy_workflow_indexes

Then drop the indexes concurrently:

defmodule MyApp.Repo.Migrations.DropObanLegacyWorkflowIndexes do
  use Ecto.Migration

  @disable_migration_lock true
  @disable_ddl_transaction true

  def change do
    drop_if_exists index(:oban_jobs, [:id], name: :oban_jobs_workflow_index_old, concurrently: true)
    drop_if_exists index(:oban_jobs, [:id], name: :oban_jobs_sup_workflow_index_old, concurrently: true)
    drop_if_exists index(:oban_jobs, [:id], name: :oban_jobs_chain_index_old, concurrently: true)
  end
end

Enable Usage Rules (Optional)

Oban Pro v1.7 ships with usage rules—reference documents that help coding agents understand Pro's idioms and best practices. If you use Claude Code, Cursor, etc., enable usage rules to get better suggestions when working with Pro.

Add the usage_rules package to your dependencies:

{:usage_rules, "~> 1.2"}

Then configure it in mix.exs to include Oban Pro's rules:

defp usage_rules do
  [
    usage_rules: [:oban_pro, ...]
  ]
end

Then run:

mix deps.get && mix usage_rules.sync

Re-run mix usage_rules.sync after upgrading Pro to get updated rules.

Drop Args and Meta GIN Indexes (Optional)

The args and meta GIN indexes were added by earlier Oban migrations for ad-hoc querying, but they are not required for normal operation. These indexes can be quite large, and removing them may reclaim significant storage space.

Generate a migration:

mix ecto.gen.migration drop_oban_gin_indexes

Then drop the indexes concurrently:

defmodule MyApp.Repo.Migrations.DropObanGinIndexes do
  use Ecto.Migration

  @disable_migration_lock true
  @disable_ddl_transaction true

  def change do
    drop_if_exists index(:oban_jobs, [:args], name: :oban_jobs_args_index, concurrently: true)
    drop_if_exists index(:oban_jobs, [:meta], name: :oban_jobs_meta_index, concurrently: true)
  end
end

Before Dropping

Only drop these indexes if you don't query jobs by arbitrary args or meta fields. The specialized indexes Pro uses for workflows, chains, etc. will remain and be used internal queries.