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 +4 -4
- data/CHANGELOG.md +40 -0
- data/README.md +2 -1
- data/lib/redlock/client.rb +26 -1
- data/lib/redlock/version.rb +1 -1
- data/redlock.gemspec +1 -1
- data/spec/client_spec.rb +46 -4
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8bdda4faebf87c2d1fb0026a3142497d5ae14092340bce6e35e46f51635e5c6a
|
4
|
+
data.tar.gz: a9f5636898a2a3ece98b95a286bd88f417e68d119b1f1ad60bd75b5d1da94baf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
|
data/lib/redlock/client.rb
CHANGED
@@ -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.
|
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
|
data/lib/redlock/version.rb
CHANGED
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(
|
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(
|
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(
|
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.
|
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-
|
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:
|
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:
|
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.
|
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.
|