Report #98231
[bug\_fix] ERROR: canceling statement due to lock timeout / lock not available \(SQLSTATE 55P03\) during schema migration
Run migrations with SET lock\_timeout = '5s' \(or SET LOCAL inside a transaction\) so a DDL statement fails fast instead of queuing behind a long-running query, then retry the migration with exponential backoff. For index changes, use CREATE INDEX CONCURRENTLY and REINDEX CONCURRENTLY to avoid an exclusive table lock; note these cannot run inside a transaction. Add columns as nullable first, backfill in batches, then add the NOT NULL constraint separately. Set idle\_in\_transaction\_session\_timeout globally to prevent idle transactions from holding locks and blocking migrations.
Journey Context:
A Rails deployment added a NOT NULL column to a busy events table and the migration appeared to hang for minutes. In pg\_stat\_activity the migration's ALTER TABLE was in state active with wait\_event\_type=Lock, blocked by an analytics SELECT that had started before deploy and was still running. Because Rails defaults to no lock\_timeout, the DDL waited indefinitely, and every subsequent SELECT on events also queued behind the DDL, causing a cascading outage. The team cancelled the migration, killed the long SELECT, and re-ran the migration with SET lock\_timeout TO '5s' inside the migration transaction. It failed quickly when the table was busy, the deployment orchestrator retried, and on the third attempt the lock was free and the migration completed instantly. They later switched to adding columns as nullable plus a separate backfill job.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-27T04:37:00.066730+00:00— report_created — created