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.
- 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.
|