-
Serialize to JSON
cursorvalue for background data migrationsNote: Run
bin/rails generate online_migrations:upgradeif using background migrations. -
Do not consider running background migrations as stuck
-
Do not automatically retry stuck background migrations
- Drop support for Ruby < 3.3
- Defer
tick_totalcalculation to the background data migration starting time instead of enqueueing time - Make "replacing an index" check independent of the live database state
-
Drop support for Rails < 7.2
-
Do not schedule background data migrations with non existent data migration classes
This prevents a situation when the background data migration was enqueued and started running before the deploy completes and the data migration class is available.
-
Add ability to enqueue background migrations for specific shards (if using sharding)
add_index_in_background(:users, :name, connection_class_name: "ApplicationRecord", shard: :shard_one) enqueue_background_data_migration("MyMigration", shard: :shard_one)
-
Fix validating foreign key in
add_reference_concurrentlywhen multiple foreign keys target the same table
- Fix message for adding to
ignored_columnswhen renaming a column - Schedule background data migration jobs only after committing the database transaction
- Allow "running" to "enqueued" state transition for stuck background data migrations
-
Change background migrations default status to "pending"
Note: Run
bin/rails generate online_migrations:upgradeif using background migrations. -
Fix copying partial indexes when changing column type
-
Add ability to create delayed background migrations
add_index_in_background(:users, :name, connection_class_name: "ApplicationRecord", delay: true) enqueue_background_data_migration("MyMigration", delay: true)
They will start only after approval from the user.
-
Fix enqueueing background data migrations with the same name on different tables
Note: Run
bin/rails generate online_migrations:upgradeif using background schema migrations.
- Fix running background data migrations inline
- Do not attempt running background migrations on database with
database_tasks: false
- Store backtraces for failed background data migrations jobs
- Make
add_columnidempotent - Fix
NullLockRetrier#with_lock_retriesmethod signature
- Fix
remove_check_constraintwhen using:if_exists - Add schema statement to lock retrier
- Fix an edge case where a background index might not be added
- Fix analyzing tables when indexes are added
- Fix idempotency for adding/removing indexes in background
-
Add
iteration_pausecolumn to background data migrationsNote: Run
bin/rails generate online_migrations:upgradeif using background data migrations. Make sure all data migrations finished before applying this migration or change its timestamp to be older than all pending db migrations which will enqueue data migrations.
- Support renaming columns and tables for tables within custom schemas
- Include operation name into the background schema migrations names
- Fix background data migrations to enumerate using the correct shard
-
WARNING: This release has breaking changes! See
docs/0.27-upgrade.mdfor release notes -
Add ability to run background data migrations in parallel
# Run 2 data migrations in parallel. OnlineMigrations.run_background_data_migrations(concurrency: 2)
-
Retry deadlocks by lock retrier
-
Fix copying check constraints on columns with similar names when changing column type
-
Drop support for Ruby < 3.1 and Rails < 7.1
-
Add check for
change_columnfor columns with check constraints -
Allow to require safety reason explanation when calling
safety_assured# config/initializers/online_migrations.rb config.require_safety_assured_reason = true # in migration safety_assured("Table is small") do add_index :users, :email, unique: true end
-
Track start/finish time of background data migrations
Note: Make sure to run
bin/rails generate online_migrations:upgradeif using background migrations. -
Add new state for errored background migrations
- errored - migration raised an error during last run
- failed - migration raises an error when running and retry attempts exceeded
-
Fix thread safety issue for lock retrier
Note: Lock retrier changed its API (
LockRetrier#connectionaccessor was removed andLockRetrier#with_lock_retriesnow accepts a connection argument). This change might be of interest if you implemented a custom lock retrier class.
-
Add ability to run a separate background migrations scheduler per shard
-
Revert "Prevent multiple instances of schedulers from being running simultaneously"
The feature was implemented using advisory locks, but they do not always play nicely with the database poolers, so the feature was reverted. It is expected now for user's code to prevent multiple instances of the schedulers from being run (by wrapping it into Redis lock etc).
-
Prevent multiple instances of schedulers from being running simultaneously
-
Reduce default batch sizes for background data migrations
batch_sizewas 20_000, now 1_000;sub_batch_sizewas 1_000, now 100 -
Remove deprecated code
-
Drop support for PostgreSQL < 12
-
Drop support for Ruby < 3.0 and Rails < 7.0
-
Make background data migrations scheduler run a single migration at a time
This is similar to background schema migrations scheduler's behavior. Running multiple migrations at a time can cause unneeded database load and other bad effects.
-
Add
#pausable?,#can_be_cancelled?and#can_be_paused?helpers to background migrations
-
Fix
add_foreign_keywhen referencing same table via different columns -
Make
validate_not_null_constraint,remove_foreign_keyandremove_check_constraintidempotent -
Add helpers for validating constraints in background
validate_foreign_key_in_background(:users, :companies) validate_constraint_in_background(:users, "first_name_not_null")
- Fix running background migrations over relations with duplicate records
- Fix background data migrations to work with
includes/eager_loadon relation - Fix problem with running migrations for
activerecord8.0.0.rc2
- Enhance columns removal check to check indexes
whereandincludeoptions - Do not wait before running retried background migrations
- Fix
add_reference_concurrentlywhen adding non polymorphic references - Fix progress for background migrations with small number of records
- Fix
add_reference_concurrentlywhen adding polymorphic references
-
Fix an edge case for background schema migrations
If you use background schema migrations, you need to run
bin/rails generate online_migrations:upgrade bin/rails db:migrate -
Fix retrying running stuck background schema migrations
-
Fix renaming columns for tables with long names
- Fix idempotency for
add_index/remove_indexfor expression indexes
- Fix
add_reference_concurrentlyto be idempotent when adding a foreign key - Fix
enqueue_background_data_migrationto be idempotent
- Fix
add_index_in_backgroundto be idempotent
- Add ability to cancel background migrations
- Fix setting
started_at/finished_atfor parents of sharded background schema migrations - Improve retrying of failed sharded background migrations
- Fix a bug when retried background data migration can not start
- Do not run multiple background schema migrations on the same table at the same time
- Fix raising in development when using sharding and background index creation/removal was not enqueued
- Fix background migrations
#progresspossibility to fail with zero division error - Add
ensure_background_data_migration_succeededandensure_background_schema_migration_succeededmigration helpers - Raise in development when background index creation/removal was not enqueued
- Suggest two migrations for adding foreign keys
- Reraise errors when running background schema migrations inline
- Improve error message when background schema migration name is already taken
- Fix copying column background migration to work with primary keys added via
initialize_column_type_change
-
Add support for asynchronous creation/removal of indexes
See
docs/background_schema_migrations.mdfor the feature description.
- Reraise errors when running background migrations inline
- Add
remove_background_migrationmigration helper - Allow adding bigint foreign keys referencing integer primary keys
- Fix
add_reference_concurrentlyto check for mismatched key types
- Fix
MigrationRunnerto considerrun_background_migrations_inlineproc
-
Add ability to configure whether background migrations should be run inline
The previous behavior of running inline in development and test environments is preserved, unless overriden.
config.run_background_migrations_inline = -> { Rails.env.local? }
- Fix calculation of batch ranges for background migration created with explicit ranges
-
Add ability to configure the path where generated background migrations will be placed
# It is placed in lib/ by default. config.background_migrations.migrations_path = "app/lib"
-
Reduce number of queries needed to calculate batch ranges for background migrations
-
Fix
finalize_column_type_changeto not recreate already existing indexes on the temporary column -
Remove potentially heavy queries used to get the ranges of a background migration
-
Require passing model name for background migration helpers when using multiple databases
-
Add
statement_timeoutconfiguration option -
Make
lock_timeoutargument optional forconfig.lock_retrierThis way, a default lock timeout value will be used (configured in
database.ymlor for the database user). -
Fix a bug that can lead to unfinished children of a sharded background migration
- Fix calculation of batch ranges for sharded background migrations
-
Support sharding for background migrations
Now, if a
relationinside background migration definition is defined on a sharded model, then that background migration would automatically run on all the shards.To get all the new sharding related schema changes, you need to run:
$ bin/rails generate online_migrations:upgrade $ bin/rails db:migrate
-
Change background migration
progressto return values in range from 0.0 to 100.0Previously, these were values in range from 0.0 to 1.0 and caused confusion
-
Copy exclusion constraints when changing column type
-
Update
revert_finalize_columns_type_changeto not remove indexes, foreign keys etc -
Fix verbose query logging when
ActiveRecord::Base.loggerisnil -
Add a shortcut for running background migrations
# Before: OnlineMigrations::BackgroundMigrations::Scheduler.run # After OnlineMigrations.run_background_migrations
-
Add support for
:type_cast_functiontoinitialize_column_type_changehelper -
Drop support for Ruby < 2.7 and Rails < 6.1
- Add
auto_analyzeconfiguration option - Add
alphabetize_schemaconfiguration option - Fix
backfill_column_for_type_change_in_backgroundfor cast expressions - Fix copying indexes with long names when changing column type
- Enhance error messages with the link to the detailed description
- Fix checking which expression indexes to copy when changing column type
- Fix copying expression indexes when changing column type
-
Add ability to use custom raw sql for
backfill_column_for_type_change'stype_cast_functionbackfill_column_for_type_change(:users, :company_id, type_cast_function: Arel.sql("company_id::integer"))
-
Fix version safety with
revert
- Promote check constraint to
NOT NULLon PostgreSQL >= 12 when changing column type - Fix
safety_assuredwithrevert
- Fix
update_columns_in_batcheswhen multiple columns are passed - Fix reverting adding/removing not null and text limit constraints
- Add check for
change_column_default - Add check for
add_unique_constraint(Active Record >= 7.1) - Add check for
add_columnwith stored generated columns
- Fix removing columns having expression indexes on them
- Suggest additional steps for safely renaming a column if Active Record
enumerate_columns_in_select_statementssetting is enabled (implemented in Active Record 7+, disabled by default) - Fix
add_reference_concurrentlyto correctly check for existence of foreign keys - Fix column quoting in
add_not_null_constraint
- Fix Schema Cache to correctly retrieve metadata from renamed tables
- Add support for renaming multiple columns at once in the same table
- Fix quoting table/column names across the library
- Fix deffered foreign keys support in
add_foreign_key(Active Record >= 7) - Reset attempts of failing jobs before executing background migration inline
- Ignore internal Active Record migrations compatibility related options when suggesting a safe column type change
- Added check for
add_exclusion_constraint - Fix preserving old column options (
:commentand:collation) when changing column type - Set
NOT NULLduring new column creation when changing column type for PostgreSQL >= 11
- Support ruby 3.2.0
- Fix removing index by name
- Fix multiple databases support for
start_afterandtarget_versionconfigs - Fix error when
Railsdefined withoutRails.env - Improve error message for adding column with a NULL default for PostgreSQL < 11
-
Fix sequence resetting in tests that use fixtures
-
Fix
update_column_in_batchesfor SQL subquery valuesIt generated inefficient queries before, e.g.:
update_column_in_batches(:users, :comments_count, Arel.sql(<<~SQL)) (select count(*) from comments where comments.user_id = users.id) SQL
Generated SQL queries before:
update users set comments_count = (..count subquery..) where comments_count is null or comments_count != (..count subquery..)
Generated SQL queries now:
update users set comments_count = (..count subquery..)
-
Fix check for
add_columnwithdefault: nilfor PostgreSQL < 11 -
Replacing a unique index when other unique index with the prefix of columns exists is safe
- Raise for possible index corruption in all environments (previously, the check was made only in the production environment)
-
Added check for index corruption with PostgreSQL 14.0 to 14.3
-
No need to separately remove indexes when removing a column from the small table
-
Add ability to perform specific action on a relation or individual records using background migrations
Example, assuming you have lots and lots of fraud likes:
class DeleteFraudLikes < ActiveRecord::Migration[7.0] def up perform_action_on_relation_in_background("Like", { fraud: true }, :delete_all) end end
Example, assuming you added a new column to the users and want to populate it:
class User < ApplicationRecord def generate_invite_token self.invite_token = # some complex logic end end perform_action_on_relation_in_background("User", { invite_token: nil }, :generate_invite_token)
You can use
delete_all/destroy_all/update_allfor the whole relation or run specific methods on individual records. -
Add ability to delete records associated with a parent object using background migrations
class Link < ActiveRecord::Base has_many :clicks end class Click < ActiveRecord::Base belongs_to :link end class DeleteSomeLinkClicks < ActiveRecord::Migration[7.0] def up some_link = ... delete_associated_records_in_background("Link", some_link.id, :clicks) end end
-
Add ability to delete orphaned records using background migrations
class User < ApplicationRecord has_many :posts end class Post < ApplicationRecord belongs_to :author, class_name: 'User' end class DeleteOrphanedPosts < ActiveRecord::Migration[7.0] def up delete_orphaned_records_in_background("Post", :author) end end
- Fix missing options in suggested command for columns removal
- Fix retrieving raw postgresql connection
-
Lazy load this gem
-
Add ability to reset counter caches using background migrations
class User < ApplicationRecord has_many :projects end class Project < ApplicationRecord belongs_to :user, counter_cache: true end class ResetUsersProjectsCount < ActiveRecord::Migration[7.0] def up reset_counters_in_background("User", :projects) end end
-
Accept
0asbatch_pausevalue for background migrations -
Ignore default scopes in
CopyColumnandBackfillColumnbackground migrations -
Raise an error for unsupported database versions
-
Fix backfilling code in suggestion for changing column's NOT NULL
New safe operations
- Changing between
textandcitextwhen not indexed - Changing a
stringcolumn to acitextcolumn when not indexed - Changing a
citextcolumn to astringcolumn with no length limit - Increasing the
:precisionof anintervalcolumn - Changing a
cidrcolumn to aninetcolumn - Changing an
xmlcolumn to atextcolumn - Changing an
xmlcolumn to astringcolumn with no:limit - Changing a
bitcolumn to abit_varyingcolumn - Increasing or removing the
:limitof abit_varyingcolumn
New unsafe operations
- Decreasing
:precisionof adatetimecolumn - Decreasing
:limitof atimestamptzcolumn - Decreasing
:limitof abit_varyingcolumn - Adding a
:limitto abit_varyingcolumn
-
Support ActiveRecord 7.0+ versioned schemas
-
Check for addition of single table inheritance column
See Adding a single table inheritance column for details
-
Add a way to log every SQL query to stdout
See Verbose SQL logs for details
-
Ignore new tables when checking for removing table with multiple fkeys
-
Fix backfilling column in add_column_with_default when default is an expression
-
Check removing a table with multiple foreign keys
-
Check for mismatched reference column types
For example, it detects cases like:
class AddUserIdToProjects < ActiveRecord::Migration[7.0] def change add_column :projects, :user_id, :integer end end
where
users.idis of typebigint. -
Add support for multiple databases to
start_afterandtarget_versionconfiguration optionsOnlineMigrations.configure do |config| config.start_after = { primary: 20211112000000, animals: 20220101000000 } config.target_version = { primary: 10, animals: 14.1 } end
-
Do not suggest
ignored_columnswhen removing columns for Active Record 4.2 (ignored_columnswas introduced in 5.0) -
Check replacing indexes
For example, you have an index on
projects.creator_id. But decide, it is better to have a multicolumn index on[creator_id, created_at]:class AddIndexOnCreationToProjects < ActiveRecord::Migration[7.0] disable_ddl_transaction! def change remove_index :projects, :creator_id, algorithm: :concurrently # (1) add_index :projects, [:creator_id, :created_at], algorithm: :concurrently # (2) end end
If there is no existing indexes covering
creator_id, removing an old index (1) before replacing it with the new one (2) might result in slow queries while building the new index. A safer approach is to swap removing the old and creation of the new index operations.
- First release