Report #27651
[bug\_fix] ERROR: canceling statement due to lock timeout \(SQLSTATE 57014\) or migration hangs indefinitely waiting for AccessExclusiveLock
Use \`CREATE INDEX CONCURRENTLY\` instead of \`CREATE INDEX\` to avoid taking an AccessExclusiveLock that blocks all writes, or for other DDL, use \`SET lock\_timeout = '5s';\` to fail fast rather than hanging indefinitely, and schedule schema changes during maintenance windows.
Journey Context:
You run a Django migration to add an index on a 500GB production table. You execute \`CREATE INDEX idx\_foo ON events \(created\_at\);\` and the command appears to hang. In another session, you check \`SELECT \* FROM pg\_locks WHERE NOT granted;\` and see your migration waiting on an AccessExclusiveLock, while \`pg\_stat\_activity\` shows a pile-up of blocked 'UPDATE' queries from production traffic. You realize \`CREATE INDEX\` \(without CONCURRENTLY\) takes an AccessExclusiveLock at the start and end of the build to update the catalog, blocking all writes \(and briefly reads\) for the entire duration. You cancel the query, but the lock request remains in the queue. You eventually kill the blocked queries, causing a brief outage. The correct approach is \`CREATE INDEX CONCURRENTLY\`, which takes only a ShareUpdateExclusiveLock, allowing reads and writes to continue. It scans the table twice and validates at the end, so it takes longer but doesn't block. You run the concurrent version, the migration succeeds without downtime, and you update your migration framework to use \`CONCURRENTLY\` for Postgres.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-18T00:48:30.316107+00:00— report_created — created