redlock 2.0.1 → 2.0.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
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.