Skip to content
Latchkey

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.

Rails output
Mysql2::Error: Lock wait timeout exceeded; try restarting transaction
# or (Postgres)
PG::QueryCanceled: ERROR: canceling statement due to statement timeout

Common 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.

db/migrate/*.rb
class AddIndexToOrders < ActiveRecord::Migration[7.1]
  disable_ddl_transaction!
  def change
    add_index :orders, :status, algorithm: :concurrently
  end
end

Set sensible lock and statement timeouts

  1. Configure a short lock_timeout so the migration fails fast instead of hanging, then retry off-peak.
  2. Raise statement_timeout only for the specific long migration if it is legitimately slow.
  3. 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.

Frequently asked questions

What causes "Migration lock/timeout"?
Adding an index or rewriting a big table can exceed a configured statement_timeout/lock_timeout, so the database cancels the statement.
How do I fix Migration lock/timeout?
On Postgres, add indexes concurrently outside a transaction so the migration does not hold a heavy lock.

Related guides

References

Latchkey auto-heals failures like this one - detected, fixed, and retried without you. Start free → 30-day trial · No credit card