The whole operation runs inside a single transaction: every pending
migration's SQL and its ledger row are committed together, or nothing is. If
any migration fails the transaction is rolled back and the database is left
exactly at its previous schema version — there are no partially-applied
upgrades.
For this guarantee to hold, migration .sql files must contain only
transaction-safe statements. PostgreSQL DDL (CREATE TABLE/FUNCTION/INDEX,
ALTER …, …) is transactional, but a few commands are not and must never be
used in a migration: CREATE INDEX CONCURRENTLY, VACUUM, CREATE DATABASE,
etc.
Isolation
A transaction-scoped pg_advisory_xact_lock serializes concurrent starters
(many Queue/Worker instances booting at once across processes), so the
migrations run exactly once. The lock is acquired as the first statement and
released automatically when the transaction commits or rolls back (or if the
connection dies), so it can never leak. Late starters block until the winner
commits, then observe the up-to-date version and no-op.
Namespace
All objects are created in schema (default DEFAULT_SCHEMA), the
connection-level namespace that replaces Redis's per-queue key prefix. The
schema is created if missing and search_path is set (transaction-locally)
so the migrations' unqualified table names resolve into it.
The caller MUST provide a single dedicated session (e.g. a checked-out
pg.PoolClient or a standalone pg.Client), never the pool itself, so the
lock and the transaction share one connection.
Brings the database schema up to LATEST_SCHEMA_VERSION.
Run on the backend's first
waitUntilReady()(a constructor cannot perform async I/O). Behaviour by current database version:Atomicity
The whole operation runs inside a single transaction: every pending migration's SQL and its ledger row are committed together, or nothing is. If any migration fails the transaction is rolled back and the database is left exactly at its previous schema version — there are no partially-applied upgrades.
For this guarantee to hold, migration
.sqlfiles must contain only transaction-safe statements. PostgreSQL DDL (CREATE TABLE/FUNCTION/INDEX,ALTER …, …) is transactional, but a few commands are not and must never be used in a migration:CREATE INDEX CONCURRENTLY,VACUUM,CREATE DATABASE, etc.Isolation
A transaction-scoped
pg_advisory_xact_lockserializes concurrent starters (many Queue/Worker instances booting at once across processes), so the migrations run exactly once. The lock is acquired as the first statement and released automatically when the transaction commits or rolls back (or if the connection dies), so it can never leak. Late starters block until the winner commits, then observe the up-to-date version and no-op.Namespace
All objects are created in
schema(default DEFAULT_SCHEMA), the connection-level namespace that replaces Redis's per-queue key prefix. The schema is created if missing andsearch_pathis set (transaction-locally) so the migrations' unqualified table names resolve into it.The caller MUST provide a single dedicated session (e.g. a checked-out
pg.PoolClientor a standalonepg.Client), never the pool itself, so the lock and the transaction share one connection.