Set lock_timeout Before Blocking DDL in PostgreSQL
Flags PostgreSQL ALTER TABLE and other blocking DDL run without lock_timeout, which can stall every query on the table while it waits for its lock.
What it flags
BEGIN;
-- Waits behind open transactions on orders while new queries queue behind it
ALTER TABLE orders
ADD COLUMN priority smallint NOT NULL DEFAULT 0;
ALTER TABLE orders
ALTER COLUMN status SET DEFAULT 'pending';
COMMIT;BEGIN;
-- Give up after 3s instead of stalling the queries queued behind the lock
SET LOCAL lock_timeout = '3s';
ALTER TABLE orders
ADD COLUMN priority smallint NOT NULL DEFAULT 0;
ALTER TABLE orders
ALTER COLUMN status SET DEFAULT 'pending';
COMMIT;Why it matters
An ALTER TABLE that needs an ACCESS EXCLUSIVE lock must wait for every transaction already using the table, including a slow report or a session left idle in a transaction. While it waits, every new query on that table queues behind it, so a statement that would finish in milliseconds can stop all traffic to the table and exhaust the connection pool. With lock_timeout set to a few seconds, the migration fails fast, the queued queries proceed, and the deploy can retry later. Staging rarely has a long transaction holding the table, so the problem first appears during a production deploy.
Agent instructions
cubic checks every pull request against these instructions. Paste them into AGENTS.md or Cursor rules and your AI coding tool follows them while it writes code.
AGENTS.md
Add this section to AGENTS.md at the repository root. Codex, Cursor, and other AI coding tools read it.
## Set lock_timeout Before Blocking DDL in PostgreSQL Applies to `**/*.sql`, `**/migrations/**`, `**/migrate/**`. Review PostgreSQL migrations for DDL that can wait in the lock queue and stall all traffic to a table. Flag changes that: - Run `ALTER TABLE` on an existing table without setting `lock_timeout` earlier in the same session or transaction. Most forms, including ADD COLUMN, DROP COLUMN, ALTER COLUMN TYPE, SET DEFAULT, SET NOT NULL, ADD CHECK, and RENAME, take an ACCESS EXCLUSIVE lock. - Run other lock-heavy DDL on live tables without `lock_timeout`: ADD FOREIGN KEY (which also locks the referenced table), `CREATE TRIGGER`, `TRUNCATE`, `DROP TABLE`, or `LOCK TABLE`. - Weaken the limit: `SET LOCAL lock_timeout` outside a transaction block (it has no effect there), `lock_timeout = 0` (no limit), or a value in minutes. - Hold an ACCESS EXCLUSIVE lock across slow work in the same transaction, such as a backfill, a full-table scan, or DDL on several busy tables in a row. - Retry a lock timeout in a loop with no attempt limit or delay between attempts. Do not flag: - DDL on tables created earlier in the same migration. - Migrations where the runner configuration, the database role settings, or a shared migration helper already sets `lock_timeout`. - `CREATE INDEX CONCURRENTLY`, `DROP INDEX CONCURRENTLY`, and `ALTER TABLE ... VALIDATE CONSTRAINT`, whose locks do not conflict with reads or writes.
Checks files matching **/*.sql, **/migrations/**, **/migrate/**.
More PostgreSQL agents
See all PostgreSQL code review standards
Run this agent on every pull request
cubic reviews every pull request against the agents you turn on and comments on the lines that break them.