Packages

A durable, resumable workflow engine for Elixir

Current section

Files

Jump to
durable lib durable migration.ex
Raw

lib/durable/migration.ex

defmodule Durable.Migration do
@moduledoc """
Migrations for Durable workflow engine.
Durable requires several database tables to store workflow executions,
step executions, pending inputs, and scheduled workflows.
## Usage
Create a migration in your application:
defmodule MyApp.Repo.Migrations.AddDurable do
use Ecto.Migration
def up, do: Durable.Migration.up()
def down, do: Durable.Migration.down()
end
Then run the migration:
mix ecto.migrate
When Durable adds new internal migrations in a future release, generate a
new host-app wrapper migration:
mix durable.gen.upgrade -r MyApp.Repo
mix ecto.migrate
To check whether a database is behind the Durable library version:
mix durable.migrations -r MyApp.Repo --check
## Options
* `:prefix` - The PostgreSQL schema name (default: `"durable"`)
* `:to` - Migrate up to a specific version (inclusive)
* `:step` - Number of migrations to roll back (for down only)
## Examples
# Default prefix - runs all pending migrations
def up, do: Durable.Migration.up()
# Custom prefix for isolation
def up, do: Durable.Migration.up(prefix: "my_app_durable")
# Rollback the last 2 migrations
def down, do: Durable.Migration.down(step: 2)
## Version History
Migration versions use timestamps (YYYYMMDDHHmmss format). Use
`Durable.Migration.migrated_versions/1` to see applied migrations.
* 20260103000000 - Initial schema with all core tables:
* `workflow_executions` - Stores workflow instances with compensation support
* `step_executions` - Stores step execution history including compensation steps
* `pending_inputs` - Stores pending human-in-the-loop inputs
* `scheduled_workflows` - Stores cron-scheduled workflow definitions
"""
alias Durable.Migration.Migrator
@doc """
Runs all pending migrations up to the latest version.
## Options
* `:prefix` - The PostgreSQL schema name (default: `"durable"`)
* `:to` - Migrate up to a specific version (inclusive)
* `:log` - Log level for migration output (default: `:info`)
"""
@spec up(keyword()) :: :ok
defdelegate up(opts \\ []), to: Migrator
@doc """
Rolls back migrations.
## Options
* `:prefix` - The PostgreSQL schema name (default: `"durable"`)
* `:to` - Rollback to a specific version (exclusive - keeps that version)
* `:step` - Number of migrations to roll back (default: all)
* `:log` - Log level for migration output (default: `:info`)
"""
@spec down(keyword()) :: :ok
defdelegate down(opts \\ []), to: Migrator
@doc """
Returns the list of all available migration versions.
"""
@spec all_versions() :: [pos_integer()]
defdelegate all_versions(), to: Migrator
@doc """
Returns the latest available Durable migration version.
"""
@spec current_version() :: pos_integer()
defdelegate current_version(), to: Migrator
@doc """
Returns the migration version immediately before `version`, or 0 for the first migration.
"""
@spec previous_version(pos_integer()) :: non_neg_integer()
defdelegate previous_version(version \\ Migrator.current_version()), to: Migrator
@doc """
Returns the list of applied migration versions.
Requires a repo connection to be available (called within an Ecto migration).
"""
@spec migrated_versions(keyword()) :: [pos_integer()]
defdelegate migrated_versions(opts \\ []), to: Migrator
@doc """
Returns the latest applied Durable migration version, or 0 when none are applied.
Pass an Ecto repo to check outside an Ecto migration:
Durable.Migration.migrated_version(MyApp.Repo)
Durable.Migration.migrated_version(MyApp.Repo, prefix: "private")
Without a repo, this uses the current Ecto migration runner context.
"""
@spec migrated_version(keyword() | module()) :: non_neg_integer()
def migrated_version(opts_or_repo \\ [])
def migrated_version(opts) when is_list(opts), do: Migrator.migrated_version(opts)
def migrated_version(repo) when is_atom(repo), do: Migrator.migrated_version(repo)
@spec migrated_version(module(), keyword()) :: non_neg_integer()
defdelegate migrated_version(repo, opts), to: Migrator
@doc """
Returns pending migrations (not yet applied).
"""
@spec pending_versions(keyword() | module()) :: [pos_integer()]
def pending_versions(opts_or_repo \\ [])
def pending_versions(opts) when is_list(opts), do: Migrator.pending_versions(opts)
def pending_versions(repo) when is_atom(repo), do: Migrator.pending_versions(repo)
@spec pending_versions(module(), keyword()) :: [pos_integer()]
defdelegate pending_versions(repo, opts), to: Migrator
end