Rails Migration Timeout / Lock Wait in CI
A migration took too long or waited on a lock it could not get. Either the DDL itself is slow on a large table, or another session held a lock until a timeout fired. This is about the operation, not connectivity.
What this error means
rails db:migrate hangs and then fails with a statement/lock-wait timeout, or the CI step hits its own timeout while a migration is mid-flight. It reproduces when the table is large or a lock is contended.
Mysql2::Error: Lock wait timeout exceeded; try restarting transaction
# or (Postgres)
PG::QueryCanceled: ERROR: canceling statement due to statement timeoutCommon causes
Slow DDL on a large table
Adding an index or rewriting a big table can exceed a configured statement_timeout/lock_timeout, so the database cancels the statement.
Blocking lock held by another session
A long transaction or a competing migration holds a lock; the migration waits and then fails with a lock-wait timeout.
A genuinely long migration in a time-boxed step
The migration is correct but slower than the CI step or database timeout allows, so it is cancelled rather than completing.
How to fix it
Build indexes without blocking
On Postgres, add indexes concurrently outside a transaction so the migration does not hold a heavy lock.
class AddIndexToOrders < ActiveRecord::Migration[7.1]
disable_ddl_transaction!
def change
add_index :orders, :status, algorithm: :concurrently
end
endSet sensible lock and statement timeouts
- Configure a short
lock_timeoutso the migration fails fast instead of hanging, then retry off-peak. - Raise
statement_timeoutonly for the specific long migration if it is legitimately slow. - Avoid running migrations while another long transaction holds the table.
Split a heavy migration
Break a large data/DDL change into smaller steps (add nullable column, backfill in batches, then enforce) so no single statement runs long.
How to prevent it
- Use
algorithm: :concurrently/ non-blocking DDL for large tables. - Backfill data in batches separate from the schema change.
- Run migrations when contention is low and set explicit lock timeouts.