Please sign in to comment.
Use advisory locks to prevent concurrent migrations
- Addresses issue #22092 - Works on Postgres and MySQL - Uses advisory locks because of two important properties: 1. The can be obtained outside of the context of a transaction 2. They are automatically released when the session ends, so if a migration process crashed for whatever reason the lock is not left open perpetually - Adds get_advisory_lock and release_advisory_lock methods to database adapters - Attempting to run a migration while another one is in process will raise a ConcurrentMigrationError instead of attempting to run in parallel with undefined behavior. This could be rescued and the migration could exit cleanly instead. Perhaps as a configuration option? Technical Notes ============== The Migrator uses generate_migrator_advisory_lock_key to build the key for the lock. In order to be compatible across multiple adapters there are some constraints on this key. - Postgres limits us to 64 bit signed integers - MySQL advisory locks are server-wide so we have to scope to the database - To fulfil these requirements we use a Migrator salt (a randomly chosen signed integer with max length of 31 bits) that identifies the Rails migration process as the owner of the lock. We multiply this salt with a CRC32 unsigned integer hash of the database name to get a signed 64 bit integer that can also be converted to a string to act as a lock key in MySQL databases. - It is important for subsequent versions of the Migrator to use the same salt, otherwise different versions of the Migrator will not see each other's locks.
- Loading branch information...
Showing with 322 additions and 23 deletions.
- +19 −0 activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
- +14 −0 activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
- +18 −0 activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
- +72 −23 activerecord/lib/active_record/migration.rb
- +28 −0 activerecord/test/cases/adapters/mysql/connection_test.rb
- +28 −0 activerecord/test/cases/adapters/mysql2/connection_test.rb
- +42 −0 activerecord/test/cases/adapters/postgresql/connection_test.rb
- +101 −0 activerecord/test/cases/migration_test.rb
Oops, something went wrong.