online_migrations 0.9.2 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +41 -0
  3. data/README.md +155 -150
  4. data/docs/background_migrations.md +43 -10
  5. data/docs/configuring.md +23 -18
  6. data/lib/generators/online_migrations/install_generator.rb +3 -7
  7. data/lib/generators/online_migrations/templates/add_sharding_to_online_migrations.rb.tt +18 -0
  8. data/lib/generators/online_migrations/templates/initializer.rb.tt +12 -3
  9. data/lib/generators/online_migrations/templates/migration.rb.tt +8 -3
  10. data/lib/generators/online_migrations/upgrade_generator.rb +33 -0
  11. data/lib/online_migrations/background_migrations/application_record.rb +13 -0
  12. data/lib/online_migrations/background_migrations/backfill_column.rb +1 -1
  13. data/lib/online_migrations/background_migrations/copy_column.rb +11 -19
  14. data/lib/online_migrations/background_migrations/delete_orphaned_records.rb +2 -20
  15. data/lib/online_migrations/background_migrations/migration.rb +123 -34
  16. data/lib/online_migrations/background_migrations/migration_helpers.rb +0 -4
  17. data/lib/online_migrations/background_migrations/migration_job.rb +15 -12
  18. data/lib/online_migrations/background_migrations/migration_job_runner.rb +2 -2
  19. data/lib/online_migrations/background_migrations/migration_runner.rb +56 -11
  20. data/lib/online_migrations/background_migrations/reset_counters.rb +3 -9
  21. data/lib/online_migrations/background_migrations/scheduler.rb +5 -15
  22. data/lib/online_migrations/change_column_type_helpers.rb +71 -86
  23. data/lib/online_migrations/command_checker.rb +50 -46
  24. data/lib/online_migrations/config.rb +19 -15
  25. data/lib/online_migrations/copy_trigger.rb +15 -10
  26. data/lib/online_migrations/error_messages.rb +13 -25
  27. data/lib/online_migrations/foreign_keys_collector.rb +2 -2
  28. data/lib/online_migrations/indexes_collector.rb +3 -3
  29. data/lib/online_migrations/lock_retrier.rb +4 -9
  30. data/lib/online_migrations/schema_cache.rb +0 -6
  31. data/lib/online_migrations/schema_dumper.rb +21 -0
  32. data/lib/online_migrations/schema_statements.rb +80 -256
  33. data/lib/online_migrations/utils.rb +36 -55
  34. data/lib/online_migrations/verbose_sql_logs.rb +3 -2
  35. data/lib/online_migrations/version.rb +1 -1
  36. data/lib/online_migrations.rb +9 -6
  37. metadata +9 -7
  38. data/lib/online_migrations/background_migrations/advisory_lock.rb +0 -62
  39. data/lib/online_migrations/foreign_key_definition.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 579a0db844c82c2c9153a1a3f44bfcffe929011112b132afe73b1dfdf68f4583
4
- data.tar.gz: b88f26034f0c1d044607ef191ca3feae71e3c6093cb05bd9a277790447da01af
3
+ metadata.gz: 9ceda570f99a1712496ac5c9549859c7dc2cad7e3cc4ab59c97a22ba17f3bfd0
4
+ data.tar.gz: dfc5a83147a92bf5e04a532210e72b6f32f561f032b2a1b746ff142bf4e16635
5
5
  SHA512:
6
- metadata.gz: 229a2a31c44a358425a684ea8d1771d98ecee6412c9b4480e30e2cd68e53c0c9e77039959fa8f379ad0a83fb9f592d133d29fe43e2c79ae4dbf674cd9c210598
7
- data.tar.gz: 9255d5ad7b7d350021cc553b5d91bec852cfbecc0b9c7d6296606beffae266de72096cdf5a4d12dd88d921fb0af60a9e870d08a676a90fad552c5bd2d36096f6
6
+ metadata.gz: 0ff6cce820134ef6b893f9405b71a0d8f8cd42dd79616123aaa492983671ebcff98dbff21477afa78b29342d40098822da4b8de0306489faf956b21ee44e2113
7
+ data.tar.gz: 42dcf132290c9affe812ef7489397d4c05d48f33069f3ee09710fced6aaf8ac928fee63c1498333657a824f98885e3af5e706e4dcc4a089b9cd62284ad0223d1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  ## master (unreleased)
2
2
 
