rails_async_migrations 1.0.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 +7 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +138 -0
- data/.travis.yml +7 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +171 -0
- data/LICENSE.txt +21 -0
- data/README.md +162 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/generators/rails_async_migrations/install_generator.rb +22 -0
- data/lib/generators/rails_async_migrations/templates/create_async_schema_migrations.rb +11 -0
- data/lib/rails_async_migrations/config.rb +13 -0
- data/lib/rails_async_migrations/connection/active_record.rb +59 -0
- data/lib/rails_async_migrations/migration/check_queue.rb +72 -0
- data/lib/rails_async_migrations/migration/fire_migration.rb +56 -0
- data/lib/rails_async_migrations/migration/lock.rb +84 -0
- data/lib/rails_async_migrations/migration/method_added.rb +24 -0
- data/lib/rails_async_migrations/migration/overwrite.rb +28 -0
- data/lib/rails_async_migrations/migration/run.rb +73 -0
- data/lib/rails_async_migrations/migration/unlock.rb +42 -0
- data/lib/rails_async_migrations/migration.rb +23 -0
- data/lib/rails_async_migrations/models/async_schema_migration.rb +18 -0
- data/lib/rails_async_migrations/mutators/base.rb +9 -0
- data/lib/rails_async_migrations/mutators/trigger_callback.rb +57 -0
- data/lib/rails_async_migrations/mutators/turn_async.rb +22 -0
- data/lib/rails_async_migrations/mutators.rb +24 -0
- data/lib/rails_async_migrations/railtie.rb +7 -0
- data/lib/rails_async_migrations/tracer.rb +23 -0
- data/lib/rails_async_migrations/version.rb +3 -0
- data/lib/rails_async_migrations/workers/sidekiq/check_queue_worker.rb +16 -0
- data/lib/rails_async_migrations/workers/sidekiq/fire_migration_worker.rb +18 -0
- data/lib/rails_async_migrations/workers.rb +58 -0
- data/lib/rails_async_migrations.rb +35 -0
- data/lib/tasks/rails_async_migrations.rake +6 -0
- data/rails_async_migrations.gemspec +30 -0
- metadata +193 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a9ffc3ef530a9f156013b067663ddeea6b06c65e155a668c56936b6803a50945
|
4
|
+
data.tar.gz: e72fe2f4fe6289eff981e77653a3a1d00b97a90bd4750138a816ca38ea2a594f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 42e96d488de2c1b5d49b039e0174a193cfcbccae1fa619526e46f91b9edb840cf4f2c7041f325f43800cd705d3b5935d694b798402f7d9f3c6b34697fd721885
|
7
|
+
data.tar.gz: e8de2e332165ebdd35554bb1df53c74b77f5a3dd884cafe9624f522bcec133810b589e8c89263cad1097ead136f0d4e7f2ad0b2a660373c325c158d244b56f1b
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
# require: rubocop-rspec
|
2
|
+
|
3
|
+
AllCops:
|
4
|
+
TargetRubyVersion: 2.5
|
5
|
+
# RuboCop has a bunch of cops enabled by default. This setting tells RuboCop
|
6
|
+
# to ignore them, so only the ones explicitly set in this file are enabled.
|
7
|
+
DisabledByDefault: true
|
8
|
+
Exclude:
|
9
|
+
- '**/.DS_Store'
|
10
|
+
- 'db/**/*'
|
11
|
+
- 'tmp/**/*'
|
12
|
+
- 'vendor/**/*'
|
13
|
+
- 'bin/**/*'
|
14
|
+
- 'log/**/*'
|
15
|
+
- 'script/**/*'
|
16
|
+
|
17
|
+
Include:
|
18
|
+
- 'app/**/*'
|
19
|
+
- 'spec/**/*'
|
20
|
+
- 'lib/**/*'
|
21
|
+
|
22
|
+
# Prefer &&/|| over and/or.
|
23
|
+
Style/AndOr:
|
24
|
+
Enabled: true
|
25
|
+
|
26
|
+
# Do not use braces for hash literals when they are the last argument of a
|
27
|
+
# method call.
|
28
|
+
Style/BracesAroundHashParameters:
|
29
|
+
Enabled: true
|
30
|
+
|
31
|
+
# Align `when` with `case`.
|
32
|
+
Layout/CaseIndentation:
|
33
|
+
Enabled: true
|
34
|
+
|
35
|
+
# Align comments with method definitions.
|
36
|
+
Layout/CommentIndentation:
|
37
|
+
Enabled: true
|
38
|
+
|
39
|
+
# No extra empty lines.
|
40
|
+
Layout/EmptyLines:
|
41
|
+
Enabled: true
|
42
|
+
|
43
|
+
# In a regular class definition, no empty lines around the body.
|
44
|
+
Layout/EmptyLinesAroundClassBody:
|
45
|
+
Enabled: true
|
46
|
+
|
47
|
+
# In a regular method definition, no empty lines around the body.
|
48
|
+
Layout/EmptyLinesAroundMethodBody:
|
49
|
+
Enabled: true
|
50
|
+
|
51
|
+
# In a regular module definition, no empty lines around the body.
|
52
|
+
Layout/EmptyLinesAroundModuleBody:
|
53
|
+
Enabled: true
|
54
|
+
|
55
|
+
# Use Ruby >= 1.9 syntax for hashes. Prefer { a: :b } over { :a => :b }.
|
56
|
+
Style/HashSyntax:
|
57
|
+
Enabled: true
|
58
|
+
|
59
|
+
# Two spaces, no tabs (for indentation).
|
60
|
+
Layout/IndentationWidth:
|
61
|
+
Enabled: true
|
62
|
+
|
63
|
+
Layout/SpaceAfterColon:
|
64
|
+
Enabled: true
|
65
|
+
|
66
|
+
Layout/SpaceAfterComma:
|
67
|
+
Enabled: true
|
68
|
+
|
69
|
+
Layout/SpaceAroundEqualsInParameterDefault:
|
70
|
+
Enabled: true
|
71
|
+
|
72
|
+
Layout/SpaceAroundKeyword:
|
73
|
+
Enabled: true
|
74
|
+
|
75
|
+
Layout/SpaceAroundOperators:
|
76
|
+
Enabled: true
|
77
|
+
|
78
|
+
Layout/SpaceBeforeFirstArg:
|
79
|
+
Enabled: true
|
80
|
+
|
81
|
+
# Defining a method with parameters needs parentheses.
|
82
|
+
Style/MethodDefParentheses:
|
83
|
+
Enabled: true
|
84
|
+
|
85
|
+
# Use `foo {}` not `foo{}`.
|
86
|
+
Layout/SpaceBeforeBlockBraces:
|
87
|
+
Enabled: true
|
88
|
+
|
89
|
+
# Use `foo { bar }` not `foo {bar}`.
|
90
|
+
Layout/SpaceInsideBlockBraces:
|
91
|
+
Enabled: true
|
92
|
+
|
93
|
+
# Use `{ a: 1 }` not `{a:1}`.
|
94
|
+
Layout/SpaceInsideHashLiteralBraces:
|
95
|
+
Enabled: true
|
96
|
+
|
97
|
+
Layout/SpaceInsideParens:
|
98
|
+
Enabled: true
|
99
|
+
|
100
|
+
# Check quotes usage according to lint rule below.
|
101
|
+
Style/StringLiterals:
|
102
|
+
Enabled: true
|
103
|
+
EnforcedStyle: single_quotes
|
104
|
+
|
105
|
+
# Detect hard tabs, no hard tabs.
|
106
|
+
Layout/Tab:
|
107
|
+
Enabled: true
|
108
|
+
|
109
|
+
# Blank lines should not have any spaces.
|
110
|
+
Layout/TrailingBlankLines:
|
111
|
+
Enabled: true
|
112
|
+
|
113
|
+
# No trailing whitespace.
|
114
|
+
Layout/TrailingWhitespace:
|
115
|
+
Enabled: true
|
116
|
+
|
117
|
+
# Use quotes for string literals when they are enough.
|
118
|
+
Style/UnneededPercentQ:
|
119
|
+
Enabled: true
|
120
|
+
|
121
|
+
# Align `end` with the matching keyword or starting expression except for
|
122
|
+
# assignments, where it should be aligned with the LHS.
|
123
|
+
Layout/EndAlignment:
|
124
|
+
Enabled: true
|
125
|
+
EnforcedStyleAlignWith: variable
|
126
|
+
|
127
|
+
# Use my_method(my_arg) not my_method( my_arg ) or my_method my_arg.
|
128
|
+
Lint/RequireParentheses:
|
129
|
+
Enabled: true
|
130
|
+
|
131
|
+
Lint/Debugger:
|
132
|
+
Enabled: true
|
133
|
+
|
134
|
+
Metrics/LineLength:
|
135
|
+
# This will disable the rule completely, regardless what other options you put
|
136
|
+
Enabled: true
|
137
|
+
# Change the default 80 chars limit value
|
138
|
+
Max: 150
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rails_async_migrations (1.0.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
actioncable (5.2.2)
|
10
|
+
actionpack (= 5.2.2)
|
11
|
+
nio4r (~> 2.0)
|
12
|
+
websocket-driver (>= 0.6.1)
|
13
|
+
actionmailer (5.2.2)
|
14
|
+
actionpack (= 5.2.2)
|
15
|
+
actionview (= 5.2.2)
|
16
|
+
activejob (= 5.2.2)
|
17
|
+
mail (~> 2.5, >= 2.5.4)
|
18
|
+
rails-dom-testing (~> 2.0)
|
19
|
+
actionpack (5.2.2)
|
20
|
+
actionview (= 5.2.2)
|
21
|
+
activesupport (= 5.2.2)
|
22
|
+
rack (~> 2.0)
|
23
|
+
rack-test (>= 0.6.3)
|
24
|
+
rails-dom-testing (~> 2.0)
|
25
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
26
|
+
actionview (5.2.2)
|
27
|
+
activesupport (= 5.2.2)
|
28
|
+
builder (~> 3.1)
|
29
|
+
erubi (~> 1.4)
|
30
|
+
rails-dom-testing (~> 2.0)
|
31
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
32
|
+
activejob (5.2.2)
|
33
|
+
activesupport (= 5.2.2)
|
34
|
+
globalid (>= 0.3.6)
|
35
|
+
activemodel (5.2.2)
|
36
|
+
activesupport (= 5.2.2)
|
37
|
+
activerecord (5.2.2)
|
38
|
+
activemodel (= 5.2.2)
|
39
|
+
activesupport (= 5.2.2)
|
40
|
+
arel (>= 9.0)
|
41
|
+
activestorage (5.2.2)
|
42
|
+
actionpack (= 5.2.2)
|
43
|
+
activerecord (= 5.2.2)
|
44
|
+
marcel (~> 0.3.1)
|
45
|
+
activesupport (5.2.2)
|
46
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
47
|
+
i18n (>= 0.7, < 2)
|
48
|
+
minitest (~> 5.1)
|
49
|
+
tzinfo (~> 1.1)
|
50
|
+
arel (9.0.0)
|
51
|
+
builder (3.2.3)
|
52
|
+
coderay (1.1.2)
|
53
|
+
concurrent-ruby (1.1.3)
|
54
|
+
connection_pool (2.2.2)
|
55
|
+
crass (1.0.4)
|
56
|
+
database_cleaner (1.7.0)
|
57
|
+
delayed_job (4.1.5)
|
58
|
+
activesupport (>= 3.0, < 5.3)
|
59
|
+
delayed_job_active_record (4.1.3)
|
60
|
+
activerecord (>= 3.0, < 5.3)
|
61
|
+
delayed_job (>= 3.0, < 5)
|
62
|
+
diff-lcs (1.3)
|
63
|
+
erubi (1.7.1)
|
64
|
+
globalid (0.4.1)
|
65
|
+
activesupport (>= 4.2.0)
|
66
|
+
i18n (1.2.0)
|
67
|
+
concurrent-ruby (~> 1.0)
|
68
|
+
loofah (2.2.3)
|
69
|
+
crass (~> 1.0.2)
|
70
|
+
nokogiri (>= 1.5.9)
|
71
|
+
mail (2.7.1)
|
72
|
+
mini_mime (>= 0.1.1)
|
73
|
+
marcel (0.3.3)
|
74
|
+
mimemagic (~> 0.3.2)
|
75
|
+
method_source (0.9.2)
|
76
|
+
mimemagic (0.3.2)
|
77
|
+
mini_mime (1.0.1)
|
78
|
+
mini_portile2 (2.3.0)
|
79
|
+
minitest (5.11.3)
|
80
|
+
nio4r (2.3.1)
|
81
|
+
nokogiri (1.8.5)
|
82
|
+
mini_portile2 (~> 2.3.0)
|
83
|
+
pry (0.12.2)
|
84
|
+
coderay (~> 1.1.0)
|
85
|
+
method_source (~> 0.9.0)
|
86
|
+
rack (2.0.6)
|
87
|
+
rack-protection (2.0.5)
|
88
|
+
rack
|
89
|
+
rack-test (1.1.0)
|
90
|
+
rack (>= 1.0, < 3)
|
91
|
+
rails (5.2.2)
|
92
|
+
actioncable (= 5.2.2)
|
93
|
+
actionmailer (= 5.2.2)
|
94
|
+
actionpack (= 5.2.2)
|
95
|
+
actionview (= 5.2.2)
|
96
|
+
activejob (= 5.2.2)
|
97
|
+
activemodel (= 5.2.2)
|
98
|
+
activerecord (= 5.2.2)
|
99
|
+
activestorage (= 5.2.2)
|
100
|
+
activesupport (= 5.2.2)
|
101
|
+
bundler (>= 1.3.0)
|
102
|
+
railties (= 5.2.2)
|
103
|
+
sprockets-rails (>= 2.0.0)
|
104
|
+
rails-dom-testing (2.0.3)
|
105
|
+
activesupport (>= 4.2.0)
|
106
|
+
nokogiri (>= 1.6)
|
107
|
+
rails-html-sanitizer (1.0.4)
|
108
|
+
loofah (~> 2.2, >= 2.2.2)
|
109
|
+
railties (5.2.2)
|
110
|
+
actionpack (= 5.2.2)
|
111
|
+
activesupport (= 5.2.2)
|
112
|
+
method_source
|
113
|
+
rake (>= 0.8.7)
|
114
|
+
thor (>= 0.19.0, < 2.0)
|
115
|
+
rake (10.5.0)
|
116
|
+
redis (4.1.0)
|
117
|
+
rspec (3.8.0)
|
118
|
+
rspec-core (~> 3.8.0)
|
119
|
+
rspec-expectations (~> 3.8.0)
|
120
|
+
rspec-mocks (~> 3.8.0)
|
121
|
+
rspec-core (3.8.0)
|
122
|
+
rspec-support (~> 3.8.0)
|
123
|
+
rspec-expectations (3.8.2)
|
124
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
125
|
+
rspec-support (~> 3.8.0)
|
126
|
+
rspec-mocks (3.8.0)
|
127
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
128
|
+
rspec-support (~> 3.8.0)
|
129
|
+
rspec-sidekiq (3.0.3)
|
130
|
+
rspec-core (~> 3.0, >= 3.0.0)
|
131
|
+
sidekiq (>= 2.4.0)
|
132
|
+
rspec-support (3.8.0)
|
133
|
+
sidekiq (5.2.3)
|
134
|
+
connection_pool (~> 2.2, >= 2.2.2)
|
135
|
+
rack-protection (>= 1.5.0)
|
136
|
+
redis (>= 3.3.5, < 5)
|
137
|
+
sprockets (3.7.2)
|
138
|
+
concurrent-ruby (~> 1.0)
|
139
|
+
rack (> 1, < 3)
|
140
|
+
sprockets-rails (3.2.1)
|
141
|
+
actionpack (>= 4.0)
|
142
|
+
activesupport (>= 4.0)
|
143
|
+
sprockets (>= 3.0.0)
|
144
|
+
sqlite3 (1.3.13)
|
145
|
+
thor (0.20.3)
|
146
|
+
thread_safe (0.3.6)
|
147
|
+
tzinfo (1.2.5)
|
148
|
+
thread_safe (~> 0.1)
|
149
|
+
websocket-driver (0.7.0)
|
150
|
+
websocket-extensions (>= 0.1.0)
|
151
|
+
websocket-extensions (0.1.3)
|
152
|
+
|
153
|
+
PLATFORMS
|
154
|
+
ruby
|
155
|
+
|
156
|
+
DEPENDENCIES
|
157
|
+
activerecord (~> 5.2.0)
|
158
|
+
bundler (~> 1.17)
|
159
|
+
database_cleaner
|
160
|
+
delayed_job_active_record (~> 4.1.3)
|
161
|
+
pry (~> 0.12)
|
162
|
+
rails (~> 5.2)
|
163
|
+
rails_async_migrations!
|
164
|
+
rake (~> 10.0)
|
165
|
+
rspec (~> 3.0)
|
166
|
+
rspec-sidekiq
|
167
|
+
sidekiq (~> 5.2.3)
|
168
|
+
sqlite3
|
169
|
+
|
170
|
+
BUNDLED WITH
|
171
|
+
1.17.1
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018 Laurent Schaffner
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
# RailsAsyncMigrations
|
2
|
+
|
3
|
+
`ActiveRecord::Migration` extension to turn your migrations asynschonous in a simple and straight forward way.
|
4
|
+
|
5
|
+
## Motives
|
6
|
+
|
7
|
+
This library was made with the intent to help small companies which are struggling to scale at technical level. Small projects don't need asynchronous migrations queues, and big companies build their own parallel systems when facing scaling problems, but what about medium sized companies with limited resources ?
|
8
|
+
|
9
|
+
When a project grows, your database starts to be heavy and changing the data through the deployment process can be very painful. There are numerous reasons you want this process to be [at least] partially asynchronous.
|
10
|
+
|
11
|
+
Most people turn heavy data changes into `rake tasks` or split workers; there are two schools of thought about this.
|
12
|
+
|
13
|
+
1. Migrations should only mutate database structures and not its data, and if it's the case, it should be split and processed via other means.
|
14
|
+
2. Migrations are everything which alter data one time, typically during a deployment of new code and structure.
|
15
|
+
|
16
|
+
Turning data changes into a `rake task` can be a good idea, and it's ideal to test it out too, but sometimes you need this **fat ass loop** of **>1,000,000 records** which will be run **once, and only once** to be run fast and without locking down the deployment process itself; making a `rake task` for that is overkill. After all, it will only be used once and within a specific structure / data conxtext. This gem is here to answer this need.
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
Add this line to your application's Gemfile:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
gem 'rails_async_migrations'
|
24
|
+
```
|
25
|
+
|
26
|
+
And then execute:
|
27
|
+
|
28
|
+
$ bundle
|
29
|
+
|
30
|
+
After the gem has been installed, use the generator to add the needed changes
|
31
|
+
|
32
|
+
$ rails generate rails_async_migrations:install
|
33
|
+
|
34
|
+
This will add a new migration for the table `async_schema_migrations` which will be used by the gem. You can also add the migration yourself like so:
|
35
|
+
|
36
|
+
```
|
37
|
+
class CreateAsyncSchemaMigrations < ActiveRecord::Migration[5.2]
|
38
|
+
def change
|
39
|
+
create_table :async_schema_migrations do |t|
|
40
|
+
t.string :version
|
41
|
+
t.string :direction
|
42
|
+
t.string :state
|
43
|
+
|
44
|
+
t.timestamps
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
## Usage
|
51
|
+
|
52
|
+
To turn some of your migrations asynchronous, generate a migration as you would normally do and use the `turn_async` keyword.
|
53
|
+
|
54
|
+
```
|
55
|
+
class Test < ActiveRecord::Migration[5.2]
|
56
|
+
turn_async
|
57
|
+
|
58
|
+
def change
|
59
|
+
create_table 'tests' do |t|
|
60
|
+
t.string :test
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
From now on, when you run this migration it'll simply run the normal queue, but the content of `#change` will be taken away and later on executed into an asynchronous queue.
|
67
|
+
|
68
|
+
What is turned asynchronous is executed exactly the same way as a classical migration, which means you can use all keywords of the classic `ActiveRecord::Migration` such as `create_table`, `add_column`, etc.
|
69
|
+
|
70
|
+
## Configuration
|
71
|
+
|
72
|
+
**No configuration is needed to start using this library**, but some options are given to adapt to your needs.
|
73
|
+
|
74
|
+
Add the following lines into your `config/initializer/` folder
|
75
|
+
|
76
|
+
```
|
77
|
+
RailsAsyncMigrations.config do |config|
|
78
|
+
# :verbose can be used if you want a full log of the execution
|
79
|
+
config.mode = :quiet
|
80
|
+
|
81
|
+
# which kind of worker do you want to use for this library
|
82
|
+
# for now you have two options: :delayed_job or :sidekiq
|
83
|
+
config.workers = :sidekiq
|
84
|
+
|
85
|
+
# when the migration is turned asynchronous
|
86
|
+
# it watches over some specific `ActiveRecord` methods
|
87
|
+
# by adding them to this array, you'll lock and turn those methods asynchronous
|
88
|
+
# by removing them you'll let them use the classical migration process
|
89
|
+
# for example, if you set the `locked_methods` to %i[async] the migration will be processed normally
|
90
|
+
# but the content of the `#async` method will be taken away and executed within the asynchronous queue
|
91
|
+
config.locked_methods = %i[change up down]
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
## Queue
|
96
|
+
|
97
|
+
Each migration which is turned asynchronous follows each other, once one migration of the queue is ended without problem, it passes to the next one.
|
98
|
+
|
99
|
+
If it fails, the error will be raised within the worker so it retries until it eventually works, or until it's considered dead. None of the further asynchronous migrations will be run until you fix the failed one, which is a good protection for data consistency.
|
100
|
+
|
101
|
+
You can also manually launch the queue check and fire by using:
|
102
|
+
|
103
|
+
$ rake rails_async_migrations:check_queue
|
104
|
+
|
105
|
+
**For now, there is no rollback mechanism authorized, even if the source code is ready for it, it complexifies the build up logic and may not be needed in asynchronous cases.**
|
106
|
+
|
107
|
+
## States
|
108
|
+
|
109
|
+
| State | Description |
|
110
|
+
| -------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
111
|
+
| **created** | the migration has just been added through the classical migration queue |
|
112
|
+
| **pending** | the migration has been spotted by the asynchronous queue and will be processed |
|
113
|
+
| **processing** | the migration is being processed right now |
|
114
|
+
| **done** | the migration was successful |
|
115
|
+
| **failed** | the migration failed while being processed, there may be other attempts to make it pass depending your workers configuration |
|
116
|
+
|
117
|
+
## Failure handling
|
118
|
+
|
119
|
+
If your migration crashes, and blocks the rest of your asynchronous migrations but you want to execute them anyway, you can change the code of the migration file and push it again so it passes, or simply remove / update as `state = done` the matching `async_schema_migrations` row.
|
120
|
+
|
121
|
+
The `version` value is always the same as the classic migrations ones.
|
122
|
+
|
123
|
+
## Compatibility
|
124
|
+
|
125
|
+
You can use this library through different adapters
|
126
|
+
|
127
|
+
| Type | Version | Documentation |
|
128
|
+
| ---------------- | ------- | --------------------------------------------- |
|
129
|
+
| **Sidekiq** | 5.2.3 | https://github.com/mperham/sidekiq |
|
130
|
+
| **Delayed::Job** | 4.1.3 | https://github.com/collectiveidea/delayed_job |
|
131
|
+
|
132
|
+
If you use other technologies to setup your workers, please hit me up and I'll create additional adapters for you.
|
133
|
+
|
134
|
+
The gem has been tested and is working with `ActiveRecord 5.2.2`, if you notice abnormal behavior with other versions or want it compatible with earlier versions, please hit me up.
|
135
|
+
|
136
|
+
## Development
|
137
|
+
|
138
|
+
I created this library as my company was struggling in its deployment process. It lacks functionalities but this is a good starting point; everything is easily extendable so don't hesitate to add your own needed methods to it.
|
139
|
+
|
140
|
+
You're more than welcome to open an issue with feature requests so I can work on improving this library.
|
141
|
+
|
142
|
+
## Contributing
|
143
|
+
|
144
|
+
1. Fork it ( https://github.com/[my-github-username]/rails_async_migrations/fork )
|
145
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
146
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
147
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
148
|
+
5. Create a new Pull Request
|
149
|
+
|
150
|
+
## Author
|
151
|
+
|
152
|
+
[Laurent Schaffner](http://www.laurentschaffner.com)
|
153
|
+
|
154
|
+
## Credits
|
155
|
+
|
156
|
+
This project and its idea was inspired by [Kir Shatrov article](https://kirshatrov.com/2018/04/01/async-migrations/) on the matter, it's worth a look!
|
157
|
+
|
158
|
+
## License
|
159
|
+
|
160
|
+
MIT License.
|
161
|
+
|
162
|
+
## Changelog
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "rails_async_migrations"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
|
4
|
+
module RailsAsyncMigrations
|
5
|
+
module Generators
|
6
|
+
class InstallGenerator < ::Rails::Generators::Base
|
7
|
+
include Rails::Generators::Migration
|
8
|
+
source_root File.expand_path('../templates', __FILE__)
|
9
|
+
desc "Add the migrations for AsyncSchemaMigration"
|
10
|
+
|
11
|
+
def self.next_migration_number(path)
|
12
|
+
next_migration_number = current_migration_number(path) + 1
|
13
|
+
ActiveRecord::Migration.next_migration_number(next_migration_number)
|
14
|
+
end
|
15
|
+
|
16
|
+
def copy_migrations
|
17
|
+
migration_template "create_async_schema_migrations.rb",
|
18
|
+
"db/migrate/create_async_schema_migrations.rb"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# configuration of the gem and
|
2
|
+
# default values set here
|
3
|
+
module RailsAsyncMigrations
|
4
|
+
class Config
|
5
|
+
attr_accessor :locked_methods, :mode, :workers
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@locked_methods = %i[change up down]
|
9
|
+
@mode = :quiet # verbose, quiet
|
10
|
+
@workers = :sidekiq # delayed_job, sidekiq
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module RailsAsyncMigrations
|
2
|
+
module Connection
|
3
|
+
class ActiveRecord
|
4
|
+
attr_reader :current_direction
|
5
|
+
|
6
|
+
def initialize(current_direction)
|
7
|
+
@current_direction = current_direction
|
8
|
+
end
|
9
|
+
|
10
|
+
# NOTE : down isn't available
|
11
|
+
# from the public API of the gem
|
12
|
+
def current_version
|
13
|
+
if current_direction == :down
|
14
|
+
migration_context.current_version
|
15
|
+
elsif current_direction == :up
|
16
|
+
pending_migrations.first
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def current_migration
|
21
|
+
@current_migration ||= migration_from current_version
|
22
|
+
end
|
23
|
+
|
24
|
+
def migration_from(version)
|
25
|
+
migration_context.migrations.find do |migration|
|
26
|
+
migration.version.to_s == version.to_s
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def allowed_direction?
|
31
|
+
current_direction == :up
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def pending_migrations
|
37
|
+
achieved_migrations - all_migrations
|
38
|
+
end
|
39
|
+
|
40
|
+
def achieved_migrations
|
41
|
+
migration_context.migrations.collect(&:version)
|
42
|
+
end
|
43
|
+
|
44
|
+
def all_migrations
|
45
|
+
migration_context.get_all_versions
|
46
|
+
end
|
47
|
+
|
48
|
+
def migration_context
|
49
|
+
connection.migration_context
|
50
|
+
end
|
51
|
+
|
52
|
+
# NOTE: seems at it was ActiveRecord::Migrator
|
53
|
+
# in anterior versions
|
54
|
+
def connection
|
55
|
+
@connection || ::ActiveRecord::Base.connection
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|