Module: OnlineMigrations::BackgroundDataMigrations::MigrationHelpers
- Included in:
- SchemaStatements
- Defined in:
- lib/online_migrations/background_data_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::BackgroundDataMigrations::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::BackgroundDataMigrations::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::BackgroundDataMigrations::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.
-
#delete_associated_records_in_background(model_name, record_id, association, **options) ⇒ OnlineMigrations::BackgroundDataMigrations::Migration
Deletes associated records for a specific parent record using background migrations.
-
#delete_orphaned_records_in_background(model_name, *associations, **options) ⇒ OnlineMigrations::BackgroundDataMigrations::Migration
Deletes records with one or more missing relations using background migrations.
-
#enqueue_background_data_migration(migration_name, *arguments, **options) ⇒ OnlineMigrations::BackgroundDataMigrations::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::BackgroundDataMigrations::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::BackgroundDataMigrations::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::BackgroundDataMigrations::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_data_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::BackgroundDataMigrations::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_data_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 123 124 125 126 127 128 129 130 |
# File 'lib/online_migrations/background_data_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" } if model_name model_name = model_name.name if model_name.is_a?(Class) connection_class_name = Utils.find_connection_class(model_name.constantize).name end # model_name = model_name.name if model_name.is_a?(Class) # connection_class = Utils.find_connection_class(model_name.constantize) if model_name enqueue_background_data_migration( "CopyColumn", table_name, column_names, tmp_columns, model_name, type_cast_functions, connection_class_name: connection_class_name, ** ) 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_data_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::BackgroundDataMigrations::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.
153 154 155 156 157 158 159 160 161 162 |
# File 'lib/online_migrations/background_data_migrations/migration_helpers.rb', line 153 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.
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/online_migrations/background_data_migrations/migration_helpers.rb', line 171 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 |
#delete_associated_records_in_background(model_name, record_id, association, **options) ⇒ OnlineMigrations::BackgroundDataMigrations::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.
274 275 276 277 278 279 280 281 282 283 284 |
# File 'lib/online_migrations/background_data_migrations/migration_helpers.rb', line 274 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::BackgroundDataMigrations::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.
245 246 247 248 249 250 251 252 253 254 |
# File 'lib/online_migrations/background_data_migrations/migration_helpers.rb', line 245 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::BackgroundDataMigrations::Migration Also known as: enqueue_background_migration
For convenience, the enqueued background data 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.
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 |
# File 'lib/online_migrations/background_data_migrations/migration_helpers.rb', line 371 def enqueue_background_data_migration(migration_name, *arguments, **) .assert_valid_keys(:max_attempts, :iteration_pause, :connection_class_name) migration_name = migration_name.name if migration_name.is_a?(Class) [:connection_class_name] ||= compute_connection_class_name(migration_name, arguments) if Utils.multiple_databases? && ![:connection_class_name] raise ArgumentError, "You must pass a :connection_class_name when using multiple databases." end connection_class = [:connection_class_name].constantize shards = Utils.shard_names(connection_class) shards = [nil] if shards.size == 1 shards.each do |shard| # Can't use `find_or_create_by` or hash syntax here, because it does not correctly work with json `arguments`. migration = Migration.where(migration_name: migration_name, shard: shard).where("arguments = ?", arguments.to_json).first migration ||= Migration.create!(**, migration_name: migration_name, arguments: arguments, shard: shard) if Utils.run_background_migrations_inline? && !migration.succeeded? job = OnlineMigrations.config.background_data_migrations.job job.constantize.perform_inline(migration.id) end end true 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.
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'lib/online_migrations/background_data_migrations/migration_helpers.rb', line 430 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) migrations = Migration.for_configuration(migration_name, arguments).to_a configuration[:arguments] = arguments.to_json else migrations = Migration.for_migration_name(migration_name).to_a end if migrations.empty? Utils.raise_in_prod_or_say_in_dev("Could not find background data migration(s) for the given configuration: #{configuration}.") elsif !migrations.all?(&:succeeded?) raise "Expected background data migration(s) for the given configuration to be marked as 'succeeded': #{configuration}." end end |
#perform_action_on_relation_in_background(model_name, conditions, action, updates: nil, **options) ⇒ OnlineMigrations::BackgroundDataMigrations::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.
320 321 322 323 324 325 326 327 328 329 330 331 |
# File 'lib/online_migrations/background_data_migrations/migration_helpers.rb', line 320 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.
408 409 410 411 |
# File 'lib/online_migrations/background_data_migrations/migration_helpers.rb', line 408 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::BackgroundDataMigrations::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.
216 217 218 219 220 221 222 223 224 225 226 |
# File 'lib/online_migrations/background_data_migrations/migration_helpers.rb', line 216 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 |