redlock 2.0.1 → 2.0.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
2
  SHA256:
3
- metadata.gz: 13a2ef1c0096e1135a1996719fbeb679234b639e0debecba4fa6d72f3fcd174a
4
- data.tar.gz: e8e247b8be0ded9272f04addfbcbc6b99c1c6ad4429f344c74d61101af7e1682
3
+ metadata.gz: 8bdda4faebf87c2d1fb0026a3142497d5ae14092340bce6e35e46f51635e5c6a
4
+ data.tar.gz: a9f5636898a2a3ece98b95a286bd88f417e68d119b1f1ad60bd75b5d1da94baf
5
5
  SHA512:
6
- metadata.gz: 81bf9233769810cc1a3f08a6927dc5ffddfa5c31ed158071daf1ad2ffc8303060094dbd78aa4af4185148b49878e36fcf54483a41378277e3bb306dd45c483e6
7
- data.tar.gz: b0f198c408d7281c8d1d45ef57ce612a964571bf728969601ecfd448f6e482c83567cef66d94fab6bcf722909bd19ef3120ad2ae56171dc83fae815ad81111a9
6
+ metadata.gz: 0b10ec10f00ce9282d604b1d0c8a2e845d8d95a6684a5a235d6d39b598efa6324725c4d088e8016b11d473b0195a1e29a451e79fad115f04f6b217493be297cf
7
+ data.tar.gz: e1d54bb8bb8a108e0989d1c11baa44cbfc4c971029fb23819d000758b42739698342ef803a826990757c888d113fb8a49663df1f3812603965c5498361596f86
data/CHANGELOG.md ADDED
@@ -0,0 +1,40 @@
1
+
2
+ # Change Log
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](http://keepachangelog.com/)
6
+ and this project adheres to [Semantic Versioning](http://semver.org/).
7
+
8
+ ## [Unreleased] - yyyy-mm-dd
9
+
10
+ Here we write upgrading notes for brands. It's a team effort to make them as
11
+ straightforward as possible.
12
+
13
+ ### Added
14
+
15
+ ### Changed
16
+
17
+ ### Fixed
18
+
19
+
20
+ ## [2.0.1] - 2023-02-14
21
+
22
+ ### Added
23
+
24
+ ### Changed
25
+
26
+ ### Fixed
27
+
28
+ * always treat redis instance as pool-like #125
29
+
30
+ ## [2.0.0] - 2023-02-09
31
+
32
+ ### Added
33
+
34
+ * support for redis >= 6.0
35
+
36
+ ### Changed
37
+
38
+ * **BREAKING**: The library now only works with `RedisClient` instance.
39
+
40
+ ### Fixed
data/README.md CHANGED
@@ -15,7 +15,8 @@ This is an implementation of a proposed [distributed lock algorithm with Redis](
15
15
 
16
16
  ## Compatibility
17
17
 
18
- Redlock works with Redis versions 6.0 or later.
18
+ * It works with Redis server versions 6.0 or later.
19
+ * Redlock >= 2.0 only works with [`RedisClient`](https://github.com/redis-rb/redis-client) client instance.
19
20
 
20
21
  ## Installation
21
22
 
@@ -4,6 +4,15 @@ require 'securerandom'
4
4
  module Redlock
5
5
  include Scripts
6
6
 
7
+ class LockAcquisitionError < StandardError
8
+ attr_reader :errors
9
+
10
+ def initialize(message, errors)
11
+ super(message)
12
+ @errors = errors
13
+ end
14
+ end
15
+
7
16
  class Client
8
17
  DEFAULT_REDIS_HOST = ENV["DEFAULT_REDIS_HOST"] || "localhost"
9
18
  DEFAULT_REDIS_PORT = ENV["DEFAULT_REDIS_PORT"] || "6379"
@@ -232,6 +241,7 @@ module Redlock
232
241
  def try_lock_instances(resource, ttl, options)
233
242
  retry_count = options[:retry_count] || @retry_count
234
243
  tries = options[:extend] ? 1 : (retry_count + 1)
244
+ last_error = nil
235
245
 
236
246
  tries.times do |attempt_number|
237
247
  # Wait a random delay before retrying.
@@ -239,8 +249,12 @@ module Redlock
239
249
 
240
250
  lock_info = lock_instances(resource, ttl, options)
241
251
  return lock_info if lock_info
252
+ rescue => e
253
+ last_error = e
242
254
  end
243
255
 
256
+ raise last_error if last_error
257
+
244
258
  false
245
259
  end
246
260
 
@@ -261,9 +275,15 @@ module Redlock
261
275
  def lock_instances(resource, ttl, options)
262
276
  value = (options[:extend] || { value: SecureRandom.uuid })[:value]
263
277
  allow_new_lock = options[:extend_only_if_locked] ? 'no' : 'yes'
278
+ errors = []
264
279
 
265
280
  locked, time_elapsed = timed do
266
- @servers.select { |s| s.lock resource, value, ttl, allow_new_lock }.size
281
+ @servers.count do |s|
282
+ s.lock(resource, value, ttl, allow_new_lock)
283
+ rescue => e
284
+ errors << e
285
+ false
286
+ end
267
287
  end
268
288
 
269
289
  validity = ttl - time_elapsed - drift(ttl)
@@ -272,6 +292,11 @@ module Redlock
272
292
  { validity: validity, resource: resource, value: value }
273
293
  else
274
294
  @servers.each { |s| s.unlock(resource, value) }
295
+
296
+ if errors.size >= @quorum
297
+ raise LockAcquisitionError.new('Too many Redis errors prevented lock acquisition', errors)
298
+ end
299
+
275
300
  false
276
301
  end
277
302
  end
@@ -1,3 +1,3 @@
1
1
  module Redlock
2
- VERSION = '2.0.1'
2
+ VERSION = '2.0.2'
3
3
  end
data/redlock.gemspec CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ['lib']
20
20
 
21
- spec.add_dependency 'redis-client'
21
+ spec.add_dependency 'redis-client', '~> 0.14.1'
22
22
 
23
23
  spec.add_development_dependency 'connection_pool', '~> 2.2'
24
24
  spec.add_development_dependency 'coveralls', '~> 0.8'
data/spec/client_spec.rb CHANGED
@@ -76,6 +76,38 @@ RSpec.describe Redlock::Client do
76
76
  context 'when lock is available' do
77
77
  after(:each) { lock_manager.unlock(@lock_info) if @lock_info }
78
78
 
79
+ context 'when redis connection error occurs' do
80
+ let(:servers_with_quorum) {
81
+ [
82
+ "redis://#{redis1_host}:#{redis1_port}",
83
+ "redis://#{redis2_host}:#{redis2_port}",
84
+ unreachable_redis
85
+ ]
86
+ }
87
+
88
+ let(:servers_without_quorum) {
89
+ [
90
+ "redis://#{redis1_host}:#{redis1_port}",
91
+ unreachable_redis,
92
+ unreachable_redis
93
+ ]
94
+ }
95
+
96
+ it 'locks if majority of redis instances are available' do
97
+ redlock = Redlock::Client.new(servers_with_quorum)
98
+
99
+ expect(redlock.lock(resource_key, ttl)).to be_truthy
100
+ end
101
+
102
+ it 'fails to acquire a lock if majority of Redis instances are not available' do
103
+ redlock = Redlock::Client.new(servers_without_quorum)
104
+
105
+ expect {
106
+ redlock.lock(resource_key, ttl)
107
+ }.to raise_error(Redlock::LockAcquisitionError)
108
+ end
109
+ end
110
+
79
111
  it 'locks' do
80
112
  @lock_info = lock_manager.lock(resource_key, ttl)
81
113
 
@@ -266,7 +298,10 @@ RSpec.describe Redlock::Client do
266
298
 
267
299
  expect {
268
300
  lock_manager.lock(resource_key, ttl)
269
- }.to raise_error(RedisClient::CannotConnectError)
301
+ }.to raise_error(Redlock::LockAcquisitionError) do |e|
302
+ expect(e.errors[0]).to be_a(RedisClient::CannotConnectError)
303
+ expect(e.errors.count).to eq 1
304
+ end
270
305
  end
271
306
  end
272
307
 
@@ -278,7 +313,10 @@ RSpec.describe Redlock::Client do
278
313
  redis_instance.instance_variable_set(:@redis, unreachable_redis)
279
314
  expect {
280
315
  lock_manager.lock(resource_key, ttl)
281
- }.to raise_error(RedisClient::CannotConnectError)
316
+ }.to raise_error(Redlock::LockAcquisitionError) do |e|
317
+ expect(e.errors[0]).to be_a(RedisClient::CannotConnectError)
318
+ expect(e.errors.count).to eq 1
319
+ end
282
320
  redis_instance.instance_variable_set(:@redis, old_redis)
283
321
  expect(lock_manager.lock(resource_key, ttl)).to be_truthy
284
322
  end
@@ -308,11 +346,15 @@ RSpec.describe Redlock::Client do
308
346
  # This time we do not pass it through to Redis, in order to simulate a passing
309
347
  # call to LOAD SCRIPT followed by another NOSCRIPT error. Imagine someone
310
348
  # repeatedly calling SCRIPT FLUSH on our Redis instance.
311
- expect(@manipulated_instance).to receive(:load_scripts)
349
+ expect(@manipulated_instance).to receive(:load_scripts).exactly(8).times
312
350
 
313
351
  expect {
314
352
  lock_manager.lock(resource_key, ttl)
315
- }.to raise_error(/NOSCRIPT/)
353
+ }.to raise_error(Redlock::LockAcquisitionError) do |e|
354
+ expect(e.errors[0]).to be_a(RedisClient::CommandError)
355
+ expect(e.errors[0].message).to match(/NOSCRIPT/)
356
+ expect(e.errors.count).to eq 1
357
+ end
316
358
  end
317
359
  end
318
360
 
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redlock
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Leandro Moreira
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-14 00:00:00.000000000 Z
11
+ date: 2023-06-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis-client
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 0.14.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 0.14.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: connection_pool
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +122,7 @@ files:
122
122
  - ".github/workflows/ci.yml"
123
123
  - ".gitignore"
124
124
  - ".rspec"
125
+ - CHANGELOG.md
125
126
  - CONTRIBUTORS
126
127
  - Gemfile
127
128
  - LICENSE
@@ -158,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
158
159
  - !ruby/object:Gem::Version
159
160
  version: '0'
160
161
  requirements: []
161
- rubygems_version: 3.2.3
162
+ rubygems_version: 3.3.7
162
163
  signing_key:
163
164
  specification_version: 4
164
165
  summary: Distributed lock using Redis written in Ruby.