A migration adds a column. It needs a brief exclusive lock, which should take milliseconds.
A long-running query is holding a conflicting lock, so the migration waits. That is fine on its own. What is not fine is that in most databases, lock requests queue — so every subsequent query needing any conflicting lock on that table now waits behind the migration, including ordinary reads that would have been served instantly.
Within seconds the table is effectively unavailable, and the cause is a schema change that has not done anything yet.
What changed in 2026
- Lock timeouts before DDL became standard practice. Migration tooling increasingly sets them automatically.
- The queueing behaviour got better understood. Recognition that a waiting DDL statement blocks readers spread beyond database specialists.
- Retry-with-backoff patterns matured. Automated retry of schema changes became a feature of migration frameworks.
- Online DDL support improved. More operations became possible without long locks, reducing exposure.
Why queueing turns a wait into an outage
Lock requests are generally granted in order. When a request for a strong lock is waiting, later requests for conflicting locks queue behind it rather than jumping ahead — this prevents starvation of the strong request.
The consequence in this scenario: a SELECT that would conflict with the pending exclusive lock waits behind it, even though it does not conflict with the currently-held lock at all.
So the sequence is: a long query holds a weak lock, DDL requests a strong lock and waits, and everything else piles up behind the DDL. Traffic stops on that table, connections fill, and the application starts failing — all triggered by a schema change that never executed.
The fix
Set a short lock timeout before the DDL statement.
With a timeout of a few seconds, the migration attempts to acquire its lock. If it cannot within that window, it fails immediately rather than queueing. The queue never forms, traffic continues, and you retry.
Then retry with backoff. Schema changes usually succeed on a later attempt, when whatever was holding the conflicting lock has finished. Several attempts with a pause between them is far safer than one attempt that waits indefinitely.
| Approach |
Outcome when blocked |
| No lock timeout |
Queue forms, table effectively down |
| Short lock timeout |
DDL fails fast, traffic unaffected |
| Short timeout plus retry |
Fails fast, succeeds later |
| Long lock timeout |
Long queue, delayed outage |
A long lock timeout is close to no timeout — the queue still forms, it just clears eventually.
The other half: what is holding the lock
A lock timeout stops you from causing an outage; it does not get your migration applied. If it fails repeatedly, something is holding a conflicting lock persistently.
The usual culprits are long-running analytical queries, and idle-in-transaction sessions — an application that opened a transaction, touched the table, and then went off to do something slow. The latter is particularly frustrating because the session is doing nothing at all while blocking your change.
Bounding those is the complementary fix, per statement timeouts. A database with sensible statement and idle-in-transaction timeouts has far fewer persistent lock holders for DDL to collide with.
Checking what holds the lock before retrying is worth the minute it takes — most engines expose current locks and the sessions holding them.
Common mistakes
- DDL without a lock timeout on a busy table. The classic self-inflicted outage.
- A long lock timeout. Delays the outage rather than preventing it.
- Retrying immediately without backoff. Repeatedly forms short queues.
- Not investigating persistent blockers. Retrying forever against an idle transaction.
- Assuming a quick operation needs no protection. The lock acquisition is the risk, not the operation.
- No idle-in-transaction timeout. Leaves the most common blocker unbounded.
FAQ
How short should the lock timeout be?
A few seconds is typical. Short enough that a queue does not build meaningfully, long enough to succeed when the table is momentarily quiet.
Does this apply to all schema changes?
Any operation requiring a strong lock, which is most DDL. Some operations have online variants avoiding long locks — check what your engine offers for the specific change.
What about the concurrent index build?
Building an index concurrently avoids the long lock and still needs a brief one at start and finish, so a lock timeout is still worth setting.
Should application queries have lock timeouts?
Usually a short one, so a blocked query fails rather than holding a connection indefinitely. It is a different value from the DDL case.
Where to go next
For bounding query duration and idle transactions, read statement timeouts. For the schema-change pattern that minimises locking, expand and contract migrations, and for cancelling a blocking session, query cancellation.