strong_migrations 2.5.0 → 2.5.2
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 +8 -0
- data/README.md +45 -74
- data/lib/strong_migrations/checker.rb +10 -3
- data/lib/strong_migrations/checks.rb +8 -3
- data/lib/strong_migrations/migrator.rb +5 -1
- data/lib/strong_migrations/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9c7db529b15ac630f2f6c9e284b185ca5affbdb275c2d34d26e508317f6e2fa4
|
|
4
|
+
data.tar.gz: 8b00a79a0f4cb787d43de827cbafe495c081b5250db4f08d75a893953778d72a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 73091c6f13637427c09ed2dc013f804ac1ce91d78844b6dc10b7deb89aeb4d391123c4f16e661b34a1fd9e8013f5c6c50295e2a45824ca042e9418d3c6cd338d
|
|
7
|
+
data.tar.gz: 96bbd494942e1a9f3a529f2ee7453fb693e9ec61aba22846d6be9c5a986b9213053142f9f06a87dc16dbc9361520ae9ae84e32c72e43654fdd0f2da921590cdb
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## 2.5.2 (2025-12-20)
|
|
2
|
+
|
|
3
|
+
- Fixed false positive for `add_reference` with `foreign_key: {validate: false}`
|
|
4
|
+
|
|
5
|
+
## 2.5.1 (2025-10-13)
|
|
6
|
+
|
|
7
|
+
- Fixed `transaction_timeout` option with DDL transaction
|
|
8
|
+
|
|
1
9
|
## 2.5.0 (2025-07-27)
|
|
2
10
|
|
|
3
11
|
- Added check for `rename_schema`
|
data/README.md
CHANGED
|
@@ -43,7 +43,7 @@ end
|
|
|
43
43
|
|
|
44
44
|
Deploy the code, then wrap this step in a safety_assured { ... } block.
|
|
45
45
|
|
|
46
|
-
class RemoveColumn < ActiveRecord::Migration[8.
|
|
46
|
+
class RemoveColumn < ActiveRecord::Migration[8.1]
|
|
47
47
|
def change
|
|
48
48
|
safety_assured { remove_column :users, :name }
|
|
49
49
|
end
|
|
@@ -82,10 +82,6 @@ Postgres-specific checks:
|
|
|
82
82
|
- [adding a column with a volatile default value](#adding-a-column-with-a-volatile-default-value)
|
|
83
83
|
- [renaming a schema](#renaming-a-schema)
|
|
84
84
|
|
|
85
|
-
Config-specific checks:
|
|
86
|
-
|
|
87
|
-
- [changing the default value of a column](#changing-the-default-value-of-a-column)
|
|
88
|
-
|
|
89
85
|
Best practices:
|
|
90
86
|
|
|
91
87
|
- [keeping non-unique indexes to three columns or less](#keeping-non-unique-indexes-to-three-columns-or-less)
|
|
@@ -99,7 +95,7 @@ You can also add [custom checks](#custom-checks) or [disable specific checks](#d
|
|
|
99
95
|
Active Record caches database columns at runtime, so if you drop a column, it can cause exceptions until your app reboots.
|
|
100
96
|
|
|
101
97
|
```ruby
|
|
102
|
-
class RemoveSomeColumnFromUsers < ActiveRecord::Migration[8.
|
|
98
|
+
class RemoveSomeColumnFromUsers < ActiveRecord::Migration[8.1]
|
|
103
99
|
def change
|
|
104
100
|
remove_column :users, :some_column
|
|
105
101
|
end
|
|
@@ -120,7 +116,7 @@ end
|
|
|
120
116
|
3. Write a migration to remove the column (wrap in `safety_assured` block)
|
|
121
117
|
|
|
122
118
|
```ruby
|
|
123
|
-
class RemoveSomeColumnFromUsers < ActiveRecord::Migration[8.
|
|
119
|
+
class RemoveSomeColumnFromUsers < ActiveRecord::Migration[8.1]
|
|
124
120
|
def change
|
|
125
121
|
safety_assured { remove_column :users, :some_column }
|
|
126
122
|
end
|
|
@@ -137,7 +133,7 @@ end
|
|
|
137
133
|
Changing the type of a column causes the entire table to be rewritten. During this time, reads and writes are blocked in Postgres, and writes are blocked in MySQL and MariaDB.
|
|
138
134
|
|
|
139
135
|
```ruby
|
|
140
|
-
class ChangeSomeColumnType < ActiveRecord::Migration[8.
|
|
136
|
+
class ChangeSomeColumnType < ActiveRecord::Migration[8.1]
|
|
141
137
|
def change
|
|
142
138
|
change_column :users, :some_column, :new_type
|
|
143
139
|
end
|
|
@@ -183,7 +179,7 @@ A safer approach is to:
|
|
|
183
179
|
Renaming a column that’s in use will cause errors in your application.
|
|
184
180
|
|
|
185
181
|
```ruby
|
|
186
|
-
class RenameSomeColumn < ActiveRecord::Migration[8.
|
|
182
|
+
class RenameSomeColumn < ActiveRecord::Migration[8.1]
|
|
187
183
|
def change
|
|
188
184
|
rename_column :users, :some_column, :new_name
|
|
189
185
|
end
|
|
@@ -208,7 +204,7 @@ A safer approach is to:
|
|
|
208
204
|
Renaming a table that’s in use will cause errors in your application.
|
|
209
205
|
|
|
210
206
|
```ruby
|
|
211
|
-
class RenameUsersToCustomers < ActiveRecord::Migration[8.
|
|
207
|
+
class RenameUsersToCustomers < ActiveRecord::Migration[8.1]
|
|
212
208
|
def change
|
|
213
209
|
rename_table :users, :customers
|
|
214
210
|
end
|
|
@@ -233,7 +229,7 @@ A safer approach is to:
|
|
|
233
229
|
The `force` option can drop an existing table.
|
|
234
230
|
|
|
235
231
|
```ruby
|
|
236
|
-
class CreateUsers < ActiveRecord::Migration[8.
|
|
232
|
+
class CreateUsers < ActiveRecord::Migration[8.1]
|
|
237
233
|
def change
|
|
238
234
|
create_table :users, force: true do |t|
|
|
239
235
|
# ...
|
|
@@ -247,7 +243,7 @@ end
|
|
|
247
243
|
Create tables without the `force` option.
|
|
248
244
|
|
|
249
245
|
```ruby
|
|
250
|
-
class CreateUsers < ActiveRecord::Migration[8.
|
|
246
|
+
class CreateUsers < ActiveRecord::Migration[8.1]
|
|
251
247
|
def change
|
|
252
248
|
create_table :users do |t|
|
|
253
249
|
# ...
|
|
@@ -265,7 +261,7 @@ If you intend to drop an existing table, run `drop_table` first.
|
|
|
265
261
|
Adding an auto-incrementing column (`serial`/`bigserial` in Postgres and `AUTO_INCREMENT` in MySQL and MariaDB) causes the entire table to be rewritten. During this time, reads and writes are blocked in Postgres, and writes are blocked in MySQL and MariaDB.
|
|
266
262
|
|
|
267
263
|
```ruby
|
|
268
|
-
class AddIdToCitiesUsers < ActiveRecord::Migration[8.
|
|
264
|
+
class AddIdToCitiesUsers < ActiveRecord::Migration[8.1]
|
|
269
265
|
def change
|
|
270
266
|
add_column :cities_users, :id, :primary_key
|
|
271
267
|
end
|
|
@@ -285,7 +281,7 @@ Create a new table and migrate the data with the same steps as [renaming a table
|
|
|
285
281
|
Adding a stored generated column causes the entire table to be rewritten. During this time, reads and writes are blocked in Postgres, and writes are blocked in MySQL and MariaDB.
|
|
286
282
|
|
|
287
283
|
```ruby
|
|
288
|
-
class AddSomeColumnToUsers < ActiveRecord::Migration[8.
|
|
284
|
+
class AddSomeColumnToUsers < ActiveRecord::Migration[8.1]
|
|
289
285
|
def change
|
|
290
286
|
add_column :users, :some_column, :virtual, type: :string, as: "...", stored: true
|
|
291
287
|
end
|
|
@@ -305,7 +301,7 @@ Add a non-generated column and use callbacks or triggers instead (or a virtual g
|
|
|
305
301
|
Adding a check constraint blocks reads and writes in Postgres and blocks writes in MySQL and MariaDB while every row is checked.
|
|
306
302
|
|
|
307
303
|
```ruby
|
|
308
|
-
class AddCheckConstraint < ActiveRecord::Migration[8.
|
|
304
|
+
class AddCheckConstraint < ActiveRecord::Migration[8.1]
|
|
309
305
|
def change
|
|
310
306
|
add_check_constraint :users, "price > 0", name: "price_check"
|
|
311
307
|
end
|
|
@@ -317,7 +313,7 @@ end
|
|
|
317
313
|
Add the check constraint without validating existing rows:
|
|
318
314
|
|
|
319
315
|
```ruby
|
|
320
|
-
class AddCheckConstraint < ActiveRecord::Migration[8.
|
|
316
|
+
class AddCheckConstraint < ActiveRecord::Migration[8.1]
|
|
321
317
|
def change
|
|
322
318
|
add_check_constraint :users, "price > 0", name: "price_check", validate: false
|
|
323
319
|
end
|
|
@@ -327,7 +323,7 @@ end
|
|
|
327
323
|
Then validate them in a separate migration.
|
|
328
324
|
|
|
329
325
|
```ruby
|
|
330
|
-
class ValidateCheckConstraint < ActiveRecord::Migration[8.
|
|
326
|
+
class ValidateCheckConstraint < ActiveRecord::Migration[8.1]
|
|
331
327
|
def change
|
|
332
328
|
validate_check_constraint :users, name: "price_check"
|
|
333
329
|
end
|
|
@@ -343,7 +339,7 @@ end
|
|
|
343
339
|
Strong Migrations can’t ensure safety for raw SQL statements. Make really sure that what you’re doing is safe, then use:
|
|
344
340
|
|
|
345
341
|
```ruby
|
|
346
|
-
class ExecuteSQL < ActiveRecord::Migration[8.
|
|
342
|
+
class ExecuteSQL < ActiveRecord::Migration[8.1]
|
|
347
343
|
def change
|
|
348
344
|
safety_assured { execute "..." }
|
|
349
345
|
end
|
|
@@ -359,7 +355,7 @@ Note: Strong Migrations does not detect dangerous backfills.
|
|
|
359
355
|
Active Record creates a transaction around each migration, and backfilling in the same transaction that alters a table keeps the table locked for the [duration of the backfill](https://wework.github.io/data/2015/11/05/add-columns-with-default-values-to-large-tables-in-rails-postgres/).
|
|
360
356
|
|
|
361
357
|
```ruby
|
|
362
|
-
class AddSomeColumnToUsers < ActiveRecord::Migration[8.
|
|
358
|
+
class AddSomeColumnToUsers < ActiveRecord::Migration[8.1]
|
|
363
359
|
def change
|
|
364
360
|
add_column :users, :some_column, :text
|
|
365
361
|
User.update_all some_column: "default_value"
|
|
@@ -374,7 +370,7 @@ Also, running a single query to update data can cause issues for large tables.
|
|
|
374
370
|
There are three keys to backfilling safely: batching, throttling, and running it outside a transaction. Use the Rails console or a separate migration with `disable_ddl_transaction!`.
|
|
375
371
|
|
|
376
372
|
```ruby
|
|
377
|
-
class BackfillSomeColumn < ActiveRecord::Migration[8.
|
|
373
|
+
class BackfillSomeColumn < ActiveRecord::Migration[8.1]
|
|
378
374
|
disable_ddl_transaction!
|
|
379
375
|
|
|
380
376
|
def up
|
|
@@ -397,7 +393,7 @@ Note: If backfilling with a method other than `update_all`, use `User.reset_colu
|
|
|
397
393
|
In Postgres, adding an index non-concurrently blocks writes.
|
|
398
394
|
|
|
399
395
|
```ruby
|
|
400
|
-
class AddSomeIndexToUsers < ActiveRecord::Migration[8.
|
|
396
|
+
class AddSomeIndexToUsers < ActiveRecord::Migration[8.1]
|
|
401
397
|
def change
|
|
402
398
|
add_index :users, :some_column
|
|
403
399
|
end
|
|
@@ -409,7 +405,7 @@ end
|
|
|
409
405
|
Add indexes concurrently.
|
|
410
406
|
|
|
411
407
|
```ruby
|
|
412
|
-
class AddSomeIndexToUsers < ActiveRecord::Migration[8.
|
|
408
|
+
class AddSomeIndexToUsers < ActiveRecord::Migration[8.1]
|
|
413
409
|
disable_ddl_transaction!
|
|
414
410
|
|
|
415
411
|
def change
|
|
@@ -435,7 +431,7 @@ rails g index table column
|
|
|
435
431
|
Rails adds an index non-concurrently to references by default, which blocks writes in Postgres.
|
|
436
432
|
|
|
437
433
|
```ruby
|
|
438
|
-
class AddReferenceToUsers < ActiveRecord::Migration[8.
|
|
434
|
+
class AddReferenceToUsers < ActiveRecord::Migration[8.1]
|
|
439
435
|
def change
|
|
440
436
|
add_reference :users, :city
|
|
441
437
|
end
|
|
@@ -447,7 +443,7 @@ end
|
|
|
447
443
|
Make sure the index is added concurrently.
|
|
448
444
|
|
|
449
445
|
```ruby
|
|
450
|
-
class AddReferenceToUsers < ActiveRecord::Migration[8.
|
|
446
|
+
class AddReferenceToUsers < ActiveRecord::Migration[8.1]
|
|
451
447
|
disable_ddl_transaction!
|
|
452
448
|
|
|
453
449
|
def change
|
|
@@ -465,7 +461,7 @@ end
|
|
|
465
461
|
In Postgres, adding a foreign key blocks writes on both tables.
|
|
466
462
|
|
|
467
463
|
```ruby
|
|
468
|
-
class AddForeignKeyOnUsers < ActiveRecord::Migration[8.
|
|
464
|
+
class AddForeignKeyOnUsers < ActiveRecord::Migration[8.1]
|
|
469
465
|
def change
|
|
470
466
|
add_foreign_key :users, :orders
|
|
471
467
|
end
|
|
@@ -475,7 +471,7 @@ end
|
|
|
475
471
|
or
|
|
476
472
|
|
|
477
473
|
```ruby
|
|
478
|
-
class AddReferenceToUsers < ActiveRecord::Migration[8.
|
|
474
|
+
class AddReferenceToUsers < ActiveRecord::Migration[8.1]
|
|
479
475
|
def change
|
|
480
476
|
add_reference :users, :order, foreign_key: true
|
|
481
477
|
end
|
|
@@ -487,7 +483,7 @@ end
|
|
|
487
483
|
Add the foreign key without validating existing rows:
|
|
488
484
|
|
|
489
485
|
```ruby
|
|
490
|
-
class AddForeignKeyOnUsers < ActiveRecord::Migration[8.
|
|
486
|
+
class AddForeignKeyOnUsers < ActiveRecord::Migration[8.1]
|
|
491
487
|
def change
|
|
492
488
|
add_foreign_key :users, :orders, validate: false
|
|
493
489
|
end
|
|
@@ -497,7 +493,7 @@ end
|
|
|
497
493
|
Then validate them in a separate migration.
|
|
498
494
|
|
|
499
495
|
```ruby
|
|
500
|
-
class ValidateForeignKeyOnUsers < ActiveRecord::Migration[8.
|
|
496
|
+
class ValidateForeignKeyOnUsers < ActiveRecord::Migration[8.1]
|
|
501
497
|
def change
|
|
502
498
|
validate_foreign_key :users, :orders
|
|
503
499
|
end
|
|
@@ -511,7 +507,7 @@ end
|
|
|
511
507
|
In Postgres, adding a unique constraint creates a unique index, which blocks reads and writes.
|
|
512
508
|
|
|
513
509
|
```ruby
|
|
514
|
-
class AddUniqueConstraint < ActiveRecord::Migration[8.
|
|
510
|
+
class AddUniqueConstraint < ActiveRecord::Migration[8.1]
|
|
515
511
|
def change
|
|
516
512
|
add_unique_constraint :users, :some_column
|
|
517
513
|
end
|
|
@@ -523,7 +519,7 @@ end
|
|
|
523
519
|
Create a unique index concurrently, then use it for the constraint.
|
|
524
520
|
|
|
525
521
|
```ruby
|
|
526
|
-
class AddUniqueConstraint < ActiveRecord::Migration[8.
|
|
522
|
+
class AddUniqueConstraint < ActiveRecord::Migration[8.1]
|
|
527
523
|
disable_ddl_transaction!
|
|
528
524
|
|
|
529
525
|
def up
|
|
@@ -544,7 +540,7 @@ end
|
|
|
544
540
|
In Postgres, adding an exclusion constraint blocks reads and writes while every row is checked.
|
|
545
541
|
|
|
546
542
|
```ruby
|
|
547
|
-
class AddExclusionConstraint < ActiveRecord::Migration[8.
|
|
543
|
+
class AddExclusionConstraint < ActiveRecord::Migration[8.1]
|
|
548
544
|
def change
|
|
549
545
|
add_exclusion_constraint :users, "number WITH =", using: :gist
|
|
550
546
|
end
|
|
@@ -562,7 +558,7 @@ end
|
|
|
562
558
|
In Postgres, there’s no equality operator for the `json` column type, which can cause errors for existing `SELECT DISTINCT` queries in your application.
|
|
563
559
|
|
|
564
560
|
```ruby
|
|
565
|
-
class AddPropertiesToUsers < ActiveRecord::Migration[8.
|
|
561
|
+
class AddPropertiesToUsers < ActiveRecord::Migration[8.1]
|
|
566
562
|
def change
|
|
567
563
|
add_column :users, :properties, :json
|
|
568
564
|
end
|
|
@@ -574,7 +570,7 @@ end
|
|
|
574
570
|
Use `jsonb` instead.
|
|
575
571
|
|
|
576
572
|
```ruby
|
|
577
|
-
class AddPropertiesToUsers < ActiveRecord::Migration[8.
|
|
573
|
+
class AddPropertiesToUsers < ActiveRecord::Migration[8.1]
|
|
578
574
|
def change
|
|
579
575
|
add_column :users, :properties, :jsonb
|
|
580
576
|
end
|
|
@@ -590,7 +586,7 @@ end
|
|
|
590
586
|
In Postgres, setting `NOT NULL` on an existing column blocks reads and writes while every row is checked.
|
|
591
587
|
|
|
592
588
|
```ruby
|
|
593
|
-
class SetSomeColumnNotNull < ActiveRecord::Migration[8.
|
|
589
|
+
class SetSomeColumnNotNull < ActiveRecord::Migration[8.1]
|
|
594
590
|
def change
|
|
595
591
|
change_column_null :users, :some_column, false
|
|
596
592
|
end
|
|
@@ -602,7 +598,7 @@ end
|
|
|
602
598
|
Instead, add a check constraint.
|
|
603
599
|
|
|
604
600
|
```ruby
|
|
605
|
-
class SetSomeColumnNotNull < ActiveRecord::Migration[8.
|
|
601
|
+
class SetSomeColumnNotNull < ActiveRecord::Migration[8.1]
|
|
606
602
|
def change
|
|
607
603
|
add_check_constraint :users, "some_column IS NOT NULL", name: "users_some_column_null", validate: false
|
|
608
604
|
end
|
|
@@ -612,7 +608,7 @@ end
|
|
|
612
608
|
Then validate it in a separate migration. Once the check constraint is validated, you can safely set `NOT NULL` on the column and drop the check constraint.
|
|
613
609
|
|
|
614
610
|
```ruby
|
|
615
|
-
class ValidateSomeColumnNotNull < ActiveRecord::Migration[8.
|
|
611
|
+
class ValidateSomeColumnNotNull < ActiveRecord::Migration[8.1]
|
|
616
612
|
def up
|
|
617
613
|
validate_check_constraint :users, name: "users_some_column_null"
|
|
618
614
|
change_column_null :users, :some_column, false
|
|
@@ -633,7 +629,7 @@ end
|
|
|
633
629
|
Adding a column with a volatile default value to an existing table causes the entire table to be rewritten. During this time, reads and writes are blocked.
|
|
634
630
|
|
|
635
631
|
```ruby
|
|
636
|
-
class AddSomeColumnToUsers < ActiveRecord::Migration[8.
|
|
632
|
+
class AddSomeColumnToUsers < ActiveRecord::Migration[8.1]
|
|
637
633
|
def change
|
|
638
634
|
add_column :users, :some_column, :uuid, default: "gen_random_uuid()"
|
|
639
635
|
end
|
|
@@ -645,7 +641,7 @@ end
|
|
|
645
641
|
Instead, add the column without a default value, then change the default.
|
|
646
642
|
|
|
647
643
|
```ruby
|
|
648
|
-
class AddSomeColumnToUsers < ActiveRecord::Migration[8.
|
|
644
|
+
class AddSomeColumnToUsers < ActiveRecord::Migration[8.1]
|
|
649
645
|
def up
|
|
650
646
|
add_column :users, :some_column, :uuid
|
|
651
647
|
change_column_default :users, :some_column, from: nil, to: "gen_random_uuid()"
|
|
@@ -684,36 +680,6 @@ A safer approach is to:
|
|
|
684
680
|
5. Stop writing to the old schema
|
|
685
681
|
6. Drop the old schema
|
|
686
682
|
|
|
687
|
-
### Changing the default value of a column
|
|
688
|
-
|
|
689
|
-
#### Bad
|
|
690
|
-
|
|
691
|
-
Rails < 7 enables partial writes by default, which can cause incorrect values to be inserted when changing the default value of a column.
|
|
692
|
-
|
|
693
|
-
```ruby
|
|
694
|
-
class ChangeSomeColumnDefault < ActiveRecord::Migration[6.1]
|
|
695
|
-
def change
|
|
696
|
-
change_column_default :users, :some_column, from: "old", to: "new"
|
|
697
|
-
end
|
|
698
|
-
end
|
|
699
|
-
|
|
700
|
-
User.create!(some_column: "old") # can insert "new"
|
|
701
|
-
```
|
|
702
|
-
|
|
703
|
-
#### Good
|
|
704
|
-
|
|
705
|
-
Disable partial writes in `config/application.rb`. For Rails < 7, use:
|
|
706
|
-
|
|
707
|
-
```ruby
|
|
708
|
-
config.active_record.partial_writes = false
|
|
709
|
-
```
|
|
710
|
-
|
|
711
|
-
For Rails 7+, use:
|
|
712
|
-
|
|
713
|
-
```ruby
|
|
714
|
-
config.active_record.partial_inserts = false
|
|
715
|
-
```
|
|
716
|
-
|
|
717
683
|
### Keeping non-unique indexes to three columns or less
|
|
718
684
|
|
|
719
685
|
#### Bad
|
|
@@ -721,7 +687,7 @@ config.active_record.partial_inserts = false
|
|
|
721
687
|
Adding a non-unique index with more than three columns rarely improves performance.
|
|
722
688
|
|
|
723
689
|
```ruby
|
|
724
|
-
class AddSomeIndexToUsers < ActiveRecord::Migration[8.
|
|
690
|
+
class AddSomeIndexToUsers < ActiveRecord::Migration[8.1]
|
|
725
691
|
def change
|
|
726
692
|
add_index :users, [:a, :b, :c, :d]
|
|
727
693
|
end
|
|
@@ -733,7 +699,7 @@ end
|
|
|
733
699
|
Instead, start an index with columns that narrow down the results the most.
|
|
734
700
|
|
|
735
701
|
```ruby
|
|
736
|
-
class AddSomeIndexToUsers < ActiveRecord::Migration[8.
|
|
702
|
+
class AddSomeIndexToUsers < ActiveRecord::Migration[8.1]
|
|
737
703
|
def change
|
|
738
704
|
add_index :users, [:d, :b]
|
|
739
705
|
end
|
|
@@ -747,7 +713,7 @@ For Postgres, be sure to add them concurrently.
|
|
|
747
713
|
To mark a step in the migration as safe, despite using a method that might otherwise be dangerous, wrap it in a `safety_assured` block.
|
|
748
714
|
|
|
749
715
|
```ruby
|
|
750
|
-
class MySafeMigration < ActiveRecord::Migration[8.
|
|
716
|
+
class MySafeMigration < ActiveRecord::Migration[8.1]
|
|
751
717
|
def change
|
|
752
718
|
safety_assured { remove_column :users, :some_column }
|
|
753
719
|
end
|
|
@@ -967,15 +933,20 @@ StrongMigrations.auto_analyze = true
|
|
|
967
933
|
|
|
968
934
|
## Faster Migrations
|
|
969
935
|
|
|
970
|
-
Only dump the schema when adding a new migration. If you use Git, add to `
|
|
936
|
+
Only dump the schema when adding a new migration. If you use Git, add to the end of your `Rakefile`:
|
|
971
937
|
|
|
972
938
|
```rb
|
|
973
|
-
|
|
939
|
+
task :faster_migrations do
|
|
940
|
+
ActiveRecord.dump_schema_after_migration = Rails.env.development? &&
|
|
941
|
+
`git status db/migrate/ --porcelain`.present?
|
|
942
|
+
end
|
|
943
|
+
|
|
944
|
+
task "db:migrate" => "faster_migrations"
|
|
974
945
|
```
|
|
975
946
|
|
|
976
947
|
## Schema Sanity
|
|
977
948
|
|
|
978
|
-
|
|
949
|
+
With Active Record < 8.1, columns can flip order in `db/schema.rb` when you have multiple developers. One way to prevent this is to [alphabetize them](https://www.pgrs.net/2008/03/12/alphabetize-schema-rb-columns/). Add to `config/initializers/strong_migrations.rb`:
|
|
979
950
|
|
|
980
951
|
```ruby
|
|
981
952
|
StrongMigrations.alphabetize_schema = true
|
|
@@ -151,6 +151,16 @@ module StrongMigrations
|
|
|
151
151
|
StrongMigrations.skipped_databases.map(&:to_s).include?(db_config_name)
|
|
152
152
|
end
|
|
153
153
|
|
|
154
|
+
def set_transaction_timeout
|
|
155
|
+
return if defined?(@transaction_timeout_set)
|
|
156
|
+
|
|
157
|
+
if StrongMigrations.transaction_timeout
|
|
158
|
+
adapter.set_transaction_timeout(StrongMigrations.transaction_timeout)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
@transaction_timeout_set = true
|
|
162
|
+
end
|
|
163
|
+
|
|
154
164
|
private
|
|
155
165
|
|
|
156
166
|
def check_adapter
|
|
@@ -183,9 +193,6 @@ module StrongMigrations
|
|
|
183
193
|
if StrongMigrations.statement_timeout
|
|
184
194
|
adapter.set_statement_timeout(StrongMigrations.statement_timeout)
|
|
185
195
|
end
|
|
186
|
-
if StrongMigrations.transaction_timeout
|
|
187
|
-
adapter.set_transaction_timeout(StrongMigrations.transaction_timeout)
|
|
188
|
-
end
|
|
189
196
|
if StrongMigrations.lock_timeout
|
|
190
197
|
adapter.set_lock_timeout(StrongMigrations.lock_timeout)
|
|
191
198
|
end
|
|
@@ -147,9 +147,13 @@ module StrongMigrations
|
|
|
147
147
|
if postgresql?
|
|
148
148
|
index_value = options.fetch(:index, true)
|
|
149
149
|
concurrently_set = index_value.is_a?(Hash) && index_value[:algorithm] == :concurrently
|
|
150
|
-
|
|
150
|
+
index_unsafe = index_value && !concurrently_set
|
|
151
151
|
|
|
152
|
-
|
|
152
|
+
foreign_key_value = options[:foreign_key]
|
|
153
|
+
validate_false = foreign_key_value.is_a?(Hash) && foreign_key_value[:validate] == false
|
|
154
|
+
foreign_key_unsafe = foreign_key_value && !validate_false
|
|
155
|
+
|
|
156
|
+
if index_unsafe || foreign_key_unsafe
|
|
153
157
|
if index_value.is_a?(Hash)
|
|
154
158
|
options[:index] = options[:index].merge(algorithm: :concurrently)
|
|
155
159
|
elsif index_value
|
|
@@ -161,7 +165,8 @@ module StrongMigrations
|
|
|
161
165
|
throw :safe
|
|
162
166
|
end
|
|
163
167
|
|
|
164
|
-
if
|
|
168
|
+
if foreign_key_unsafe
|
|
169
|
+
options.delete(:foreign_key)
|
|
165
170
|
headline = "Adding a foreign key blocks writes on both tables."
|
|
166
171
|
append = "\n\nThen add the foreign key in separate migrations."
|
|
167
172
|
else
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
module StrongMigrations
|
|
2
2
|
module Migrator
|
|
3
3
|
def ddl_transaction(migration, ...)
|
|
4
|
-
|
|
4
|
+
retries = StrongMigrations.lock_timeout_retries > 0 && use_transaction?(migration)
|
|
5
|
+
return super unless retries || StrongMigrations.transaction_timeout
|
|
5
6
|
|
|
6
7
|
# handle MigrationProxy class
|
|
7
8
|
migration = migration.send(:migration) if !migration.is_a?(ActiveRecord::Migration) && migration.respond_to?(:migration, true)
|
|
@@ -9,6 +10,9 @@ module StrongMigrations
|
|
|
9
10
|
checker = migration.send(:strong_migrations_checker)
|
|
10
11
|
return super if checker.skip?
|
|
11
12
|
|
|
13
|
+
checker.set_transaction_timeout
|
|
14
|
+
return super unless retries
|
|
15
|
+
|
|
12
16
|
# retry migration since the entire transaction needs to be rerun
|
|
13
17
|
checker.retry_lock_timeouts(check_committed: true) do
|
|
14
18
|
# failed transaction reverts timeout, so need to re-apply
|