Use session-level lock timeout for migrations with disable_ddl_transaction!
Summary
This PR addresses issue #16 by implementing session-level lock timeouts for migrations that use disable_ddl_transaction!.
Problem
Previously, migrations using disable_ddl_transaction! had no lock timeout protection because the gem skipped timeout application entirely. This left concurrent index creation and other non-transactional operations vulnerable to indefinite lock waits, which could cause production issues (as mentioned in the issue comments).
The original implementation couldn't use SET LOCAL lock_timeout because it only works within transactions, and disable_ddl_transaction! runs migrations outside of transactions.
Solution
- Non-transactional migrations: Now use session-level timeout (
SET lock_timeout = '5s') - Transactional migrations: Continue using transaction-level timeout (
SET LOCAL lock_timeout = '5s') - Automatic cleanup: Session-level timeout is automatically reset after non-transactional migrations complete (
RESET lock_timeout)
Breaking Change ⚠️
This is a breaking change that bumps the version to 2.0.0:
Before: Migrations with disable_ddl_transaction! had no timeout → waited indefinitely
After: These migrations now fail if locks can't be acquired within the configured timeout
Migration Guide
Users can adapt in three ways:
- Disable timeout for specific migration:
class AddIndexConcurrently < ActiveRecord::Migration
disable_ddl_transaction!
disable_lock_timeout! # Explicitly opt-out
def change
add_index :large_table, :column, algorithm: :concurrently
end
end
- Set custom timeout:
class AddIndexConcurrently < ActiveRecord::Migration
disable_ddl_transaction!
set_lock_timeout 30 # Wait up to 30 seconds
def change
add_index :large_table, :column, algorithm: :concurrently
end
end
- Adjust default configuration in the initializer
Changes
- Modified
LockManager#migrateto detectdisable_ddl_transactionand use appropriate timeout type - Added
ensureblock to reset session-level timeout after non-transactional migrations - Updated tests to verify session-level timeout behavior (all tests passing ✅)
- Comprehensive documentation updates in README and CHANGELOG
- Added migration examples and troubleshooting guide
Testing
All tests pass on ActiveRecord 7.1 and 7.1 with strong_migrations:
13 examples, 0 failures
Documentation
- Updated README with technical explanation of why session-level timeouts are needed
- Added code examples for both opting out and customizing timeouts
- Added CHANGELOG entry with breaking change notice and migration guide
- Marked as version 2.0.0 per semantic versioning
Fixes #16