Module: OnlineMigrations::BackgroundMigrations::MigrationHelpers
- Included in:
- SchemaStatements
- Defined in:
- lib/online_migrations/background_migrations/migration_helpers.rb
Instance Method Summary collapse
-
#backfill_column_for_type_change_in_background(table_name, column_name, model_name: nil, type_cast_function: nil, **options) ⇒ OnlineMigrations::BackgroundMigrations::Migration
Backfills data from the old column to the new column using background migrations.
-
#backfill_column_in_background(table_name, column_name, value, model_name: nil, **options) ⇒ OnlineMigrations::BackgroundMigrations::Migration
Backfills column data using background migrations.
-
#backfill_columns_for_type_change_in_background(table_name, *column_names, model_name: nil, type_cast_functions: {}, **options) ⇒ Object
Same as ‘backfill_column_for_type_change_in_background` but for multiple columns.
-
#backfill_columns_in_background(table_name, updates, model_name: nil, **options) ⇒ Object
Same as ‘backfill_column_in_background` but for multiple columns.
-
#copy_column_in_background(table_name, copy_from, copy_to, model_name: nil, type_cast_function: nil, **options) ⇒ OnlineMigrations::BackgroundMigrations::Migration
Copies data from the old column to the new column using background migrations.
-
#copy_columns_in_background(table_name, copy_from, copy_to, model_name: nil, type_cast_functions: {}, **options) ⇒ Object
Same as ‘copy_column_in_background` but for multiple columns.
- #create_background_data_migration(migration_name, *arguments, **options) ⇒ Object
-
#delete_associated_records_in_background(model_name, record_id, association, **options) ⇒ OnlineMigrations::BackgroundMigrations::Migration
Deletes associated records for a specific parent record using background migrations.
-
#delete_orphaned_records_in_background(model_name, *associations, **options) ⇒ OnlineMigrations::BackgroundMigrations::Migration
Deletes records with one or more missing relations using background migrations.
-
#enqueue_background_data_migration(migration_name, *arguments, **options) ⇒ OnlineMigrations::BackgroundMigrations::Migration
(also: #enqueue_background_migration)
Creates a background migration for the given job class name.
-
#ensure_background_data_migration_succeeded(migration_name, arguments: nil) ⇒ Object
(also: #ensure_background_migration_succeeded)
Ensures that the background data migration with the provided configuration succeeded.
-
#perform_action_on_relation_in_background(model_name, conditions, action, updates: nil, **options) ⇒ OnlineMigrations::BackgroundMigrations::Migration
Performs specific action on a relation or individual records.
-
#remove_background_data_migration(migration_name, *arguments) ⇒ Object
(also: #remove_background_migration)
Removes the background migration for the given class name and arguments, if exists.
-
#reset_counters_in_background(model_name, *counters, touch: nil, **options) ⇒ OnlineMigrations::BackgroundMigrations::Migration
Resets one or more counter caches to their correct value using background migrations.
Instance Method Details
#backfill_column_for_type_change_in_background(table_name, column_name, model_name: nil, type_cast_function: nil, **options) ⇒ OnlineMigrations::BackgroundMigrations::Migration
This method is better suited for large tables (10/100s of millions of records). For smaller tables it is probably better and easier to use more flexible ‘backfill_column_for_type_change`.
Backfills data from the old column to the new column using background migrations.
86 87 88 89 90 91 92 93 94 95 |
# File 'lib/online_migrations/background_migrations/migration_helpers.rb', line 86 def backfill_column_for_type_change_in_background(table_name, column_name, model_name: nil, type_cast_function: nil, **) backfill_columns_for_type_change_in_background( table_name, column_name, model_name: model_name, type_cast_functions: { column_name => type_cast_function }, ** ) end |
#backfill_column_in_background(table_name, column_name, value, model_name: nil, **options) ⇒ OnlineMigrations::BackgroundMigrations::Migration
This method is better suited for large tables (10/100s of millions of records). For smaller tables it is probably better and easier to use more flexible ‘update_column_in_batches`.
Consider ‘backfill_columns_in_background` when backfilling multiple columns to avoid rewriting the table multiple times.
Backfills column data using background migrations.
30 31 32 33 |
# File 'lib/online_migrations/background_migrations/migration_helpers.rb', line 30 def backfill_column_in_background(table_name, column_name, value, model_name: nil, **) backfill_columns_in_background(table_name, { column_name => value }, model_name: model_name, **) end |
#backfill_columns_for_type_change_in_background(table_name, *column_names, model_name: nil, type_cast_functions: {}, **options) ⇒ Object
Same as ‘backfill_column_for_type_change_in_background` but for multiple columns.
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/online_migrations/background_migrations/migration_helpers.rb', line 104 def backfill_columns_for_type_change_in_background(table_name, *column_names, model_name: nil, type_cast_functions: {}, **) if model_name.nil? && Utils.multiple_databases? raise ArgumentError, "You must pass a :model_name when using multiple databases." end tmp_columns = column_names.map { |column_name| "#{column_name}_for_type_change" } model_name = model_name.name if model_name.is_a?(Class) enqueue_background_data_migration( "CopyColumn", table_name, column_names, tmp_columns, model_name, type_cast_functions, ** ) end |
#backfill_columns_in_background(table_name, updates, model_name: nil, **options) ⇒ Object
Same as ‘backfill_column_in_background` but for multiple columns.
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/online_migrations/background_migrations/migration_helpers.rb', line 44 def backfill_columns_in_background(table_name, updates, model_name: nil, **) if model_name.nil? && Utils.multiple_databases? raise ArgumentError, "You must pass a :model_name when using multiple databases." end model_name = model_name.name if model_name.is_a?(Class) enqueue_background_data_migration( "BackfillColumn", table_name, updates, model_name, ** ) end |
#copy_column_in_background(table_name, copy_from, copy_to, model_name: nil, type_cast_function: nil, **options) ⇒ OnlineMigrations::BackgroundMigrations::Migration
This method is better suited for large tables (10/100s of millions of records). For smaller tables it is probably better and easier to use more flexible ‘update_column_in_batches`.
Copies data from the old column to the new column using background migrations.
145 146 147 148 149 150 151 152 153 154 |
# File 'lib/online_migrations/background_migrations/migration_helpers.rb', line 145 def copy_column_in_background(table_name, copy_from, copy_to, model_name: nil, type_cast_function: nil, **) copy_columns_in_background( table_name, [copy_from], [copy_to], model_name: model_name, type_cast_functions: { copy_from => type_cast_function }, ** ) end |
#copy_columns_in_background(table_name, copy_from, copy_to, model_name: nil, type_cast_functions: {}, **options) ⇒ Object
Same as ‘copy_column_in_background` but for multiple columns.
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/online_migrations/background_migrations/migration_helpers.rb', line 163 def copy_columns_in_background(table_name, copy_from, copy_to, model_name: nil, type_cast_functions: {}, **) if model_name.nil? && Utils.multiple_databases? raise ArgumentError, "You must pass a :model_name when using multiple databases." end model_name = model_name.name if model_name.is_a?(Class) enqueue_background_data_migration( "CopyColumn", table_name, copy_from, copy_to, model_name, type_cast_functions, ** ) end |
#create_background_data_migration(migration_name, *arguments, **options) ⇒ Object
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 |
# File 'lib/online_migrations/background_migrations/migration_helpers.rb', line 437 def create_background_data_migration(migration_name, *arguments, **) .assert_valid_keys(:batch_column_name, :min_value, :max_value, :batch_size, :sub_batch_size, :batch_pause, :sub_batch_pause_ms, :batch_max_attempts) migration_name = migration_name.name if migration_name.is_a?(Class) # Can't use `find_or_create_by` or hash syntax here, because it does not correctly work with json `arguments`. existing_migration = Migration.find_by("migration_name = ? AND arguments = ? AND shard IS NULL", migration_name, arguments.to_json) return existing_migration if existing_migration Migration.create!(**, migration_name: migration_name, arguments: arguments, shard: nil) do |migration| shards = Utils.shard_names(migration.migration_model) if shards.size > 1 migration.children = shards.map do |shard| child = migration.dup child.shard = shard child end migration.composite = true end end end |
#delete_associated_records_in_background(model_name, record_id, association, **options) ⇒ OnlineMigrations::BackgroundMigrations::Migration
This method is better suited for large tables (10/100s of millions of records). For smaller tables it is probably better and easier to directly delete associated records.
Deletes associated records for a specific parent record using background migrations. This is useful when you are planning to remove a parent object (user, account etc) and needs to remove lots of its associated objects.
266 267 268 269 270 271 272 273 274 275 276 |
# File 'lib/online_migrations/background_migrations/migration_helpers.rb', line 266 def delete_associated_records_in_background(model_name, record_id, association, **) model_name = model_name.name if model_name.is_a?(Class) enqueue_background_data_migration( "DeleteAssociatedRecords", model_name, record_id, association, ** ) end |
#delete_orphaned_records_in_background(model_name, *associations, **options) ⇒ OnlineMigrations::BackgroundMigrations::Migration
This method is better suited for large tables (10/100s of millions of records). For smaller tables it is probably better and easier to directly find and delete orpahed records.
Deletes records with one or more missing relations using background migrations. This is useful when some referential integrity in the database is broken and you want to delete orphaned records.
237 238 239 240 241 242 243 244 245 246 |
# File 'lib/online_migrations/background_migrations/migration_helpers.rb', line 237 def delete_orphaned_records_in_background(model_name, *associations, **) model_name = model_name.name if model_name.is_a?(Class) enqueue_background_data_migration( "DeleteOrphanedRecords", model_name, associations, ** ) end |
#enqueue_background_data_migration(migration_name, *arguments, **options) ⇒ OnlineMigrations::BackgroundMigrations::Migration Also known as: enqueue_background_migration
For convenience, the enqueued background migration is run inline in development and test environments
Creates a background migration for the given job class name.
A background migration runs one job at a time, computing the bounds of the next batch based on the current migration settings and the previous batch bounds. Each job’s execution status is tracked in the database as the migration runs.
372 373 374 375 376 377 378 379 380 381 |
# File 'lib/online_migrations/background_migrations/migration_helpers.rb', line 372 def enqueue_background_data_migration(migration_name, *arguments, **) migration = create_background_data_migration(migration_name, *arguments, **) if Utils.run_background_migrations_inline? runner = MigrationRunner.new(migration) runner.run_all_migration_jobs end migration end |
#ensure_background_data_migration_succeeded(migration_name, arguments: nil) ⇒ Object Also known as: ensure_background_migration_succeeded
Ensures that the background data migration with the provided configuration succeeded.
If the enqueued migration was not found in development (probably when resetting a dev environment followed by ‘db:migrate`), then a log warning is printed. If enqueued migration was not found in production, then the error is raised. If enqueued migration was found but is not succeeded, then the error is raised.
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 |
# File 'lib/online_migrations/background_migrations/migration_helpers.rb', line 414 def ensure_background_data_migration_succeeded(migration_name, arguments: nil) migration_name = migration_name.name if migration_name.is_a?(Class) configuration = { migration_name: migration_name } if arguments arguments = Array(arguments) migration = Migration.parents.for_configuration(migration_name, arguments).first configuration[:arguments] = arguments.to_json else migration = Migration.parents.for_migration_name(migration_name).first end if migration.nil? Utils.raise_in_prod_or_say_in_dev("Could not find background data migration for the given configuration: #{configuration}") elsif !migration.succeeded? raise "Expected background data migration for the given configuration to be marked as 'succeeded', " \ "but it is '#{migration.status}': #{configuration}" end end |
#perform_action_on_relation_in_background(model_name, conditions, action, updates: nil, **options) ⇒ OnlineMigrations::BackgroundMigrations::Migration
This method is better suited for large tables (10/100s of millions of records). For smaller tables it is probably better and easier to directly perform the action on associated records.
Performs specific action on a relation or individual records. This is useful when you want to delete/destroy/update/etc records based on some conditions.
312 313 314 315 316 317 318 319 320 321 322 323 |
# File 'lib/online_migrations/background_migrations/migration_helpers.rb', line 312 def perform_action_on_relation_in_background(model_name, conditions, action, updates: nil, **) model_name = model_name.name if model_name.is_a?(Class) enqueue_background_data_migration( "PerformActionOnRelation", model_name, conditions, action, { updates: updates }, ** ) end |
#remove_background_data_migration(migration_name, *arguments) ⇒ Object Also known as: remove_background_migration
Removes the background migration for the given class name and arguments, if exists.
392 393 394 395 |
# File 'lib/online_migrations/background_migrations/migration_helpers.rb', line 392 def remove_background_data_migration(migration_name, *arguments) migration_name = migration_name.name if migration_name.is_a?(Class) Migration.for_configuration(migration_name, arguments).delete_all end |
#reset_counters_in_background(model_name, *counters, touch: nil, **options) ⇒ OnlineMigrations::BackgroundMigrations::Migration
This method is better suited for large tables (10/100s of millions of records). For smaller tables it is probably better and easier to use ‘reset_counters` from the Active Record.
Resets one or more counter caches to their correct value using background migrations. This is useful when adding new counter caches, or if the counter has been corrupted or modified directly by SQL.
208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/online_migrations/background_migrations/migration_helpers.rb', line 208 def reset_counters_in_background(model_name, *counters, touch: nil, **) model_name = model_name.name if model_name.is_a?(Class) enqueue_background_data_migration( "ResetCounters", model_name, counters, { touch: touch }, ** ) end |