3
+ ## 0.11.0 (2024-01-09)
4
+
5
+ - Support sharding for background migrations
6
+
7
+ Now, if a `relation` inside background migration definition is defined on a sharded model,
8
+ then that background migration would automatically run on all the shards.
9
+
10
+ To get all the new sharding related schema changes, you need to run:
11
+
12
+ ```sh
13
+ $ bin/rails generate online_migrations:upgrade
14
+ $ bin/rails db:migrate
15
+ ```
16
+
17
+ - Change background migration `progress` to return values in range from 0.0 to 100.0
18
+
19
+ Previously, these were values in range from 0.0 to 1.0 and caused confusion
20
+
21
+ - Copy exclusion constraints when changing column type
22
+ - Update `revert_finalize_columns_type_change` to not remove indexes, foreign keys etc
23
+ - Fix verbose query logging when `ActiveRecord::Base.logger` is `nil`
24
+ - Add a shortcut for running background migrations
25
+
26
+ ```ruby
27
+ # Before:
28
+ OnlineMigrations::BackgroundMigrations::Scheduler.run
29
+ # After
30
+ OnlineMigrations.run_background_migrations
31
+ ```
32
+
33
+ - Add support for `:type_cast_function` to `initialize_column_type_change` helper
34
+ - Drop support for Ruby < 2.7 and Rails < 6.1
35
+
36
+ ## 0.10.0 (2023-12-12)
37
+
38
+ - Add `auto_analyze` configuration option
39
+ - Add `alphabetize_schema` configuration option
40
+ - Fix `backfill_column_for_type_change_in_background` for cast expressions
41
+ - Fix copying indexes with long names when changing column type
42
+ - Enhance error messages with the link to the detailed description
43
+
3
44
  ## 0.9.2 (2023-11-02)
4
45
 
5
46
  - Fix checking which expression indexes to copy when changing column type
