zero_downtime_migrations 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +11 -9
- data/lib/zero_downtime_migrations/validation/add_column.rb +12 -6
- data/lib/zero_downtime_migrations/validation/add_index.rb +5 -1
- data/lib/zero_downtime_migrations/validation/ddl_migration.rb +2 -0
- data/lib/zero_downtime_migrations/validation/mixed_migration.rb +9 -2
- data/zero_downtime_migrations.gemspec +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57fd5eb4562491e398c6357a0d13d1647e88cc34
|
4
|
+
data.tar.gz: a15d532d3130ace1169a5473ee5d4b0071c7945d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 970ce6605bf9fb36e7523f4819978ca02cd711b72ec43af74caa5caaebfd38072f7b5fd1a409cf3613f11c289f41f54f04321afa00c94d13556b04edf38f13a5
|
7
|
+
data.tar.gz: 984caa68bb7e0fe27b0d211744b8b04bad2c7dd40c6967eb4af6884a3871dba74dc3eeb5bff2dfb55e90c421c8675e54e6c894980dc19c396b545fb78b4bc340
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -36,7 +36,7 @@ These exceptions display clear instructions of how to perform the same operation
|
|
36
36
|
|
37
37
|
#### Bad
|
38
38
|
|
39
|
-
This
|
39
|
+
This can take a long time with significant database size or traffic and lock your table!
|
40
40
|
|
41
41
|
```ruby
|
42
42
|
class AddPublishedToPosts < ActiveRecord::Migration[5.0]
|
@@ -48,7 +48,7 @@ end
|
|
48
48
|
|
49
49
|
#### Good
|
50
50
|
|
51
|
-
|
51
|
+
First let’s add the column without a default. When we add a column with a default it has to lock the table while it performs an UPDATE for ALL rows to set this new default.
|
52
52
|
|
53
53
|
```ruby
|
54
54
|
class AddPublishedToPosts < ActiveRecord::Migration[5.0]
|
@@ -58,7 +58,7 @@ class AddPublishedToPosts < ActiveRecord::Migration[5.0]
|
|
58
58
|
end
|
59
59
|
```
|
60
60
|
|
61
|
-
Then set the new column default in a separate migration. Note that this does not update any existing data.
|
61
|
+
Then we’ll set the new column default in a separate migration. Note that this does not update any existing data! This only sets the default for newly inserted rows going forward.
|
62
62
|
|
63
63
|
```ruby
|
64
64
|
class SetPublishedDefaultOnPosts < ActiveRecord::Migration[5.0]
|
@@ -68,7 +68,7 @@ class SetPublishedDefaultOnPosts < ActiveRecord::Migration[5.0]
|
|
68
68
|
end
|
69
69
|
```
|
70
70
|
|
71
|
-
|
71
|
+
Finally we’ll backport the default value for existing data in batches. This should be done in its own migration as well. Updating in batches allows us to lock 1000 rows at a time (or whatever batch size we prefer).
|
72
72
|
|
73
73
|
```ruby
|
74
74
|
class BackportPublishedDefaultOnPosts < ActiveRecord::Migration[5.0]
|
@@ -85,7 +85,7 @@ end
|
|
85
85
|
|
86
86
|
#### Bad
|
87
87
|
|
88
|
-
This action can
|
88
|
+
This action can lock your database table while indexing existing data!
|
89
89
|
|
90
90
|
```ruby
|
91
91
|
class IndexUsersOnEmail < ActiveRecord::Migration[5.0]
|
@@ -99,6 +99,8 @@ end
|
|
99
99
|
|
100
100
|
Instead, let's add the index concurrently in its own migration with the DDL transaction disabled.
|
101
101
|
|
102
|
+
This allows PostgreSQL to build the index without locking in a way that prevent concurrent inserts, updates, or deletes on the table. Standard indexes lock out writes (but not reads) on the table.
|
103
|
+
|
102
104
|
```ruby
|
103
105
|
class IndexUsersOnEmail < ActiveRecord::Migration[5.0]
|
104
106
|
disable_ddl_transaction!
|
@@ -129,9 +131,9 @@ end
|
|
129
131
|
|
130
132
|
Instead, let's split apart these types of migrations into separate files.
|
131
133
|
|
132
|
-
* Introduce schema changes with methods like `create_table` or `add_column` in one file.
|
133
|
-
* Update data with methods like `update_all` or `save` in another file.
|
134
|
-
* Add indexes concurrently within their own file as well.
|
134
|
+
* Introduce schema changes with methods like `create_table` or `add_column` in one file. These should be run within a DDL transaction so that they can be rolled back if there are any issues.
|
135
|
+
* Update data with methods like `update_all` or `save` in another file. Data migrations tend to be much more error prone than changing the schema or adding indexes.
|
136
|
+
* Add indexes concurrently within their own file as well. Indexes should be created without the DDL transaction enabled to avoid table locking.
|
135
137
|
|
136
138
|
```ruby
|
137
139
|
class AddPublishedToPosts < ActiveRecord::Migration[5.0]
|
@@ -163,7 +165,7 @@ end
|
|
163
165
|
|
164
166
|
#### Bad
|
165
167
|
|
166
|
-
The DDL transaction should only be disabled for migrations that add indexes.
|
168
|
+
The DDL transaction should only be disabled for migrations that add indexes. All other types of migrations should keep the DDL transaction enabled so that changes can be rolled back if any unexpected errors occur.
|
167
169
|
|
168
170
|
```ruby
|
169
171
|
class AddPublishedToPosts < ActiveRecord::Migration[5.0]
|
@@ -12,9 +12,12 @@ module ZeroDowntimeMigrations
|
|
12
12
|
<<-MESSAGE.strip_heredoc
|
13
13
|
Adding a column with a default is unsafe!
|
14
14
|
|
15
|
-
This
|
15
|
+
This can take a long time with significant database
|
16
|
+
size or traffic and lock your table!
|
16
17
|
|
17
|
-
|
18
|
+
First let’s add the column without a default. When we add
|
19
|
+
a column with a default it has to lock the table while it
|
20
|
+
performs an UPDATE for ALL rows to set this new default.
|
18
21
|
|
19
22
|
class Add#{column_title}To#{table_title} < ActiveRecord::Migration
|
20
23
|
def change
|
@@ -22,8 +25,9 @@ module ZeroDowntimeMigrations
|
|
22
25
|
end
|
23
26
|
end
|
24
27
|
|
25
|
-
Then set the new column default in a separate migration.
|
26
|
-
this does not update any existing data
|
28
|
+
Then we’ll set the new column default in a separate migration.
|
29
|
+
Note that this does not update any existing data! This only
|
30
|
+
sets the default for newly inserted rows going forward.
|
27
31
|
|
28
32
|
class AddDefault#{column_title}To#{table_title} < ActiveRecord::Migration
|
29
33
|
def change
|
@@ -31,8 +35,10 @@ module ZeroDowntimeMigrations
|
|
31
35
|
end
|
32
36
|
end
|
33
37
|
|
34
|
-
|
35
|
-
This should be done in its own migration as well.
|
38
|
+
Finally we’ll backport the default value for existing data in
|
39
|
+
batches. This should be done in its own migration as well.
|
40
|
+
Updating in batches allows us to lock 1000 rows at a time
|
41
|
+
(or whatever batch size we prefer).
|
36
42
|
|
37
43
|
class BackportDefault#{column_title}To#{table_title} < ActiveRecord::Migration
|
38
44
|
def change
|
@@ -12,11 +12,15 @@ module ZeroDowntimeMigrations
|
|
12
12
|
<<-MESSAGE.strip_heredoc
|
13
13
|
Adding a non-concurrent index is unsafe!
|
14
14
|
|
15
|
-
This action can
|
15
|
+
This action can lock your database table while indexing existing data!
|
16
16
|
|
17
17
|
Instead, let's add the index concurrently in its own migration with
|
18
18
|
the DDL transaction disabled.
|
19
19
|
|
20
|
+
This allows PostgreSQL to build the index without locking in a way
|
21
|
+
that prevent concurrent inserts, updates, or deletes on the table.
|
22
|
+
Standard indexes lock out writes (but not reads) on the table.
|
23
|
+
|
20
24
|
class Index#{table_title}On#{column_title} < ActiveRecord::Migration
|
21
25
|
disable_ddl_transaction!
|
22
26
|
|
@@ -13,6 +13,8 @@ module ZeroDowntimeMigrations
|
|
13
13
|
Disabling the DDL transaction is unsafe!
|
14
14
|
|
15
15
|
The DDL transaction should only be disabled for migrations that add indexes.
|
16
|
+
All other types of migrations should keep the DDL transaction enabled so
|
17
|
+
that changes can be rolled back if any unexpected errors occur.
|
16
18
|
|
17
19
|
Any other data or schema changes must live in their own migration files with
|
18
20
|
the DDL transaction enabled just in case they need to be rolled back.
|
@@ -14,9 +14,16 @@ module ZeroDowntimeMigrations
|
|
14
14
|
|
15
15
|
Instead, let's split apart these types of migrations into separate files.
|
16
16
|
|
17
|
-
* Introduce schema changes with methods like `create_table` or `add_column`
|
17
|
+
* Introduce schema changes with methods like `create_table` or `add_column`
|
18
|
+
in one file. These should be run within a DDL transaction so that they
|
19
|
+
can be rolled back if there are any issues.
|
20
|
+
|
18
21
|
* Update data with methods like `update_all` or `save` in another file.
|
19
|
-
|
22
|
+
Data migrations tend to be much more error prone than changing the
|
23
|
+
schema or adding indexes.
|
24
|
+
|
25
|
+
* Add indexes concurrently within their own file as well. Indexes should
|
26
|
+
be created without the DDL transaction enabled to avoid table locking.
|
20
27
|
|
21
28
|
If you're 100% positive that this migration is already safe, then simply
|
22
29
|
add a call to `safety_assured` to your migration.
|
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.required_ruby_version = ">= 2.0.0"
|
12
12
|
s.summary = "Zero downtime migrations with ActiveRecord and PostgreSQL"
|
13
13
|
s.test_files = `git ls-files -- spec/*`.split("\n")
|
14
|
-
s.version = "0.0.
|
14
|
+
s.version = "0.0.7"
|
15
15
|
|
16
16
|
s.add_dependency "activerecord"
|
17
17
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zero_downtime_migrations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- LendingHome
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|