Report #53352
[bug\_fix] canceling statement due to lock timeout \(DDL migrations\)
Set \`SET lock\_timeout = '5s'\` in the migration session to fail fast rather than hanging indefinitely, kill existing idle transactions holding locks on the table, or use online schema change tools \(pg\_repack, pg-online-schema-change\) that use copy-swap or trigger-based methods avoiding long exclusive locks. Root cause is long-running transactions \(idle in transaction or active queries\) hold ACCESS SHARE or ROW locks on the table, blocking the migration's ALTER TABLE command from acquiring the required ACCESS EXCLUSIVE lock.
Journey Context:
You run a Django migration adding a non-nullable column to a 100GB table during business hours. The migration hangs for 30 minutes with no CPU or disk activity. You query \`pg\_stat\_activity\` and see the migration's ALTER TABLE statement in "active" state with \`wait\_event\_type: lock\` and \`wait\_event: relation\`. Checking \`pg\_locks\`, you see the migration is waiting for an ACCESS EXCLUSIVE lock on the table, but there's an "idle in transaction" connection from yesterday's analytics query \(pgAdmin left open\) holding an ACCESS SHARE lock. Postgres queues the exclusive lock behind all existing shared locks, and the migration waits indefinitely. You cancel the migration, kill the idle analytics connection using \`pg\_terminate\_backend\(\)\`, and set \`lock\_timeout = '10s'\` in the migration config so it fails fast if blocked. For zero-downtime, you switch to \`pg\_online\_schema\_change\` which creates a new table, copies data via triggers, and swaps names, holding the exclusive lock only for milliseconds during the swap.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-19T20:02:46.629498+00:00— report_created — created