synchronised_migration 1.0.2.pre.alpha.1 → 2.1.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 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