online_migrations 0.9.2 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +41 -0
- data/README.md +155 -150
- data/docs/background_migrations.md +43 -10
- data/docs/configuring.md +23 -18
- data/lib/generators/online_migrations/install_generator.rb +3 -7
- data/lib/generators/online_migrations/templates/add_sharding_to_online_migrations.rb.tt +18 -0
- data/lib/generators/online_migrations/templates/initializer.rb.tt +12 -3
- data/lib/generators/online_migrations/templates/migration.rb.tt +8 -3
- data/lib/generators/online_migrations/upgrade_generator.rb +33 -0
- data/lib/online_migrations/background_migrations/application_record.rb +13 -0
- data/lib/online_migrations/background_migrations/backfill_column.rb +1 -1
- data/lib/online_migrations/background_migrations/copy_column.rb +11 -19
- data/lib/online_migrations/background_migrations/delete_orphaned_records.rb +2 -20
- data/lib/online_migrations/background_migrations/migration.rb +123 -34
- data/lib/online_migrations/background_migrations/migration_helpers.rb +0 -4
- data/lib/online_migrations/background_migrations/migration_job.rb +15 -12
- data/lib/online_migrations/background_migrations/migration_job_runner.rb +2 -2
- data/lib/online_migrations/background_migrations/migration_runner.rb +56 -11
- data/lib/online_migrations/background_migrations/reset_counters.rb +3 -9
- data/lib/online_migrations/background_migrations/scheduler.rb +5 -15
- data/lib/online_migrations/change_column_type_helpers.rb +71 -86
- data/lib/online_migrations/command_checker.rb +50 -46
- data/lib/online_migrations/config.rb +19 -15
- data/lib/online_migrations/copy_trigger.rb +15 -10
- data/lib/online_migrations/error_messages.rb +13 -25
- data/lib/online_migrations/foreign_keys_collector.rb +2 -2
- data/lib/online_migrations/indexes_collector.rb +3 -3
- data/lib/online_migrations/lock_retrier.rb +4 -9
- data/lib/online_migrations/schema_cache.rb +0 -6
- data/lib/online_migrations/schema_dumper.rb +21 -0
- data/lib/online_migrations/schema_statements.rb +80 -256
- data/lib/online_migrations/utils.rb +36 -55
- data/lib/online_migrations/verbose_sql_logs.rb +3 -2
- data/lib/online_migrations/version.rb +1 -1
- data/lib/online_migrations.rb +9 -6
- metadata +9 -7
- data/lib/online_migrations/background_migrations/advisory_lock.rb +0 -62
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9ceda570f99a1712496ac5c9549859c7dc2cad7e3cc4ab59c97a22ba17f3bfd0
|
4
|
+
data.tar.gz: dfc5a83147a92bf5e04a532210e72b6f32f561f032b2a1b746ff142bf4e16635
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
20
|
-
- Rails
|
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
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
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
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
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
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
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
|
-
|
339
|
-
|
340
|
-
|
353
|
+
```ruby
|
354
|
+
class BackfillChangeFilesSizeType < ActiveRecord::Migration[7.1]
|
355
|
+
disable_ddl_transaction!
|
341
356
|
|
342
|
-
|
343
|
-
|
344
|
-
|
357
|
+
def up
|
358
|
+
backfill_column_for_type_change :files, :size
|
359
|
+
end
|
345
360
|
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
361
|
+
def down
|
362
|
+
# no op
|
363
|
+
end
|
364
|
+
end
|
365
|
+
```
|
351
366
|
|
352
|
-
3.
|
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
|
-
|
355
|
-
|
356
|
-
|
372
|
+
```ruby
|
373
|
+
class FinalizeChangeFilesSizeType < ActiveRecord::Migration[7.1]
|
374
|
+
disable_ddl_transaction!
|
357
375
|
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
376
|
+
def change
|
377
|
+
finalize_column_type_change :files, :size
|
378
|
+
end
|
379
|
+
end
|
380
|
+
```
|
363
381
|
|
364
|
-
|
365
|
-
|
382
|
+
6. Deploy
|
383
|
+
7. Finally, if everything works as expected, remove copy trigger and old column:
|
366
384
|
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
385
|
+
```ruby
|
386
|
+
class CleanupChangeFilesSizeType < ActiveRecord::Migration[7.1]
|
387
|
+
def up
|
388
|
+
cleanup_column_type_change :files, :size
|
389
|
+
end
|
372
390
|
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
391
|
+
def down
|
392
|
+
initialize_column_type_change :files, :size, :integer
|
393
|
+
end
|
394
|
+
end
|
395
|
+
```
|
378
396
|
|
379
|
-
|
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
|
-
|
436
|
-
|
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
|
-
|
448
|
-
|
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
|
-
|
459
|
-
|
460
|
-
|
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
|
-
|
487
|
+
(is disabled by default in Active Record >= 7), then you need to ignore old column:
|
467
488
|
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
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
|
-
|
490
|
-
|
491
|
-
|
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
|
-
|
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
|
-
|
561
|
-
|
562
|
-
|
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
|
-
|
574
|
-
|
575
|
-
|
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
|
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
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
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
|
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
|
141
|
-
rails = Project.create!(name: "
|
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
|
-
|
147
|
+
postgres.issues.create!
|
146
148
|
|
147
|
-
BackfillProjectIssuesCount.new
|
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
|
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
|
-
|
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.
|