synchronised_migration 1.0.2.pre.alpha.1 → 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 46137a7221280e53c8a46dffc717f940e94cf02e
4
- data.tar.gz: bfacb7b23898da580e100d53eb1fbc0d7b641cdc
2
+ SHA256:
3
+ metadata.gz: 9881480f34d86d8a177756df3a12e3ec54d1f6b9cfa80d2847084101749bca5a
4
+ data.tar.gz: e79ad9a4ceddcd69a1936789d485c50daa3bbdc95ef74f2052eac8aef08d65b2
5
5
  SHA512:
6
- metadata.gz: ee2658296ac508f679c42af3e18031ff860aba20f07f9a7f89342ea710565ff6afa477d42007f73dbd265090c9724d60ba34907e6636aa3cbb00efe5a787faad
7
- data.tar.gz: fc49da8ca250dd9a5fa0b0769c4c343a60a4cdb95b556bdc7b622b15e4c2df81b2f3286b165c0cdd76ddfd791a3eca3b4b68cbbe89225981694875897dbcf30c
6
+ metadata.gz: b921a8a53758ec6a7654c3decd2b86b2e60baf58336ce31a1eaded4cf7a266ad35e1603c2bc81547b94fc4617f7e5175e5bae7e052013c68004c410d6f410263
7
+ data.tar.gz: 2f934aebd9d2c5978fe368be30e606622861480ed2fd61d1a422c70d33ad8a391b7d19802743c29c3e16cff9e61972bc80051a3a460928d0f195169ce0c8d0de
data/.gitignore CHANGED
@@ -2,3 +2,4 @@
2
2
  /vendor
3
3
  /coverage
