fix(ploeg): serialize schema migrations with an advisory lock #149
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "ryangr0/ploeg-migration-advisory-lock"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The migration runner checked schema_migrations outside each migration's
transaction and held no lock across the loop. Two ploegd processes
starting together could both decide a migration was unapplied, and one
failed startup (in the regression test, already on the concurrent
CREATE TABLE IF NOT EXISTS schema_migrations).
Migrate now acquires one pool connection, takes a session
pg_advisory_lock keyed on hashtextextended('ploeg.schema-migrations', 0)
and runs the whole create-check-apply loop on that connection. The lock
is released in a deferred unlock that also runs on error, with a
context detached from cancellation; if the unlock fails the connection
is hijacked and closed so the pool never hands out a connection that
still holds the lock. A test runs two migrators concurrently against a
fresh database in the embedded PostgreSQL: both return without error,
each migration is recorded once and no advisory lock remains.
The new concurrent-migrator test failed 3/3 against the old code (duplicate pg_type from the racing CREATE TABLE) and passes 5/5 with the lock.
Verified with
mise run verifyon the pinned toolchain (all gates passed).Ticket: https://vikunja.webgrip.dev/tasks/1726
🤖 Generated with Claude Code
The migration runner checked schema_migrations outside each migration's transaction and held no lock across the loop. Two ploegd processes starting together could both decide a migration was unapplied, and one failed startup (in the regression test, already on the concurrent CREATE TABLE IF NOT EXISTS schema_migrations). Migrate now acquires one pool connection, takes a session pg_advisory_lock keyed on hashtextextended('ploeg.schema-migrations', 0) and runs the whole create-check-apply loop on that connection. The lock is released in a deferred unlock that also runs on error, with a context detached from cancellation; if the unlock fails the connection is hijacked and closed so the pool never hands out a connection that still holds the lock. A test runs two migrators concurrently against a fresh database in the embedded PostgreSQL: both return without error, each migration is recorded once and no advisory lock remains. VIK-1726 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>