require-lock-timeout
DDL operations should set lock_timeout to prevent blocking the lock queue indefinitely.
- operation
- Session settings
- lock taken
- ACCESS EXCLUSIVE
- remediation
- Fixed by --fix
- category
- Lock safety
- Can stall the lock queue
What triggers it
Fires on any DDL statement whose computed lock type is ACCESS EXCLUSIVE or SHARE when no preceding statement in the migration set lock_timeout — checked both via a VariableSetStmt named lock_timeout and, as a fallback, by searching the raw SQL text of earlier statements.
What does not
Skips statements that don't take ACCESS EXCLUSIVE or SHARE, SET/RESET/SHOW/transaction-control statements themselves (VariableSetStmt, VariableShowStmt, TransactionStmt), and plain CREATE TABLE — a new table has no contention to wait on. It also stands down once any earlier statement in the file already set lock_timeout.
Where it applies
Applies to every PostgreSQL version MigrationPilot targets. It works on the SQL text alone — no database connection needed.
The lock, and what it blocks
This rule isn't really about the lock the DDL takes — it's about what happens while it waits to get it: without lock_timeout, a blocked DDL statement queues indefinitely behind whatever holds the lock, and every later query on that table queues behind the DDL in turn.
Why it matters
Without lock_timeout, if the table is locked by another query, your DDL waits indefinitely. All subsequent queries pile up behind it in the lock queue, causing cascading timeouts across your application.
Unsafe, and safe
Flagged
ALTER TABLE users ADD COLUMN bio TEXT;
Safe alternative
SET lock_timeout = '5s'; ALTER TABLE users ADD COLUMN bio TEXT; RESET lock_timeout;
What it assumes
The lock_timeout lookback only checks earlier statements in the same migration file — a lock_timeout set at the role or database level, or by a wrapper script before the migration runs, won't be seen, so the rule can flag a statement that's already covered outside the file it's analyzing.
What backs this rule
Every rule is a claim about PostgreSQL, so it carries what the claim rests on: a handbook chapter that cites the manual, the incidents that put it there, and the version it was last checked against.
This is the entry that matters most.
- verified against
- PostgreSQL 17.10
- last checked
- 2026-08-11
- confidence
- High
PostgreSQL manual
Public incidents and write-ups
- GoCardless: Zero-downtime Postgres migrations — the hard parts2024-06
- Xata: Schema changes and the Postgres lock queue2024-06-18
- GitLab.com production incident #6642: Post Deploy migrations Failure on Auto-Deploy2022-03-18
- postgres.ai: Zero-downtime Postgres schema migrations need this — lock_timeout and retries2021-09-23
What the CLI prints
✗ [MP004] CRITICAL (line 1) DDL statement acquires ACCESS EXCLUSIVE lock without a preceding SET lock_timeout. Without a timeout, this statement could block the lock queue indefinitely if it can't acquire the lock, causing cascading query failures. Safe alternative: -- Set a timeout so DDL fails fast instead of blocking the queue SET lock_timeout = '5s'; ALTER TABLE users ADD COLUMN bio TEXT RESET lock_timeout; Why: Without lock_timeout, if the table is locked by another query, your DDL waits indefinitely. All subsequent queries pile up behind it in the lock queue, causing cascading timeouts across your application. GoCardless enforces a 750ms lock_timeout for this reason. Docs: https://migrationpilot.dev/rules/mp004
Generated by running the CLI's own formatter over the flagged example above, so it is the text the tool actually produces. A real run also reports the other rules that fire on the same statement; those blocks are left out here.
Turning it off
For one statement, put a comment on the line before it:
-- migrationpilot-disable MP004 ALTER TABLE users ADD COLUMN bio TEXT;
For the whole project, in .migrationpilotrc.yml — by name or by id:
rules:
MP004: false
# or keep it, and downgrade it
rules:
MP004:
severity: warningTry it
Open this rule's flagged example in the playground. It runs in your browser — edit it and watch the finding appear and disappear.
Run MP004 in the playground