Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Migrations

Graphile Worker RS stores its database objects in a PostgreSQL schema and tracks the installed schema revision in a migrations table inside that schema. The graphile_worker_migrations crate owns this setup and exposes the migration runner used to install or update the schema.

The public entry point is graphile_worker_migrations::migrate(database, schema). It accepts a database connection/executor value that can be converted into the crate database type, plus the schema name to manage.

use graphile_worker_migrations::migrate;

migrate(&database, "graphile_worker").await?;

What migrate does

On each run, the migration runner reads:

  • the PostgreSQL server_version_num
  • the latest row in <schema>.migrations
  • the latest row in <schema>.migrations marked as a breaking migration

If <schema>.migrations does not exist, the runner installs the base schema tracking objects first:

create schema if not exists graphile_worker;

create table if not exists graphile_worker.migrations (
    id int primary key,
    ts timestamptz default now() not null,
    breaking boolean not null default false
);

After that, it runs every packaged migration whose number is greater than the latest recorded migration id. Each migration runs in its own database transaction. When the SQL completes, the runner inserts a row into the migrations table with the migration id and whether that migration is marked as breaking, then commits the transaction.

Running migrate again after the schema is current is expected to be harmless: the runner sees the latest recorded revision and skips already-applied migrations.

Packaged revisions

Migration SQL is bundled in the graphile_worker_migrations crate under src/sql. At runtime the crate loads those files into the GRAPHILE_WORKER_MIGRATIONS registry in revision order.

The current package contains revisions 1 through 20. The revisions marked as breaking in the registry are:

1, 3, 11, 13, 14, 16

The runner supports taking over from an existing Graphile Worker schema as long as the migrations table accurately records the already-applied revision. For example, if revision 1 is already present, migrate starts at revision 2.

Startup guidance

Run migrations before starting workers that enqueue, poll, or execute jobs:

use graphile_worker_migrations::migrate;

async fn boot(database: &Database) -> Result<(), Box<dyn std::error::Error>> {
    migrate(database, "graphile_worker").await?;

    // Start worker processes only after the schema is installed and current.
    Ok(())
}

This is especially important around breaking revisions. The runner refuses to continue if the database has a newer breaking migration than the currently running package supports. For example, a database whose migrations table records a future breaking revision cannot safely be used by an older worker binary.

If the database records a future non-breaking revision, the runner logs a warning and continues. That warning means the database schema is newer than the current package knows about, so all running Graphile Worker RS versions should be checked for compatibility.

PostgreSQL version check

The migration package requires PostgreSQL 12 or newer. It checks current_setting('server_version_num') and returns an incompatible-version error for versions below 120000.

On a fresh schema install, the PostgreSQL version is checked before creating the schema and migrations table. On existing schemas, the version reported while reading migration state is checked before pending migrations are applied.

Locked jobs during migration 11

Migration 11 has a specific guard for locked jobs. If PostgreSQL returns SQL state 22012 while applying that migration, the runner returns MigrateError::LockedJobInMigration11.

Before applying a package version that still needs to run migration 11, stop workers cleanly and make sure jobs and queues are not left locked. Then rerun migrate.

Custom schemas

The schema argument controls where the Graphile Worker objects are installed:

migrate(&database, "my_worker_schema").await?;

Packaged migration SQL uses the :GRAPHILE_WORKER_SCHEMA placeholder internally. The migration executor replaces that placeholder with the escaped schema name before executing each SQL statement.