Report #5370
[bug\_fix] Migration hang / DDL timeout: CREATE INDEX blocked indefinitely by AccessExclusiveLock
Use CREATE INDEX CONCURRENTLY \(or REINDEX CONCURRENTLY\) instead of standard CREATE INDEX, and ensure long-running queries are terminated before migration. Root cause: Standard CREATE INDEX requires an AccessExclusiveLock on the table, which conflicts with every other lock; if a long-running SELECT or idle transaction holds a ShareLock, the DDL queues indefinitely, blocking all subsequent queries.
Journey Context:
You trigger an ActiveRecord migration to add an index on a 2TB table during a "low traffic" window. The migration command hangs for 45 minutes. In another terminal, you query pg\_stat\_activity and see your CREATE INDEX is in "waiting" state, blocked by a BI dashboard query \(SELECT \* FROM large\_table\) that has been "idle in transaction" for 3 hours. Meanwhile, your app's connection pool fills up because new requests are queuing behind the waiting DDL. You realize the standard CREATE INDEX is trying to acquire an AccessExclusiveLock \(the strongest lock\), which cannot coexist with the ShareLock held by the idle transaction. You terminate the BI query \(pg\_terminate\_backend\), and the index creation completes in 2 minutes. To prevent this, you read the Postgres docs and discover CREATE INDEX CONCURRENTLY, which only takes a ShareLock \(allowing reads/writes\) and builds the index in two passes. You test this in staging: it takes longer overall but doesn't block. You update the migration to use add\_index ... algorithm: :concurrently \(Rails\) or raw SQL with CONCURRENTLY. You also set idle\_in\_transaction\_session\_timeout to kill long idle transactions. The next deployment proceeds without blocking because the concurrent index build never requests the heavyweight lock that conflicts with application queries.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-15T21:09:58.042108+00:00— report_created — created