4
4
  /*.gem
5
+ Gemfile.lock
@@ -1 +1 @@
1
- 2.2.7
1
+ 2.6.3
@@ -1,3 +1,10 @@
1
- ---
2
1
  language: ruby
3
- script: bundle && bundle exec rspec
2
+ rvm:
3
+ - 2.4
4
+ - 2.5
5
+ - 2.6
6
+ script: bundle exec rspec
7
+ sudo: false
8
+ cache: bundler
9
+ before_install:
10
+ - gem install bundler
@@ -1,5 +1,27 @@
1
1
  # Synchronised Migration
2
2
 
3
+ ## 2.1.2
4
+
5
+ * [TT-7511] Use the correct `ex` option for key value expiry times instead of `ttl`
6
+
7
+ ## 2.1.1
8
+
9
+ * [TT-5896] Early exit from migration inside lock if already completed
10
+
11
+ ## 2.1.0
12
+
13
+ * [TT-5827] Add "success key" when REDLOCK_VERSION_SUFFIX is set, preventing repeat runs
14
+ * [TT-5830] Add rake and Rakefile for 'rake release'
15
+
16
+ ## 2.0.0
17
+
18
+ * [DO-168] Removed requirement for defining RedisConfig, now set on
19
+ SynchronisedMigration module
20
+
21
+ ## 1.0.2
22
+
23
+ * [DO-105] Use the `original` environment instead
24
+
3
25
  ## 1.0.1
4
26
 
5
27
  * [DO-100] Address the Travis issue
data/README.md CHANGED
@@ -15,14 +15,19 @@ Docker](https://github.com/sealink/craft-docker) project.
15
15
 
16
16
  ## Usage
17
17
 
18
- Class `RedisConfig` needs to be provided as follow.
18
+ Module `SynchronisedMigration` needs to be configured as below.
19
19
 
20
20
  ```
21
- RedisConfig.get[:host] # example.com
22
- RedisConfig.get[:port] # 6379
23
- RedisConfig.get[:db] # 0
21
+ SynchronisedMigration.configure do |config|
22
+ config.host = 'example.com'
23
+ config.port = 6379
24
+ config.db = 0
25
+ end
24
26
  ```
25
27
 
28
+ Configuration can be called by using
29
+ ```SynchronisedMigration.redis_config.host``` or similar.
30
+
26
31
  You may override these settings through environment variables.
27
32
 
28
33
  ```
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
@@ -1,4 +1,22 @@
1
1
  module SynchronisedMigration
2
+ class << self
3
+ attr_accessor :redis_config
4
+ end
5
+
6
+ def self.configure
7
+ self.redis_config ||= Configuration.new
8
+ yield(redis_config)
9
+ end
10
+
11
+ class Configuration
12
+ attr_accessor :host, :port, :db
13
+
14
+ def initialize
15
+ @host = ''
16
+ @port = 0
17
+ @db = 0
18
+ end
19
+ end
2
20
  end
3
21
 
4
22
  require 'synchronised_migration/railtie' if defined?(Rails)
@@ -15,11 +15,16 @@ class SynchronisedMigration::Main
15
15
  end
16
16
 
17
17
  def call
18
- lock_and_execute
18
+ done_or_execute
19
19
  end
20
20
 
21
21
  private
22
22
 
23
+ def done_or_execute
24
+ return Result.ok if migration_already_completed?
25
+ lock_and_execute
26
+ end
27
+
23
28
  def lock_and_execute
24
29
  redlock.lock! lock_key, timeout do
25
30
  execute
@@ -27,12 +32,26 @@ class SynchronisedMigration::Main
27
32
  end
28
33
 
29
34
  def execute
30
- return Result.new 'Halting the script because the previous migration failed.' if previous_failed?
35
+ return Result.ok if migration_already_completed?
36
+ return Result.fail 'Halting the script because the previous migration failed.' if previous_failed?
31
37
  mark_failed
32
- migrate
33
- return Result.new 'Migration failed.' if migration_failed?
38
+ migration_success = migrate
39
+ return Result.fail 'Migration failed.' unless migration_success
40
+ mark_successful
34
41
  remove_fail_marker
35
- Result.new
42
+ return Result.ok
43
+ end
44
+
45
+ def migration_already_completed?
46
+ return false if !success_key
47
+ value = redis.get(success_key)
48
+ not value.nil? and not value.empty?
49
+ end
50
+
51
+ def mark_successful
52
+ if success_key
53
+ redis.set success_key, timestamp, ex: 3600*24*30
54
+ end
36
55
  end
37
56
 
38
57
  def previous_failed?
@@ -41,7 +60,7 @@ class SynchronisedMigration::Main
41
60
  end
42
61
 
43
62
  def mark_failed
44
- redis.set fail_key, 1
63
+ redis.set fail_key, timestamp, ex: 3600
45
64
  end
46
65
 
47
66
  def remove_fail_marker
@@ -49,20 +68,20 @@ class SynchronisedMigration::Main
49
68
  end
50
69
 
51
70
  def migrate
52
- return Kernel.system target_command unless with_clean_env?
53
- Bundler.with_original_env do
54
- Kernel.system target_command
71
+ if with_clean_env?
72
+ Bundler.with_original_env do
73
+ Kernel.system target_command
74
+ end
75
+ else
76
+ Kernel.system target_command
55
77
  end
78
+ $?.success?
56
79
  end
57
80
 
58
81
  def with_clean_env?
59
82
  not ENV.fetch('WITH_CLEAN_BUNDLER_ENV', '').empty?
60
83
  end
61
84
 
62
- def migration_failed?
63
- not $?.success?
64
- end
65
-
66
85
  def target_command
67
86
  ENV.fetch 'SYNCHRONISED_COMMAND', 'bin/launch/migrate'
68
87
  end
@@ -83,18 +102,22 @@ class SynchronisedMigration::Main
83
102
  def redis_url
84
103
  sprintf(
85
104
  'redis://%s:%s/%s',
86
- RedisConfig.get[:host],
87
- RedisConfig.get[:port],
88
- RedisConfig.get[:db]
105
+ SynchronisedMigration.redis_config.host,
106
+ SynchronisedMigration.redis_config.port,
107
+ SynchronisedMigration.redis_config.db
89
108
  )
90
109
  end
91
110
 
111
+ def timestamp
112
+ Time.now.to_i
113
+ end
114
+
92
115
  def timeout
93
116
  ENV.fetch('REDLOCK_TIMEOUT_MS', 3_600_000).to_i
94
117
  end
95
118
 
96
119
  def retry_delay
97
- ENV.fetch('REDLOCK_RETRY_DELAY_MS', 200).to_i
120
+ ENV.fetch('REDLOCK_RETRY_DELAY_MS', 3000).to_i
98
121
  end
99
122
 
100
123
  def retry_count
@@ -106,6 +129,16 @@ class SynchronisedMigration::Main
106
129
  end
107
130
 
108
131
  def fail_key
109
- ENV.fetch 'REDLOCK_FAIL_KEY', 'migration-failed'
132
+ ENV.fetch 'REDLOCK_FAIL_KEY', 'migration-failed' + version_suffix
133
+ end
134
+
135
+ def success_key
136
+ return false if version_suffix.empty?
137
+ 'migration-success' + version_suffix
138
+ end
139
+
140
+ def version_suffix
141
+ suffix = ENV.fetch 'REDLOCK_VERSION_SUFFIX', false
142
+ suffix ? '-' + suffix : ''
110
143
  end
111
144
  end
@@ -10,4 +10,12 @@ class SynchronisedMigration::Result
10
10
  def success?
11
11
  error.nil?
12
12
  end
13
+
14
+ def self.ok
15
+ self.new
16
+ end
17
+
18
+ def self.fail(error)
19
+ self.new error
20
+ end
13
21
  end
@@ -1,3 +1,3 @@
1
1
  module SynchronisedMigration
2
- VERSION = '1.0.2-alpha.1'
2
+ VERSION = '2.1.2'
3
3
  end
@@ -9,19 +9,43 @@ describe SynchronisedMigration::Main do
9
9
  let(:redis) { double }
10
10
  let(:redlock) { double }
11
11
  let(:fail_marker_value) { nil }
12
+ let(:success_marker_value) { nil }
13
+ let(:version_suffix) { 'bork' }
14
+ let(:set_version_suffix) { ENV['REDLOCK_VERSION_SUFFIX'] = version_suffix }
15
+ let(:time_value) { double(to_i: 123456789) }
12
16
 
13
17
  before do
18
+ set_version_suffix
19
+
14
20
  subject.instance.instance_variable_set :@redis, nil
15
21
  subject.instance.instance_variable_set :@redlock, nil
16
22
 
23
+ allow(subject.instance).to receive(:execute).and_call_original
24
+ allow(subject.instance).to receive(:migrate).and_call_original
25
+
17
26
  allow(Redis).to receive(:new).and_return(redis)
18
- allow(redis).to receive(:get).and_return(fail_marker_value)
27
+ allow(redis).to receive(:get) { |key|
28
+ case key
29
+ when "migration-failed-#{version_suffix}"
30
+ fail_marker_value
31
+ when "migration-failed"
32
+ fail_marker_value
33
+ when "migration-success-#{version_suffix}"
34
+ success_marker_value
35
+ when "migration-success"
36
+ success_marker_value
37
+ else
38
+ raise "invalid key for redis get: #{key}"
39
+ end
40
+ }
19
41
  allow(redis).to receive(:set)
20
42
  allow(redis).to receive(:del)
21
43
 
22
44
  allow(Redlock::Client).to receive(:new).and_return(redlock)
23
45
  allow(redlock).to receive(:lock!) { |lock_key, timeout, &block| block.call }
24
46
 
47
+ allow(Time).to receive(:now).and_return(time_value)
48
+
25
49
  allow(Kernel).to receive(:system).and_wrap_original { |method, *args|
26
50
  next if args == [ 'bin/launch/migrate' ]
27
51
  method.call *args
@@ -29,26 +53,49 @@ describe SynchronisedMigration::Main do
29
53
 
30
54
  allow(Bundler).to receive(:with_original_env).and_call_original
31
55
 
32
- stub_const(
33
- 'RedisConfig', double(
34
- get: {
35
- host: 'example.com',
36
- port: 6379,
37
- db: 0
38
- }
39
- )
40
- )
56
+ SynchronisedMigration.configure do |config|
57
+ config.host = 'example.com'
58
+ config.port = 6379
59
+ config.db = 0
60
+ end
41
61
  end
42
62
 
43
63
  context 'in the happy path' do
44
64
  it 'executes the migration successfully' do
45
65
  expect(result).to be_success
46
66
  expect(redlock).to have_received(:lock!)
47
- expect(redis).to have_received(:get).with('migration-failed')
48
- expect(redis).to have_received(:set).with('migration-failed', 1)
67
+ expect(redis).to have_received(:get).with('migration-failed-bork')
68
+ expect(redis).to have_received(:set).with('migration-failed-bork', 123456789, ex: 3600)
69
+ expect(redis).to have_received(:set).with('migration-success-bork', 123456789, ex: 3600*24*30)
49
70
  expect(Kernel).to have_received(:system)
50
71
  expect(Bundler).not_to have_received(:with_original_env)
51
- expect(redis).to have_received(:del).with('migration-failed')
72
+ expect(redis).to have_received(:del).with('migration-failed-bork')
73
+ end
74
+
75
+ context 'and migration completed previously' do
76
+ let(:success_marker_value) { '1' }
77
+ it 'contines without executing' do
78
+ expect(result).to be_success
79
+ expect(redlock).not_to have_received(:lock!)
80
+ end
81
+ end
82
+
83
+ context 'executing in lock waiter' do
84
+ let(:result2) { subject.call }
85
+
86
+ before do
87
+ # Note: bypasses the first success flag check so it can enter lock.
88
+ expect(result).to be_success
89
+ allow(redis).to receive(:get).and_return(nil, '1')
90
+ end
91
+
92
+ it 'early exits and does not execute again', :aggregate_failures do
93
+ expect(result2).to be_success
94
+ expect(redlock).to have_received(:lock!).exactly(2).times
95
+ expect(Kernel).to have_received(:system).exactly(1).times
96
+ expect(subject.instance).to have_received(:execute).exactly(2).times
97
+ expect(subject.instance).to have_received(:migrate).exactly(1).times
98
+ end
52
99
  end
53
100
  end
54
101
 
@@ -81,9 +128,37 @@ describe SynchronisedMigration::Main do
81
128
 
82
129
  it 'marks the failure in Redis' do
83
130
  expect(result).not_to be_success
84
- expect(redis).to have_received(:set).with('migration-failed', 1)
131
+ expect(redis).to have_received(:set).with('migration-failed-bork', 123456789, ex: 3600)
85
132
  expect(redis).not_to have_received(:del)
86
133
  end
87
134
  end
135
+
136
+ context 'without version suffix' do
137
+ let(:set_version_suffix) { ENV.delete 'REDLOCK_VERSION_SUFFIX' }
138
+
139
+ context 'in the happy path' do
140
+ it 'executes the migration successfully' do
141
+ expect(result).to be_success
142
+ expect(redlock).to have_received(:lock!)
143
+ expect(redis).to have_received(:get).with('migration-failed')
144
+ expect(redis).to have_received(:set).with('migration-failed', 123456789, ex: 3600)
145
+ expect(Kernel).to have_received(:system)
146
+ expect(Bundler).not_to have_received(:with_original_env)
147
+ expect(redis).to have_received(:del).with('migration-failed')
148
+ end
149
+ end
150
+
151
+ context 'when the task crashed' do
152
+ before do
153
+ allow_any_instance_of(Process::Status).to receive(:success?).and_return(false)
154
+ end
155
+
156
+ it 'marks the failure in Redis' do
157
+ expect(result).not_to be_success
158
+ expect(redis).to have_received(:set).with('migration-failed', 123456789, ex: 3600)
159
+ expect(redis).not_to have_received(:del)
160
+ end
161
+ end
162
+ end
88
163
  end
89
164
  end
@@ -5,7 +5,7 @@ require 'synchronised_migration/version'
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = 'synchronised_migration'
7
7
  spec.version = SynchronisedMigration::VERSION
8
- spec.authors = ['Alvin Yim']
8
+ spec.authors = ['Alvin Yim', 'Stefan Cooper']
9
9
  spec.email = 'support@travellink.com.au'
10
10
  spec.description = 'Use Redis to record the data migration status'
11
11
  spec.summary = 'For deploying to multiple instances simultaneously'
@@ -16,8 +16,10 @@ Gem::Specification.new do |spec|
16
16
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
17
  spec.require_paths = ['lib']
18
18
 
19
- spec.add_dependency 'redlock', '~> 0.2'
20
- spec.add_development_dependency 'simplecov-rcov', '~> 0.2'
21
- spec.add_development_dependency 'rspec', '~> 3.6'
22
- spec.add_development_dependency 'pry-byebug', '~> 3.5'
19
+ spec.add_dependency 'redlock', '>= 0.2'
20
+ spec.add_dependency 'redis'
21
+ spec.add_development_dependency 'rake'
22
+ spec.add_development_dependency 'simplecov-rcov', '>= 0.2'
23
+ spec.add_development_dependency 'rspec', '>= 3.6'
24
+ spec.add_development_dependency 'pry-byebug', '>= 3.5'
23
25
  end
metadata CHANGED
@@ -1,69 +1,98 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: synchronised_migration
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2.pre.alpha.1
4
+ version: 2.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alvin Yim
8
- autorequire:
8
+ - Stefan Cooper
9
+ autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2017-11-04 00:00:00.000000000 Z
12
+ date: 2020-06-25 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: redlock
15
16
  requirement: !ruby/object:Gem::Requirement
16
17
  requirements:
17
- - - "~>"
18
+ - - ">="
18
19
  - !ruby/object:Gem::Version
19
20
  version: '0.2'
20
21
  type: :runtime
21
22
  prerelease: false
22
23
  version_requirements: !ruby/object:Gem::Requirement
23
24
  requirements:
24
- - - "~>"
25
+ - - ">="
25
26
  - !ruby/object:Gem::Version
26
27
  version: '0.2'
28
+ - !ruby/object:Gem::Dependency
29
+ name: redis
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rake
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
27
56
  - !ruby/object:Gem::Dependency
28
57
  name: simplecov-rcov
29
58
  requirement: !ruby/object:Gem::Requirement
30
59
  requirements:
31
- - - "~>"
60
+ - - ">="
32
61
  - !ruby/object:Gem::Version
33
62
  version: '0.2'
34
63
  type: :development
35
64
  prerelease: false
36
65
  version_requirements: !ruby/object:Gem::Requirement
37
66
  requirements:
38
- - - "~>"
67
+ - - ">="
39
68
  - !ruby/object:Gem::Version
40
69
  version: '0.2'
41
70
  - !ruby/object:Gem::Dependency
42
71
  name: rspec
43
72
  requirement: !ruby/object:Gem::Requirement
44
73
  requirements:
45
- - - "~>"
74
+ - - ">="
46
75
  - !ruby/object:Gem::Version
47
76
  version: '3.6'
48
77
  type: :development
49
78
  prerelease: false
50
79
  version_requirements: !ruby/object:Gem::Requirement
51
80
  requirements:
52
- - - "~>"
81
+ - - ">="
53
82
  - !ruby/object:Gem::Version
54
83
  version: '3.6'
55
84
  - !ruby/object:Gem::Dependency
56
85
  name: pry-byebug
57
86
  requirement: !ruby/object:Gem::Requirement
58
87
  requirements:
59
- - - "~>"
88
+ - - ">="
60
89
  - !ruby/object:Gem::Version
61
90
  version: '3.5'
62
91
  type: :development
63
92
  prerelease: false
64
93
  version_requirements: !ruby/object:Gem::Requirement
65
94
  requirements:
66
- - - "~>"
95
+ - - ">="
67
96
  - !ruby/object:Gem::Version
68
97
  version: '3.5'
69
98
  description: Use Redis to record the data migration status
@@ -77,9 +106,9 @@ files:
77
106
  - ".travis.yml"
78
107
  - CHANGELOG.md
79
108
  - Gemfile
80
- - Gemfile.lock
81
109
  - LICENSE
82
110
  - README.md
111
+ - Rakefile
83
112
  - lib/synchronised_migration.rb
84
113
  - lib/synchronised_migration/main.rb
85
114
  - lib/synchronised_migration/railtie.rb
@@ -92,7 +121,7 @@ files:
92
121
  homepage: https://github.com/sealink/synchronised-migration-rb
93
122
  licenses: []
94
123
  metadata: {}
95
- post_install_message:
124
+ post_install_message:
96
125
  rdoc_options: []
97
126
  require_paths:
98
127
  - lib
@@ -103,13 +132,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
103
132
  version: '0'
104
133
  required_rubygems_version: !ruby/object:Gem::Requirement
105
134
  requirements:
106
- - - ">"
135
+ - - ">="
107
136
  - !ruby/object:Gem::Version
108
- version: 1.3.1
137
+ version: '0'
109
138
  requirements: []
110
- rubyforge_project:
111
- rubygems_version: 2.4.5.2
112
- signing_key:
139
+ rubygems_version: 3.0.3
140
+ signing_key:
113
141
  specification_version: 4
114
142
  summary: For deploying to multiple instances simultaneously
115
143
  test_files:
@@ -1,58 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- synchronised_migration (1.0.1)
5
- redlock (~> 0.2)
6
-
7
- GEM
8
- remote: https://rubygems.org/
9
- specs:
10
- byebug (9.1.0)
11
- coderay (1.1.2)
12
- diff-lcs (1.3)
13
- docile (1.1.5)
14
- json (2.1.0)
15
- method_source (0.8.2)
16
- pry (0.10.4)
17
- coderay (~> 1.1.0)
18
- method_source (~> 0.8.1)
19
- slop (~> 3.4)
20
- pry-byebug (3.5.0)
21
- byebug (~> 9.1)
22
- pry (~> 0.10)
23
- redis (3.3.5)
24
- redlock (0.2.0)
25
- redis (~> 3, >= 3.0.0)
26
- rspec (3.6.0)
27
- rspec-core (~> 3.6.0)
28
- rspec-expectations (~> 3.6.0)
29
- rspec-mocks (~> 3.6.0)
30
- rspec-core (3.6.0)
31
- rspec-support (~> 3.6.0)
32
- rspec-expectations (3.6.0)
33
- diff-lcs (>= 1.2.0, < 2.0)
34
- rspec-support (~> 3.6.0)
35
- rspec-mocks (3.6.0)
36
- diff-lcs (>= 1.2.0, < 2.0)
37
- rspec-support (~> 3.6.0)
38
- rspec-support (3.6.0)
39
- simplecov (0.15.0)
40
- docile (~> 1.1.0)
41
- json (>= 1.8, < 3)
42
- simplecov-html (~> 0.10.0)
43
- simplecov-html (0.10.2)
44
- simplecov-rcov (0.2.3)
45
- simplecov (>= 0.4.1)
46
- slop (3.6.0)
47
-
48
- PLATFORMS
49
- ruby
50
-
51
- DEPENDENCIES
52
- pry-byebug (~> 3.5)
53
- rspec (~> 3.6)
54
- simplecov-rcov (~> 0.2)
55
- synchronised_migration!
56
-
57
- BUNDLED WITH
58
- 1.16.0