strong_migrations 2.0.0 → 2.0.1
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 +4 -0
- data/README.md +42 -39
- data/lib/strong_migrations/schema_dumper.rb +11 -0
- data/lib/strong_migrations/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f129ef6d517a5adcc4091152f856c32b06cc0b5e4f8f2f3c0ff9ca854ba966c9
|
4
|
+
data.tar.gz: c649028a58aa6f11c598fd5b23e5a2ccec85ffc5b9de596c62ee7a6385f26b1e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba0ad1239d5dae5f01b2f92e00f3f5296241d5f9b908ab7d7f342ce1c7136b9c681973e8dc2752e3a25da333eaa880091cb352170f29a645a9209e45babaa965
|
7
|
+
data.tar.gz: 4764de588facf1cafa101fd9fdffc9bc27c821eff7613c9ca8c5e6e877674495d53c3bd8f5a45bcc8abfe2ca0105f53a8ad950c62c713eed01e3891619790553
|
data/CHANGELOG.md
CHANGED
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[7.
|
46
|
+
class RemoveColumn < ActiveRecord::Migration[7.2]
|
47
47
|
def change
|
48
48
|
safety_assured { remove_column :users, :name }
|
49
49
|
end
|
@@ -98,7 +98,7 @@ You can also add [custom checks](#custom-checks) or [disable specific checks](#d
|
|
98
98
|
Active Record caches database columns at runtime, so if you drop a column, it can cause exceptions until your app reboots.
|
99
99
|
|
100
100
|
```ruby
|
101
|
-
class RemoveSomeColumnFromUsers < ActiveRecord::Migration[7.
|
101
|
+
class RemoveSomeColumnFromUsers < ActiveRecord::Migration[7.2]
|
102
102
|
def change
|
103
103
|
remove_column :users, :some_column
|
104
104
|
end
|
@@ -119,7 +119,7 @@ end
|
|
119
119
|
3. Write a migration to remove the column (wrap in `safety_assured` block)
|
120
120
|
|
121
121
|
```ruby
|
122
|
-
class RemoveSomeColumnFromUsers < ActiveRecord::Migration[7.
|
122
|
+
class RemoveSomeColumnFromUsers < ActiveRecord::Migration[7.2]
|
123
123
|
def change
|
124
124
|
safety_assured { remove_column :users, :some_column }
|
125
125
|
end
|
@@ -136,7 +136,7 @@ end
|
|
136
136
|
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.
|
137
137
|
|
138
138
|
```ruby
|
139
|
-
class ChangeSomeColumnType < ActiveRecord::Migration[7.
|
139
|
+
class ChangeSomeColumnType < ActiveRecord::Migration[7.2]
|
140
140
|
def change
|
141
141
|
change_column :users, :some_column, :new_type
|
142
142
|
end
|
@@ -182,7 +182,7 @@ A safer approach is to:
|
|
182
182
|
Renaming a column that’s in use will cause errors in your application.
|
183
183
|
|
184
184
|
```ruby
|
185
|
-
class RenameSomeColumn < ActiveRecord::Migration[7.
|
185
|
+
class RenameSomeColumn < ActiveRecord::Migration[7.2]
|
186
186
|
def change
|
187
187
|
rename_column :users, :some_column, :new_name
|
188
188
|
end
|
@@ -207,7 +207,7 @@ A safer approach is to:
|
|
207
207
|
Renaming a table that’s in use will cause errors in your application.
|
208
208
|
|
209
209
|
```ruby
|
210
|
-
class RenameUsersToCustomers < ActiveRecord::Migration[7.
|
210
|
+
class RenameUsersToCustomers < ActiveRecord::Migration[7.2]
|
211
211
|
def change
|
212
212
|
rename_table :users, :customers
|
213
213
|
end
|
@@ -232,7 +232,7 @@ A safer approach is to:
|
|
232
232
|
The `force` option can drop an existing table.
|
233
233
|
|
234
234
|
```ruby
|
235
|
-
class CreateUsers < ActiveRecord::Migration[7.
|
235
|
+
class CreateUsers < ActiveRecord::Migration[7.2]
|
236
236
|
def change
|
237
237
|
create_table :users, force: true do |t|
|
238
238
|
# ...
|
@@ -246,7 +246,7 @@ end
|
|
246
246
|
Create tables without the `force` option.
|
247
247
|
|
248
248
|
```ruby
|
249
|
-
class CreateUsers < ActiveRecord::Migration[7.
|
249
|
+
class CreateUsers < ActiveRecord::Migration[7.2]
|
250
250
|
def change
|
251
251
|
create_table :users do |t|
|
252
252
|
# ...
|
@@ -264,7 +264,7 @@ If you intend to drop an existing table, run `drop_table` first.
|
|
264
264
|
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.
|
265
265
|
|
266
266
|
```ruby
|
267
|
-
class AddIdToCitiesUsers < ActiveRecord::Migration[7.
|
267
|
+
class AddIdToCitiesUsers < ActiveRecord::Migration[7.2]
|
268
268
|
def change
|
269
269
|
add_column :cities_users, :id, :primary_key
|
270
270
|
end
|
@@ -284,7 +284,7 @@ Create a new table and migrate the data with the same steps as [renaming a table
|
|
284
284
|
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.
|
285
285
|
|
286
286
|
```ruby
|
287
|
-
class AddSomeColumnToUsers < ActiveRecord::Migration[7.
|
287
|
+
class AddSomeColumnToUsers < ActiveRecord::Migration[7.2]
|
288
288
|
def change
|
289
289
|
add_column :users, :some_column, :virtual, type: :string, as: "...", stored: true
|
290
290
|
end
|
@@ -304,7 +304,7 @@ Add a non-generated column and use callbacks or triggers instead (or a virtual g
|
|
304
304
|
Adding a check constraint blocks reads and writes in Postgres and blocks writes in MySQL and MariaDB while every row is checked.
|
305
305
|
|
306
306
|
```ruby
|
307
|
-
class AddCheckConstraint < ActiveRecord::Migration[7.
|
307
|
+
class AddCheckConstraint < ActiveRecord::Migration[7.2]
|
308
308
|
def change
|
309
309
|
add_check_constraint :users, "price > 0", name: "price_check"
|
310
310
|
end
|
@@ -316,7 +316,7 @@ end
|
|
316
316
|
Add the check constraint without validating existing rows:
|
317
317
|
|
318
318
|
```ruby
|
319
|
-
class AddCheckConstraint < ActiveRecord::Migration[7.
|
319
|
+
class AddCheckConstraint < ActiveRecord::Migration[7.2]
|
320
320
|
def change
|
321
321
|
add_check_constraint :users, "price > 0", name: "price_check", validate: false
|
322
322
|
end
|
@@ -326,7 +326,7 @@ end
|
|
326
326
|
Then validate them in a separate migration.
|
327
327
|
|
328
328
|
```ruby
|
329
|
-
class ValidateCheckConstraint < ActiveRecord::Migration[7.
|
329
|
+
class ValidateCheckConstraint < ActiveRecord::Migration[7.2]
|
330
330
|
def change
|
331
331
|
validate_check_constraint :users, name: "price_check"
|
332
332
|
end
|
@@ -342,7 +342,7 @@ end
|
|
342
342
|
Strong Migrations can’t ensure safety for raw SQL statements. Make really sure that what you’re doing is safe, then use:
|
343
343
|
|
344
344
|
```ruby
|
345
|
-
class ExecuteSQL < ActiveRecord::Migration[7.
|
345
|
+
class ExecuteSQL < ActiveRecord::Migration[7.2]
|
346
346
|
def change
|
347
347
|
safety_assured { execute "..." }
|
348
348
|
end
|
@@ -356,7 +356,7 @@ end
|
|
356
356
|
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/).
|
357
357
|
|
358
358
|
```ruby
|
359
|
-
class AddSomeColumnToUsers < ActiveRecord::Migration[7.
|
359
|
+
class AddSomeColumnToUsers < ActiveRecord::Migration[7.2]
|
360
360
|
def change
|
361
361
|
add_column :users, :some_column, :text
|
362
362
|
User.update_all some_column: "default_value"
|
@@ -371,7 +371,7 @@ Also, running a single query to update data can cause issues for large tables.
|
|
371
371
|
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!`.
|
372
372
|
|
373
373
|
```ruby
|
374
|
-
class BackfillSomeColumn < ActiveRecord::Migration[7.
|
374
|
+
class BackfillSomeColumn < ActiveRecord::Migration[7.2]
|
375
375
|
disable_ddl_transaction!
|
376
376
|
|
377
377
|
def up
|
@@ -392,7 +392,7 @@ end
|
|
392
392
|
In Postgres, adding an index non-concurrently blocks writes.
|
393
393
|
|
394
394
|
```ruby
|
395
|
-
class AddSomeIndexToUsers < ActiveRecord::Migration[7.
|
395
|
+
class AddSomeIndexToUsers < ActiveRecord::Migration[7.2]
|
396
396
|
def change
|
397
397
|
add_index :users, :some_column
|
398
398
|
end
|
@@ -404,7 +404,7 @@ end
|
|
404
404
|
Add indexes concurrently.
|
405
405
|
|
406
406
|
```ruby
|
407
|
-
class AddSomeIndexToUsers < ActiveRecord::Migration[7.
|
407
|
+
class AddSomeIndexToUsers < ActiveRecord::Migration[7.2]
|
408
408
|
disable_ddl_transaction!
|
409
409
|
|
410
410
|
def change
|
@@ -430,7 +430,7 @@ rails g index table column
|
|
430
430
|
Rails adds an index non-concurrently to references by default, which blocks writes in Postgres.
|
431
431
|
|
432
432
|
```ruby
|
433
|
-
class AddReferenceToUsers < ActiveRecord::Migration[7.
|
433
|
+
class AddReferenceToUsers < ActiveRecord::Migration[7.2]
|
434
434
|
def change
|
435
435
|
add_reference :users, :city
|
436
436
|
end
|
@@ -442,7 +442,7 @@ end
|
|
442
442
|
Make sure the index is added concurrently.
|
443
443
|
|
444
444
|
```ruby
|
445
|
-
class AddReferenceToUsers < ActiveRecord::Migration[7.
|
445
|
+
class AddReferenceToUsers < ActiveRecord::Migration[7.2]
|
446
446
|
disable_ddl_transaction!
|
447
447
|
|
448
448
|
def change
|
@@ -460,7 +460,7 @@ end
|
|
460
460
|
In Postgres, adding a foreign key blocks writes on both tables.
|
461
461
|
|
462
462
|
```ruby
|
463
|
-
class AddForeignKeyOnUsers < ActiveRecord::Migration[7.
|
463
|
+
class AddForeignKeyOnUsers < ActiveRecord::Migration[7.2]
|
464
464
|
def change
|
465
465
|
add_foreign_key :users, :orders
|
466
466
|
end
|
@@ -470,7 +470,7 @@ end
|
|
470
470
|
or
|
471
471
|
|
472
472
|
```ruby
|
473
|
-
class AddReferenceToUsers < ActiveRecord::Migration[7.
|
473
|
+
class AddReferenceToUsers < ActiveRecord::Migration[7.2]
|
474
474
|
def change
|
475
475
|
add_reference :users, :order, foreign_key: true
|
476
476
|
end
|
@@ -482,7 +482,7 @@ end
|
|
482
482
|
Add the foreign key without validating existing rows:
|
483
483
|
|
484
484
|
```ruby
|
485
|
-
class AddForeignKeyOnUsers < ActiveRecord::Migration[7.
|
485
|
+
class AddForeignKeyOnUsers < ActiveRecord::Migration[7.2]
|
486
486
|
def change
|
487
487
|
add_foreign_key :users, :orders, validate: false
|
488
488
|
end
|
@@ -492,7 +492,7 @@ end
|
|
492
492
|
Then validate them in a separate migration.
|
493
493
|
|
494
494
|
```ruby
|
495
|
-
class ValidateForeignKeyOnUsers < ActiveRecord::Migration[7.
|
495
|
+
class ValidateForeignKeyOnUsers < ActiveRecord::Migration[7.2]
|
496
496
|
def change
|
497
497
|
validate_foreign_key :users, :orders
|
498
498
|
end
|
@@ -506,7 +506,7 @@ end
|
|
506
506
|
In Postgres, adding a unique constraint creates a unique index, which blocks reads and writes.
|
507
507
|
|
508
508
|
```ruby
|
509
|
-
class AddUniqueConstraint < ActiveRecord::Migration[7.
|
509
|
+
class AddUniqueConstraint < ActiveRecord::Migration[7.2]
|
510
510
|
def change
|
511
511
|
add_unique_constraint :users, :some_column
|
512
512
|
end
|
@@ -518,7 +518,7 @@ end
|
|
518
518
|
Create a unique index concurrently, then use it for the constraint.
|
519
519
|
|
520
520
|
```ruby
|
521
|
-
class AddUniqueConstraint < ActiveRecord::Migration[7.
|
521
|
+
class AddUniqueConstraint < ActiveRecord::Migration[7.2]
|
522
522
|
disable_ddl_transaction!
|
523
523
|
|
524
524
|
def up
|
@@ -539,7 +539,7 @@ end
|
|
539
539
|
In Postgres, adding an exclusion constraint blocks reads and writes while every row is checked.
|
540
540
|
|
541
541
|
```ruby
|
542
|
-
class AddExclusionConstraint < ActiveRecord::Migration[7.
|
542
|
+
class AddExclusionConstraint < ActiveRecord::Migration[7.2]
|
543
543
|
def change
|
544
544
|
add_exclusion_constraint :users, "number WITH =", using: :gist
|
545
545
|
end
|
@@ -557,7 +557,7 @@ end
|
|
557
557
|
In Postgres, there’s no equality operator for the `json` column type, which can cause errors for existing `SELECT DISTINCT` queries in your application.
|
558
558
|
|
559
559
|
```ruby
|
560
|
-
class AddPropertiesToUsers < ActiveRecord::Migration[7.
|
560
|
+
class AddPropertiesToUsers < ActiveRecord::Migration[7.2]
|
561
561
|
def change
|
562
562
|
add_column :users, :properties, :json
|
563
563
|
end
|
@@ -569,7 +569,7 @@ end
|
|
569
569
|
Use `jsonb` instead.
|
570
570
|
|
571
571
|
```ruby
|
572
|
-
class AddPropertiesToUsers < ActiveRecord::Migration[7.
|
572
|
+
class AddPropertiesToUsers < ActiveRecord::Migration[7.2]
|
573
573
|
def change
|
574
574
|
add_column :users, :properties, :jsonb
|
575
575
|
end
|
@@ -585,7 +585,7 @@ end
|
|
585
585
|
In Postgres, setting `NOT NULL` on an existing column blocks reads and writes while every row is checked.
|
586
586
|
|
587
587
|
```ruby
|
588
|
-
class SetSomeColumnNotNull < ActiveRecord::Migration[7.
|
588
|
+
class SetSomeColumnNotNull < ActiveRecord::Migration[7.2]
|
589
589
|
def change
|
590
590
|
change_column_null :users, :some_column, false
|
591
591
|
end
|
@@ -597,7 +597,7 @@ end
|
|
597
597
|
Instead, add a check constraint.
|
598
598
|
|
599
599
|
```ruby
|
600
|
-
class SetSomeColumnNotNull < ActiveRecord::Migration[7.
|
600
|
+
class SetSomeColumnNotNull < ActiveRecord::Migration[7.2]
|
601
601
|
def change
|
602
602
|
add_check_constraint :users, "some_column IS NOT NULL", name: "users_some_column_null", validate: false
|
603
603
|
end
|
@@ -607,7 +607,7 @@ end
|
|
607
607
|
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.
|
608
608
|
|
609
609
|
```ruby
|
610
|
-
class ValidateSomeColumnNotNull < ActiveRecord::Migration[7.
|
610
|
+
class ValidateSomeColumnNotNull < ActiveRecord::Migration[7.2]
|
611
611
|
def change
|
612
612
|
validate_check_constraint :users, name: "users_some_column_null"
|
613
613
|
change_column_null :users, :some_column, false
|
@@ -623,7 +623,7 @@ end
|
|
623
623
|
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.
|
624
624
|
|
625
625
|
```ruby
|
626
|
-
class AddSomeColumnToUsers < ActiveRecord::Migration[7.
|
626
|
+
class AddSomeColumnToUsers < ActiveRecord::Migration[7.2]
|
627
627
|
def change
|
628
628
|
add_column :users, :some_column, :uuid, default: "gen_random_uuid()"
|
629
629
|
end
|
@@ -635,7 +635,7 @@ end
|
|
635
635
|
Instead, add the column without a default value, then change the default.
|
636
636
|
|
637
637
|
```ruby
|
638
|
-
class AddSomeColumnToUsers < ActiveRecord::Migration[7.
|
638
|
+
class AddSomeColumnToUsers < ActiveRecord::Migration[7.2]
|
639
639
|
def up
|
640
640
|
add_column :users, :some_column, :uuid
|
641
641
|
change_column_default :users, :some_column, from: nil, to: "gen_random_uuid()"
|
@@ -686,7 +686,7 @@ config.active_record.partial_inserts = false
|
|
686
686
|
Adding a non-unique index with more than three columns rarely improves performance.
|
687
687
|
|
688
688
|
```ruby
|
689
|
-
class AddSomeIndexToUsers < ActiveRecord::Migration[7.
|
689
|
+
class AddSomeIndexToUsers < ActiveRecord::Migration[7.2]
|
690
690
|
def change
|
691
691
|
add_index :users, [:a, :b, :c, :d]
|
692
692
|
end
|
@@ -698,7 +698,7 @@ end
|
|
698
698
|
Instead, start an index with columns that narrow down the results the most.
|
699
699
|
|
700
700
|
```ruby
|
701
|
-
class AddSomeIndexToUsers < ActiveRecord::Migration[7.
|
701
|
+
class AddSomeIndexToUsers < ActiveRecord::Migration[7.2]
|
702
702
|
def change
|
703
703
|
add_index :users, [:d, :b]
|
704
704
|
end
|
@@ -712,7 +712,7 @@ For Postgres, be sure to add them concurrently.
|
|
712
712
|
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.
|
713
713
|
|
714
714
|
```ruby
|
715
|
-
class MySafeMigration < ActiveRecord::Migration[7.
|
715
|
+
class MySafeMigration < ActiveRecord::Migration[7.2]
|
716
716
|
def change
|
717
717
|
safety_assured { remove_column :users, :some_column }
|
718
718
|
end
|
@@ -934,15 +934,18 @@ You probably don’t need this gem for smaller projects, as operations that are
|
|
934
934
|
|
935
935
|
## Additional Reading
|
936
936
|
|
937
|
-
- [Rails Migrations with No Downtime](https://pedro.herokuapp.com/past/2011/7/13/rails_migrations_with_no_downtime/)
|
938
937
|
- [PostgreSQL at Scale: Database Schema Changes Without Downtime](https://medium.com/braintree-product-technology/postgresql-at-scale-database-schema-changes-without-downtime-20d3749ed680)
|
939
|
-
- [
|
938
|
+
- [MySQL InnoDB Online DDL Operations](https://dev.mysql.com/doc/refman/en/innodb-online-ddl-operations.html)
|
940
939
|
- [MariaDB InnoDB Online DDL Overview](https://mariadb.com/kb/en/innodb-online-ddl-overview/)
|
941
940
|
|
942
941
|
## Credits
|
943
942
|
|
944
943
|
Thanks to Bob Remeika and David Waller for the [original code](https://github.com/foobarfighter/safe-migrations) and [Sean Huber](https://github.com/LendingHome/zero_downtime_migrations) for the bad/good readme format.
|
945
944
|
|
945
|
+
## History
|
946
|
+
|
947
|
+
View the [changelog](https://github.com/ankane/strong_migrations/blob/master/CHANGELOG.md)
|
948
|
+
|
946
949
|
## Contributing
|
947
950
|
|
948
951
|
Everyone is encouraged to help improve this project. Here are a few ways you can help:
|
@@ -17,5 +17,16 @@ module StrongMigrations
|
|
17
17
|
def columns(*args, **options)
|
18
18
|
@connection.columns(*args, **options).sort_by(&:name)
|
19
19
|
end
|
20
|
+
|
21
|
+
# forward private methods with send
|
22
|
+
# method_missing cannot tell how method was called
|
23
|
+
# this is not ideal, but other solutions have drawbacks
|
24
|
+
def send(name, ...)
|
25
|
+
if respond_to?(name, true)
|
26
|
+
super
|
27
|
+
else
|
28
|
+
@connection.send(name, ...)
|
29
|
+
end
|
30
|
+
end
|
20
31
|
end
|
21
32
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: strong_migrations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2024-
|
13
|
+
date: 2024-10-15 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -75,7 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
75
75
|
- !ruby/object:Gem::Version
|
76
76
|
version: '0'
|
77
77
|
requirements: []
|
78
|
-
rubygems_version: 3.5.
|
78
|
+
rubygems_version: 3.5.16
|
79
79
|
signing_key:
|
80
80
|
specification_version: 4
|
81
81
|
summary: Catch unsafe migrations in development
|