data/README.md CHANGED
@@ -16,10 +16,12 @@ See [comparison to `strong_migrations`](#comparison-to-strong_migrations)
16
16
 
17
17
  ## Requirements
18
18
 
19
- - Ruby 2.1+
20
- - Rails 4.2+
19
+ - Ruby 2.7+
20
+ - Rails 6.1+
21
21
  - PostgreSQL 9.6+
22
22
 
23
+ For older Ruby and Rails versions you can use '< 0.11' version of this gem.
24
+
23
25
  **Note**: Since some migration helpers use database `VIEW`s to implement their logic, it is recommended to use `structure.sql` schema format, or otherwise add some gem (like [scenic](https://github.com/scenic-views/scenic)) to be able to dump them into the `schema.rb`.
24
26
 
25
27
  ## Installation
@@ -35,10 +37,20 @@ And then run:
35
37
  ```sh
36
38
  $ bundle install
37
39
  $ bin/rails generate online_migrations:install
40
+ $ bin/rails db:migrate
38
41
  ```
39
42
 
40
43
  **Note**: If you do not have plans on using [background migrations](docs/background_migrations.md) feature, then you can delete the generated migration and regenerate it later, if needed.
41
44
 
45
+ ### Upgrading
46
+
47
+ If you're already using [background migrations](docs/background_migrations.md), your background migrations tables may require additional columns. After every upgrade run:
48
+
49
+ ```sh
50
+ $ bin/rails generate online_migrations:upgrade
51
+ $ bin/rails db:migrate
52
+ ```
53
+
42
54
  ## Motivation
43
55
 
44
56
  Writing a safe migration can be daunting. Numerous articles have been written on the topic and a few gems are trying to address the problem. Even for someone who has a pretty good command of PostgreSQL, remembering all the subtleties of explicit locking can be problematic.
@@ -90,7 +102,7 @@ class AddAdminToUsers < ActiveRecord::Migration[7.1]
90
102
  execute "SET statement_timeout TO '5s'"
91
103
  change_column_null :users, :admin, false
92
104
  end
93
-
105
+
94
106
  def down
95
107
  remove_column :users, :admin
96
108
  end
@@ -177,30 +189,22 @@ end
177
189
 
178
190
  1. Ignore the column:
179
191
 
180
- ```ruby
181
- # For Active Record 5+
182
- class User < ApplicationRecord
183
- self.ignored_columns = ["name"]
184
- end
185
-
186
- # For Active Record < 5
187
- class User < ActiveRecord::Base
188
- def self.columns
189
- super.reject { |c| c.name == "name" }
190
- end
191
- end
192
- ```
192
+ ```ruby
193
+ class User < ApplicationRecord
194
+ self.ignored_columns = ["name"]
195
+ end
196
+ ```
193
197
 
194
198
  2. Deploy
195
199
  3. Wrap column removing in a `safety_assured` block:
196
200
 
197
- ```ruby
198
- class RemoveNameFromUsers < ActiveRecord::Migration[7.1]
199
- def change
200
- safety_assured { remove_column :users, :name }
201
- end
202
- end
203
- ```
201
+ ```ruby
202
+ class RemoveNameFromUsers < ActiveRecord::Migration[7.1]
203
+ def change
204
+ safety_assured { remove_column :users, :name }
205
+ end
206
+ end
207
+ ```
204
208
 
205
209
  4. Remove column ignoring from `User` model
206
210
  5. Deploy
@@ -316,67 +320,82 @@ Type | Safe Changes
316
320
 
317
321
  :white_check_mark: **Good**
318
322
 
323
+ #### "Classic" approach (abstract)
324
+
325
+ 1. Create a new column
326
+ 2. Write to both columns
327
+ 3. Backfill data from the old column to the new column
328
+ 4. Move reads from the old column to the new column
329
+ 5. Stop writing to the old column
330
+ 6. Drop the old column
331
+
332
+ #### :bullettrain_side: Concrete steps for Active Record
333
+
319
334
  **Note**: The following steps can also be used to change the primary key's type (e.g., from `integer` to `bigint`).
320
335
 
321
336
  A safer approach can be accomplished in several steps:
322
337
 
323
338
  1. Create a new column and keep column's data in sync:
324
339
 
325
- ```ruby
326
- class InitializeChangeFilesSizeType < ActiveRecord::Migration[7.1]
327
- def change
328
- initialize_column_type_change :files, :size, :bigint
329
- end
330
- end
331
- ```
340
+ ```ruby
341
+ class InitializeChangeFilesSizeType < ActiveRecord::Migration[7.1]
342
+ def change
343
+ initialize_column_type_change :files, :size, :bigint
344
+ end
345
+ end
346
+ ```
332
347
 
333
- **Note**: `initialize_column_type_change` accepts additional options (like `:limit`, `:default` etc)
334
- which will be passed to `add_column` when creating a new column, so you can override previous values.
348
+ **Note**: `initialize_column_type_change` accepts additional options (like `:limit`, `:default` etc)
349
+ which will be passed to `add_column` when creating a new column, so you can override previous values.
335
350
 
336
351
  2. Backfill data from the old column to the new column:
337
352
 
338
- ```ruby
339
- class BackfillChangeFilesSizeType < ActiveRecord::Migration[7.1]
340
- disable_ddl_transaction!
353
+ ```ruby
354
+ class BackfillChangeFilesSizeType < ActiveRecord::Migration[7.1]
355
+ disable_ddl_transaction!
341
356
 
342
- def up
343
- backfill_column_for_type_change :files, :size
344
- end
357
+ def up
358
+ backfill_column_for_type_change :files, :size
359
+ end
345
360
 
346
- def down
347
- # no op
348
- end
349
- end
350
- ```
361
+ def down
362
+ # no op
363
+ end
364
+ end
365
+ ```
351
366
 
352
- 3. Copy indexes, foreign keys, check constraints, NOT NULL constraint, swap new column in place:
367
+ 3. Make sure your application works with values in both formats (when read from the database, converting
368
+ during writes works automatically). For most column type changes, this does not need any updates in the app.
369
+ 4. Deploy
370
+ 5. Copy indexes, foreign keys, check constraints, NOT NULL constraint, swap new column in place:
353
371
 
354
- ```ruby
355
- class FinalizeChangeFilesSizeType < ActiveRecord::Migration[7.1]
356
- disable_ddl_transaction!
372
+ ```ruby
373
+ class FinalizeChangeFilesSizeType < ActiveRecord::Migration[7.1]
374
+ disable_ddl_transaction!
357
375
 
358
- def change
359
- finalize_column_type_change :files, :size
360
- end
361
- end
362
- ```
376
+ def change
377
+ finalize_column_type_change :files, :size
378
+ end
379
+ end
380
+ ```
363
381
 
364
- 4. Deploy
365
- 5. Finally, if everything is working as expected, remove copy trigger and old column:
382
+ 6. Deploy
383
+ 7. Finally, if everything works as expected, remove copy trigger and old column:
366
384
 
367
- ```ruby
368
- class CleanupChangeFilesSizeType < ActiveRecord::Migration[7.1]
369
- def up
370
- cleanup_column_type_change :files, :size
371
- end
385
+ ```ruby
386
+ class CleanupChangeFilesSizeType < ActiveRecord::Migration[7.1]
387
+ def up
388
+ cleanup_column_type_change :files, :size
389
+ end
372
390
 
373
- def down
374
- initialize_column_type_change :files, :size, :integer
375
- end
376
- end
377
- ```
391
+ def down
392
+ initialize_column_type_change :files, :size, :integer
393
+ end
394
+ end
395
+ ```
378
396
 
379
- 6. Deploy
397
+ 8. Remove changes from step 3, if any
398
+ 9. Deploy
380
399
 
381
400
  ### Renaming a column
382
401
 
@@ -430,67 +449,61 @@ To work around this limitation, we need to tell Active Record to acquire this in
430
449
 
431
450
  1. Instruct Rails that you are going to rename a column:
432
451
 
433
- ```ruby
434
- OnlineMigrations.config.column_renames = {
435
- "users" => {
436
- "name" => "first_name"
437
- }
438
- }
439
- ```
440
- NOTE: You also need to temporarily enable partial writes (is disabled by default in Active Record >= 7)
441
- until the process of column rename is fully done.
442
- ```ruby
443
- # config/application.rb
444
- # For Active Record >= 7
445
- config.active_record.partial_inserts = true
452
+ ```ruby
453
+ OnlineMigrations.config.column_renames = {
454
+ "users" => {
455
+ "name" => "first_name"
456
+ }
457
+ }
458
+ ```
446
459
 
447
- # Or for Active Record < 7
448
- config.active_record.partial_writes = true
449
- ```
460
+ **Note**: You also need to temporarily enable partial writes (is disabled by default in Active Record >= 7)
461
+ until the process of column rename is fully done.
462
+
463
+ ```ruby
464
+ # config/application.rb
465
+ # For Active Record >= 7
466
+ config.active_record.partial_inserts = true
467
+
468
+ # Or for Active Record < 7
469
+ config.active_record.partial_writes = true
470
+ ```
450
471
 
451
472
  2. Deploy
452
473
  3. Tell the database that you are going to rename a column. This will not actually rename any columns,
453
474
  nor any data/indexes/foreign keys copying will be made, so will be instantaneous.
454
475
  It will use a combination of a VIEW and column aliasing to work with both column names simultaneously
455
476
 
456
- ```ruby
457
- class InitializeRenameUsersNameToFirstName < ActiveRecord::Migration[7.1]
458
- def change
459
- initialize_column_rename :users, :name, :first_name
460
- end
461
- end
462
- ```
477
+ ```ruby
478
+ class InitializeRenameUsersNameToFirstName < ActiveRecord::Migration[7.1]
479
+ def change
480
+ initialize_column_rename :users, :name, :first_name
481
+ end
482
+ end
483
+ ```
463
484
 
464
485
  4. Replace usages of the old column with a new column in the codebase
465
486
  5. If you enabled Active Record `enumerate_columns_in_select_statements` setting in your application
466
- (is disabled by default in Active Record >= 7), then you need to ignore old column:
487
+ (is disabled by default in Active Record >= 7), then you need to ignore old column:
467
488
 
468
- ```ruby
469
- # For Active Record 5+
470
- class User < ApplicationRecord
471
- self.ignored_columns = ["name"]
472
- end
473
-
474
- # For Active Record < 5
475
- class User < ActiveRecord::Base
476
- def self.columns
477
- super.reject { |c| c.name == "name" }
478
- end
479
- end
480
- ```
489
+ ```ruby
490
+ class User < ApplicationRecord
491
+ self.ignored_columns = ["name"]
492
+ end
493
+ ```
481
494
 
482
495
  6. Deploy
483
496
  7. Remove the column rename config from step 1
484
497
  8. Remove the column ignore from step 5, if added
485
498
  9. Remove the VIEW created in step 3 and finally rename the column:
486
499
 
487
- ```ruby
488
- class FinalizeRenameUsersNameToFirstName < ActiveRecord::Migration[7.1]
489
- def change
490
- finalize_column_rename :users, :name, :first_name
491
- end
492
- end
493
- ```
500
+ ```ruby
501
+ class FinalizeRenameUsersNameToFirstName < ActiveRecord::Migration[7.1]
502
+ def change
503
+ finalize_column_rename :users, :name, :first_name
504
+ end
505
+ end
506
+ ```
494
507
 
495
508
  10. Deploy
496
509
 
@@ -546,35 +559,35 @@ To work around this limitation, we need to tell Active Record to acquire this in
546
559
 
547
560
  1. Instruct Rails that you are going to rename a table:
548
561
 
549
- ```ruby
550
- OnlineMigrations.config.table_renames = {
551
- "clients" => "users"
552
- }
553
- ```
562
+ ```ruby
563
+ OnlineMigrations.config.table_renames = {
564
+ "clients" => "users"
565
+ }
566
+ ```
554
567
 
555
568
  2. Deploy
556
569
  3. Create a VIEW:
557
570
 
558
- ```ruby
559
- class InitializeRenameClientsToUsers < ActiveRecord::Migration[7.1]
560
- def change
561
- initialize_table_rename :clients, :users
562
- end
563
- end
564
- ```
571
+ ```ruby
572
+ class InitializeRenameClientsToUsers < ActiveRecord::Migration[7.1]
573
+ def change
574
+ initialize_table_rename :clients, :users
575
+ end
576
+ end
577
+ ```
565
578
 
566
579
  4. Replace usages of the old table with a new table in the codebase
567
580
  5. Remove the table rename config from step 1
568
581
  6. Deploy
569
582
  7. Remove the VIEW created in step 3:
570
583
 
571
- ```ruby
572
- class FinalizeRenameClientsToUsers < ActiveRecord::Migration[7.1]
573
- def change
574
- finalize_table_rename :clients, :users
575
- end
576
- end
577
- ```
584
+ ```ruby
585
+ class FinalizeRenameClientsToUsers < ActiveRecord::Migration[7.1]
586
+ def change
587
+ finalize_table_rename :clients, :users
588
+ end
589
+ end
590
+ ```
578
591
 
579
592
  8. Deploy
580
593
 
@@ -1024,7 +1037,7 @@ end
1024
1037
 
1025
1038
  :x: **Bad**
1026
1039
 
1027
- Adding multiple foreign keys in a single migration blocks reads and writes on all involved tables until migration is completed.
1040
+ Adding multiple foreign keys in a single migration blocks writes on all involved tables until migration is completed.
1028
1041
  Avoid adding foreign key more than once per migration file, unless the source and target tables are identical.
1029
1042
 
1030
1043
  ```ruby
@@ -1157,19 +1170,11 @@ A safer approach is to:
1157
1170
 
1158
1171
  1. ignore the column:
1159
1172
 
1160
- ```ruby
1161
- # For Active Record 5+
1162
- class User < ApplicationRecord
1163
- self.ignored_columns = ["type"]
1164
- end
1165
-
1166
- # For Active Record < 5
1167
- class User < ActiveRecord::Base
1168
- def self.columns
1169
- super.reject { |c| c.name == "type" }
1170
- end
1171
- end
1172
- ```
1173
+ ```ruby
1174
+ class User < ApplicationRecord
1175
+ self.ignored_columns = ["type"]
1176
+ end
1177
+ ```
1173
1178
 
1174
1179
  2. deploy
1175
1180
  3. remove the column ignoring from step 1 and apply initial code changes
@@ -1281,18 +1286,18 @@ The main differences are:
1281
1286
 
1282
1287
  1. `strong_migrations` provides you **text guidance** on how to run migrations safer and you should implement them yourself. This new gem has actual [**code helpers**](https://github.com/fatkodima/online_migrations/blob/master/lib/online_migrations/schema_statements.rb) (and suggests them when fails on unsafe migrations) you can use to do what you want. See [example](#example) for an example.
1283
1288
 
1284
- It has migrations helpers for:
1289
+ It has migrations helpers for:
1285
1290
 
1286
- * renaming tables/columns
1287
- * changing columns types (including changing primary/foreign keys from `integer` to `bigint`)
1288
- * adding columns with default values
1289
- * backfilling data
1290
- * adding different types of constraints
1291
- * and others
1291
+ * renaming tables/columns
1292
+ * changing columns types (including changing primary/foreign keys from `integer` to `bigint`)
1293
+ * adding columns with default values
1294
+ * backfilling data
1295
+ * adding different types of constraints
1296
+ * and others
1292
1297
 
1293
1298
  2. This gem has a [powerful internal framework](https://github.com/fatkodima/online_migrations/blob/master/docs/background_migrations.md) for running data migrations on very large tables using background migrations.
1294
1299
 
1295
- For example, you can use background migrations to migrate data that’s stored in a single JSON column to a separate table instead; backfill values from one column to another (as one of the steps when changing column type); or backfill some column’s value from an API.
1300
+ For example, you can use background migrations to migrate data that’s stored in a single JSON column to a separate table instead; backfill values from one column to another (as one of the steps when changing column type); or backfill some column’s value from an API.
1296
1301
 
1297
1302
  3. Yet, it has more checks for unsafe changes (see [checks](#checks)).
1298
1303
 
@@ -18,7 +18,7 @@ Start a background migrations scheduler. For example, to run it on cron using [w
18
18
 
19
19
  ```ruby
20
20
  every 1.minute do
21
- runner "OnlineMigrations::BackgroundMigrations::Scheduler.run"
21
+ runner "OnlineMigrations.run_background_migrations"
22
22
  end
23
23
  ```
24
24
 
@@ -116,13 +116,15 @@ enqueue_background_migration("MyMigrationWithArgs", arg1, arg2, ...)
116
116
 
117
117
  ## Predefined background migrations
118
118
 
119
- * `BackfillColumn` - backfills column(s) with scalar values (enqueue using `backfill_column_in_background`)
119
+ * `BackfillColumn` - backfills column(s) with scalar values (enqueue using `backfill_column_in_background`; or `backfill_column_for_type_change_in_background` if backfilling column for which type change is in progress)
120
120
  * `CopyColumn` - copies data from one column(s) to other(s) (enqueue using `copy_column_in_background`)
121
121
  * `DeleteAssociatedRecords` - deletes records associated with a parent object (enqueue using `delete_associated_records_in_background`)
122
122
  * `DeleteOrphanedRecords` - deletes records with one or more missing relations (enqueue using `delete_orphaned_records_in_background`)
123
123
  * `PerformActionOnRelation` - performs specific action on a relation or individual records (enqueue using `perform_action_on_relation_in_background`)
124
124
  * `ResetCounters` - resets one or more counter caches to their correct value (enqueue using `reset_counters_in_background`)
125
125
 
126
+ **Note**: These migration helpers should be run inside the migration against the database where background migrations tables are defined.
127
+
126
128
  ## Testing
127
129
 
128
130
  At a minimum, it's recommended that the `#process_batch` method in your background migration is tested. You may also want to test the `#relation` and `#count` methods if they are sufficiently complex.
@@ -137,17 +139,18 @@ require "test_helper"
137
139
  module OnlineMigrations
138
140
  module BackgroundMigrations
139
141
  class BackfillProjectIssuesCountTest < ActiveSupport::TestCase
140
- test "#process_batch performs a background migration iteration" do
141
- rails = Project.create!(name: "rails")
142
+ test "#process_batch performs an iteration" do
143
+ rails = Project.create!(name: "Ruby on Rails")
142
144
  postgres = Project.create!(name: "PostgreSQL")
143
145
 
144
146
  2.times { rails.issues.create! }
145
- _postgres_issue = postgres.issues.create!
147
+ postgres.issues.create!
146
148
 
147
- BackfillProjectIssuesCount.new.process_batch(Project.all)
149
+ migration = BackfillProjectIssuesCount.new
150
+ migration.process_batch(migration.relation)
148
151
 
149
- assert_equal 2, rails.issues_count
150
- assert_equal 1, postgres.issues_count
152
+ assert_equal 2, rails.reload.issues_count
153
+ assert_equal 1, postgres.reload.issues_count
151
154
  end
152
155
  end
153
156
  end
@@ -220,7 +223,7 @@ To get the progress (assuming `#count` method on background migration class was
220
223
 
221
224
  ```ruby
222
225
  migration = OnlineMigrations::BackgroundMigrations::Migration.find(id)
223
- migration.progress # value from 0 to 1.0
226
+ migration.progress # value from 0 to 100.0
224
227
  ```
225
228
 
226
229
  **Note**: It will be easier to work with background migrations through some kind of Web UI, but until it is implemented, we can work with them only manually.
@@ -229,7 +232,19 @@ migration.progress # value from 0 to 1.0
229
232
 
230
233
  There are a few configurable options for the Background Migrations. Custom configurations should be placed in a `online_migrations.rb` initializer.
231
234
 
232
- **Note**: Check the [source code](https://github.com/fatkodima/online_migrations/blob/master/lib/online_migrations/background_migrations/config.rb) for the list of all available configuration options.
235
+ Check the [source code](https://github.com/fatkodima/online_migrations/blob/master/lib/online_migrations/background_migrations/config.rb) for the list of all available configuration options.
236
+
237
+ **Note**: You can dynamically change certain migration parameters while the migration is run.
238
+ For example,
239
+ ```ruby
240
+ migration = OnlineMigrations::BackgroundMigrations::Migration.find(id)
241
+ migration.update!(
242
+ batch_size: 50_000, # The # of records migration will update per run
243
+ sub_batch_size: 10_000, # The # of records migration will update via single `UPDATE`
244
+ batch_pause: 1.second, # Minimum time (in seconds) between successive migration runs
245
+ sub_batch_pause_ms: 20 # Minimum time (in ms) between successive migration `UPDATE`s
246
+ )
247
+ ```
233
248
 
234
249
  ### Throttling
235
250
 
@@ -293,3 +308,21 @@ OnlineMigrations.config.background_migrations.backtrace_cleaner = cleaner
293
308
  ```
294
309
 
295
310
  If none is specified, the default `Rails.backtrace_cleaner` will be used to clean backtraces.
311
+
312
+ ### Multiple databases and sharding
313
+
314
+ If you have multiple databases or sharding, you may need to configure where background migrations related tables live
315
+ by configuring the parent model:
316
+
317
+ ```ruby
318
+ # config/initializers/online_migrations.rb
319
+
320
+ # Referring to one of the databases
321
+ OnlineMigrations::BackgroundMigrations::ApplicationRecord.connects_to database: { writing: :animals }
322
+
323
+ # Referring to one of the shards (via `:database` option)
324
+ OnlineMigrations::BackgroundMigrations::ApplicationRecord.connects_to database: { writing: :shard_one }
325
+ ```
326
+
327
+ By default, ActiveRecord uses the database config named `:primary` (if exists) under the environment section from the `database.yml`.
328
+ Otherwise, the first config under the environment section is used.