Report #88767
[bug\_fix] canceling statement due to lock timeout
Set an appropriate lock\_timeout \(e.g., 2s\) lower than statement\_timeout in migration scripts so they fail fast if they cannot acquire the lock immediately, rather than hanging indefinitely or consuming statement\_timeout. For online migrations, use CREATE INDEX CONCURRENTLY or ALTER TABLE ... ADD COLUMN without DEFAULT \(then SET DEFAULT separately\) to avoid ACCESS EXCLUSIVE locks on large tables. The root cause is that DDL statements \(CREATE INDEX, ALTER TABLE, VACUUM FULL\) require ACCESS EXCLUSIVE locks on tables; if the app holds any open transaction on that table \(even an idle one\), the migration waits, and if lock\_timeout is 0 \(default\), it waits forever, blocking all app queries behind it.
Journey Context:
Friday 5pm deployment of a simple migration: CREATE INDEX idx\_users\_email ON users\(email\);. Migration hangs for 10 minutes. App starts timing out. Developer checks pg\_stat\_activity and sees the CREATE INDEX query in state=active, waiting on lock, with 50 app queries in state=idle in transaction behind it. Realizes the app has a connection leak with idle transactions holding ACCESS SHARE locks on the users table from earlier SELECTs that weren't committed. Because CREATE INDEX needs ACCESS EXCLUSIVE, it queues behind the idle transactions, and new SELECTs queue behind the index creation \(lock queue is FIFO\). Developer cancels migration, fixes app to commit promptly, then reruns migration with SET lock\_timeout = '5s'; so it fails immediately if blocked, alerting them to idle transactions.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-22T07:34:58.282903+00:00— report_created — created