Report #96795
[bug\_fix] canceling statement due to lock timeout or migration hangs indefinitely \(ACCESS EXCLUSIVE LOCK\)
The root cause is that DDL operations like ALTER TABLE, CREATE INDEX \(without CONCURRENTLY\), or ADD FOREIGN KEY acquire an ACCESS EXCLUSIVE lock on the table, which blocks all reads and writes. On large, busy tables, even a momentary exclusive lock creates a queue of blocked queries that cascades, effectively locking the table indefinitely or until statement\_timeout/lock\_timeout kills it. The fix for indexes is to use CREATE INDEX CONCURRENTLY, which builds the index without locking the table \(though it takes longer and uses more CPU\). For other schema changes, use online schema change tools like pg-online-schema-change, pt-online-schema-change, or native logical replication to create a new table with the schema, sync data, and swap tables using a metadata lock.
Journey Context:
A senior developer runs a Rails migration to add an index on a 500GB users table in production during business hours. Immediately, error tracking lights up with "ActiveRecord::LockWaitTimeout" and the application becomes unresponsive. The developer checks pg\_stat\_activity and sees the CREATE INDEX query in "active" state but blocking 200 other queries which are all blocked waiting for the lock. Realizing that standard CREATE INDEX takes an ACCESS EXCLUSIVE lock, they cancel the migration. After researching, they learn about the CONCURRENTLY option in PostgreSQL. They modify the migration to use add\_index ... algorithm: :concurrently \(Rails\) or run raw SQL CREATE INDEX CONCURRENTLY. They deploy during low traffic, and the index builds successfully over 30 minutes without ever blocking a single SELECT or INSERT, because CONCURRENTLY uses multiple passes and avoids the exclusive lock.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-22T21:03:19.976208+00:00— report_created — created