redis_queued_locks 0.0.5 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -1
- data/README.md +41 -18
- data/lib/redis_queued_locks/acquier/release.rb +1 -1
- data/lib/redis_queued_locks/acquier.rb +17 -9
- data/lib/redis_queued_locks/version.rb +2 -2
- data/redis_queued_locks.gemspec +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 56120a88eb6ffa4f377c014b8619cdcd227ac0efaab1c6ec6495042c74739a63
|
4
|
+
data.tar.gz: d1e7240b5ca0c8a18842cd609b1e9280813a0a51a62d2ed9bcacdb66d707801f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3df6ebf409fa4d97ebe51632832d6c9c64eb03a4102d68e6951def7a3d351c62e86e63014b8cc1d5c6ea4c5cd6bf9bdf5595ebdb2a3334550e7a27f2f6b0bcd
|
7
|
+
data.tar.gz: 6e512298fb8be556e5370574e87529b936670bec1b13250233c6ce08d9feb4c8032b1b07e90a514ff4d4687e01391424be5b546a124119c7937492baa7ef87ee
|
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,18 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.0.7] - 2024-02-27
|
4
|
+
### Changed
|
5
|
+
- Minor documentation updates;
|
6
|
+
|
7
|
+
## [0.0.6] - 2024-02-27
|
8
|
+
### Changed
|
9
|
+
- Major documentation updates;
|
10
|
+
- `RedisQueuedLock#release_lock!` now returns detaield semantic result;
|
11
|
+
- `RediSQueuedLock#release_all_locks!` now returns detailed semantic result;
|
12
|
+
|
3
13
|
## [0.0.5] - 2024-02-26
|
4
|
-
|
14
|
+
### Changed
|
15
|
+
- Minor gem update with documentation and configuration updates inside.
|
5
16
|
|
6
17
|
## [0.0.4] - 2024-02-26
|
7
18
|
### Changed
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# RedisQueuedLocks
|
2
2
|
|
3
|
-
Distributed
|
3
|
+
Distributed locks with "lock acquisition queue" capabilities based on the Redis Database.
|
4
|
+
|
4
5
|
Each lock request is put into a request queue and processed in order of their priority (FIFO).
|
5
6
|
|
6
7
|
---
|
@@ -18,7 +19,7 @@ Each lock request is put into a request queue and processed in order of their pr
|
|
18
19
|
- [clear_locks](#clear_locks---release-all-locks-and-lock-queues)
|
19
20
|
- [Instrumentation](#instrumentation)
|
20
21
|
- [Instrumentation Events](#instrumentation-events)
|
21
|
-
- [
|
22
|
+
- [Todo](#todo)
|
22
23
|
- [Contributing](#contributing)
|
23
24
|
- [License](#license)
|
24
25
|
- [Authors](#authors)
|
@@ -65,6 +66,7 @@ rq_lock_client = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
65
66
|
end
|
66
67
|
|
67
68
|
# Step 3: start to work with locks :)
|
69
|
+
rq_lock_client.lock("some-lock") { puts "Hello, lock!" }
|
68
70
|
```
|
69
71
|
|
70
72
|
---
|
@@ -127,7 +129,7 @@ end
|
|
127
129
|
|
128
130
|
---
|
129
131
|
|
130
|
-
#### lock - obtain a lock
|
132
|
+
#### #lock - obtain a lock
|
131
133
|
|
132
134
|
```ruby
|
133
135
|
def lock(
|
@@ -166,9 +168,11 @@ def lock(
|
|
166
168
|
- `instrumenter` - `[#notify]`
|
167
169
|
- See RedisQueuedLocks::Instrument::ActiveSupport for example.
|
168
170
|
- `raise_errors` - `[Boolean]`
|
169
|
-
- Raise errors on library-related limits such as timeout or
|
171
|
+
- Raise errors on library-related limits such as timeout or retry count limit.
|
170
172
|
- `block` - `[Block]`
|
171
173
|
- A block of code that should be executed after the successfully acquired lock.
|
174
|
+
- If block is **passed** the obtained lock will be released after the block execution or it's ttl (what will happen first);
|
175
|
+
- If block is **not passed** the obtained lock will be released after it's ttl;
|
172
176
|
|
173
177
|
Return value:
|
174
178
|
- `[Hash<Symbol,Any>]` Format: `{ ok: true/false, result: Symbol/Hash }`;
|
@@ -186,20 +190,18 @@ Return value:
|
|
186
190
|
```
|
187
191
|
- for failed lock obtaining:
|
188
192
|
```ruby
|
189
|
-
{ ok: false, result: :
|
190
|
-
{ ok: false, result: :
|
191
|
-
{ ok: false, result: :lock_is_acquired_during_acquire_race }
|
193
|
+
{ ok: false, result: :timeout_reached }
|
194
|
+
{ ok: false, result: :retry_count_reached }
|
192
195
|
{ ok: false, result: :unknown }
|
193
196
|
```
|
194
197
|
|
195
|
-
|
196
198
|
---
|
197
199
|
|
198
200
|
#### #lock! - exceptional lock obtaining
|
199
201
|
|
200
|
-
- fails when:
|
201
|
-
- `timeout` limit reached before lock is obtained;
|
202
|
-
- `retry_count` limit reached before lock is obtained;
|
202
|
+
- fails when (and with):
|
203
|
+
- (`RedisQueuedLocks::LockAcquiermentTimeoutError`) `timeout` limit reached before lock is obtained;
|
204
|
+
- (`RedisQueuedLocks::LockAcquiermentRetryLimitError`) `retry_count` limit reached before lock is obtained;
|
203
205
|
|
204
206
|
```ruby
|
205
207
|
def lock!(
|
@@ -233,7 +235,18 @@ def unlock(lock_name)
|
|
233
235
|
- the lock name that should be released.
|
234
236
|
|
235
237
|
Return:
|
236
|
-
- `[Hash<Symbol,Any>]` - Format: `{ ok: true/false, result: Symbol
|
238
|
+
- `[Hash<Symbol,Any>]` - Format: `{ ok: true/false, result: Hash<Symbol,Numeric|String> }`;
|
239
|
+
|
240
|
+
```ruby
|
241
|
+
{
|
242
|
+
ok: true,
|
243
|
+
result: {
|
244
|
+
rel_time: 0.02, # time spent to lock release (in seconds)
|
245
|
+
rel_key: "rql:lock:your_lock_name", # released lock key
|
246
|
+
rel_queue: "rql:lock_queue:your_lock_name" # released lock key queue
|
247
|
+
}
|
248
|
+
}
|
249
|
+
```
|
237
250
|
|
238
251
|
---
|
239
252
|
|
@@ -250,15 +263,25 @@ def clear_locks(batch_size: config[:lock_release_batch_size])
|
|
250
263
|
- batch of cleared locks and lock queus unde the one pipelined redis command;
|
251
264
|
|
252
265
|
Return:
|
253
|
-
- `[Hash<Symbol,Any>]` - Format: `{ ok: true/false, result: Symbol
|
266
|
+
- `[Hash<Symbol,Any>]` - Format: `{ ok: true/false, result: Hash<Symbol,Numeric> }`;
|
267
|
+
|
268
|
+
```ruby
|
269
|
+
{
|
270
|
+
ok: true,
|
271
|
+
result: {
|
272
|
+
rel_time: 3.07, # time spent to release all locks and related lock queues
|
273
|
+
rel_key_cnt: 100_500 # released redis keys (released locks + released lock queues)
|
274
|
+
}
|
275
|
+
}
|
276
|
+
```
|
254
277
|
|
255
278
|
---
|
256
279
|
|
257
280
|
## Instrumentation
|
258
281
|
|
259
|
-
An instrumentation layer is incapsulated in `instrumenter` object stored in
|
282
|
+
An instrumentation layer is incapsulated in `instrumenter` object stored in [config](#configuration) (`RedisQueuedLocks::Client#config[:instrumenter]`).
|
260
283
|
|
261
|
-
Instrumenter object should provide `notify(event, payload)` method with following signarue:
|
284
|
+
Instrumenter object should provide `notify(event, payload)` method with the following signarue:
|
262
285
|
|
263
286
|
- `event` - `string`;
|
264
287
|
- `payload` - `hash<Symbol,Any>`;
|
@@ -266,7 +289,7 @@ Instrumenter object should provide `notify(event, payload)` method with followin
|
|
266
289
|
`redis_queued_locks` provides two instrumenters:
|
267
290
|
|
268
291
|
- `RedisQueuedLocks::Instrument::ActiveSupport` - `ActiveSupport::Notifications` instrumenter
|
269
|
-
that instrument events via `ActiveSupport::Notifications`
|
292
|
+
that instrument events via `ActiveSupport::Notifications` API;
|
270
293
|
- `RedisQueuedLocks::Instrument::VoidNotifier` - instrumenter that does nothing;
|
271
294
|
|
272
295
|
By default `RedisQueuedLocks::Client` is configured with the void notifier (which means "instrumentation is disabled").
|
@@ -318,11 +341,11 @@ Detalized event semantics and payload structure:
|
|
318
341
|
|
319
342
|
---
|
320
343
|
|
321
|
-
##
|
344
|
+
## Todo
|
322
345
|
|
323
346
|
- `RedisQueuedLocks::Acquier::Try.try_to_lock` - detailed successful result analization;
|
324
347
|
- `100%` test coverage;
|
325
|
-
- better code stylization and interesting refactorings
|
348
|
+
- better code stylization and interesting refactorings;
|
326
349
|
|
327
350
|
---
|
328
351
|
|
@@ -38,7 +38,7 @@ module RedisQueuedLocks::Acquier::Release
|
|
38
38
|
count: batch_size
|
39
39
|
) do |lock_queue|
|
40
40
|
pipeline.call('ZREMRANGEBYSCORE', lock_queue, '-inf', '+inf')
|
41
|
-
pipeline.call('EXPIRE', RedisQueuedLocks::Resource.lock_key_from_queue(lock_queue),
|
41
|
+
pipeline.call('EXPIRE', RedisQueuedLocks::Resource.lock_key_from_queue(lock_queue), '0')
|
42
42
|
end
|
43
43
|
|
44
44
|
# Step B: release all locks
|
@@ -60,7 +60,7 @@ module RedisQueuedLocks::Acquier
|
|
60
60
|
#
|
61
61
|
# @api private
|
62
62
|
# @since 0.1.0
|
63
|
-
# rubocop:disable Metrics/MethodLength
|
63
|
+
# rubocop:disable Metrics/MethodLength, Metrics/BlockNesting
|
64
64
|
def acquire_lock!(
|
65
65
|
redis,
|
66
66
|
lock_name,
|
@@ -147,6 +147,7 @@ module RedisQueuedLocks::Acquier
|
|
147
147
|
if retry_count != nil && acq_process[:tries] >= retry_count
|
148
148
|
# NOTE: reached the retry limit => quit from the loop
|
149
149
|
acq_process[:should_try] = false
|
150
|
+
acq_process[:result] = :retry_limit_reached
|
150
151
|
# NOTE: reached the retry limit => dequeue from the lock queue
|
151
152
|
acq_dequeue.call
|
152
153
|
# NOTE: check and raise an error
|
@@ -154,10 +155,11 @@ module RedisQueuedLocks::Acquier
|
|
154
155
|
Failed to acquire the lock "#{lock_key}"
|
155
156
|
for the given retry_count limit (#{retry_count} times).
|
156
157
|
ERROR_MESSAGE
|
157
|
-
|
158
|
+
else
|
158
159
|
# NOTE:
|
159
160
|
# delay the exceution in order to prevent chaotic attempts
|
160
161
|
# and to allow other processes and threads to obtain the lock too.
|
162
|
+
delay_execution(retry_delay, retry_jitter)
|
161
163
|
end
|
162
164
|
end
|
163
165
|
end
|
@@ -191,12 +193,18 @@ module RedisQueuedLocks::Acquier
|
|
191
193
|
{ ok: true, result: acq_process[:lock_info] }
|
192
194
|
end
|
193
195
|
else
|
194
|
-
|
195
|
-
|
196
|
+
unless acq_process[:result] == :retry_limit_reached
|
197
|
+
# NOTE: we have only two situations if lock is not acquired:
|
198
|
+
# - time limit is reached
|
199
|
+
# - retry count limit is reached
|
200
|
+
# In other cases the lock obtaining time and tries count are infinite.
|
201
|
+
acq_process[:result] = :timeout_reached
|
202
|
+
end
|
203
|
+
# Step 3.b: lock is not acquired (acquier is dequeued by timeout callback)
|
196
204
|
{ ok: false, result: acq_process[:result] }
|
197
205
|
end
|
198
206
|
end
|
199
|
-
# rubocop:enable Metrics/MethodLength
|
207
|
+
# rubocop:enable Metrics/MethodLength, Metrics/BlockNesting
|
200
208
|
|
201
209
|
# Release the concrete lock:
|
202
210
|
# - 1. clear lock queue: al; related processes released
|
@@ -208,7 +216,7 @@ module RedisQueuedLocks::Acquier
|
|
208
216
|
# @param redis [RedisClient] Redis connection client.
|
209
217
|
# @param lock_name [String] The lock name that should be released.
|
210
218
|
# @param isntrumenter [#notify] See RedisQueuedLocks::Instrument::ActiveSupport for example.
|
211
|
-
# @return [Hash<Symbol,Any>] Format: { ok: true/false, result:
|
219
|
+
# @return [Hash<Symbol,Any>] Format: { ok: true/false, result: Hash<Symbil,Numeric|String> }
|
212
220
|
#
|
213
221
|
# @api private
|
214
222
|
# @since 0.1.0
|
@@ -231,7 +239,7 @@ module RedisQueuedLocks::Acquier
|
|
231
239
|
})
|
232
240
|
end
|
233
241
|
|
234
|
-
{ ok: true, result:
|
242
|
+
{ ok: true, result: { rel_time: rel_time, rel_key: lock_key, rel_queue: lock_key_queue } }
|
235
243
|
end
|
236
244
|
|
237
245
|
# Release all locks:
|
@@ -241,7 +249,7 @@ module RedisQueuedLocks::Acquier
|
|
241
249
|
# @param redis [RedisClient] Redis connection client.
|
242
250
|
# @param batch_size [Integer] The number of lock keys that should be released in a time.
|
243
251
|
# @param isntrumenter [#notify] See RedisQueuedLocks::Instrument::ActiveSupport for example.
|
244
|
-
# @return [Hash<Symbol,Any>] Format: { ok: true/false, result:
|
252
|
+
# @return [Hash<Symbol,Any>] Format: { ok: true/false, result: Hash<Symbol,Numeric> }
|
245
253
|
#
|
246
254
|
# @api private
|
247
255
|
# @since 0.1.0
|
@@ -260,7 +268,7 @@ module RedisQueuedLocks::Acquier
|
|
260
268
|
})
|
261
269
|
end
|
262
270
|
|
263
|
-
{ ok: true, result: result }
|
271
|
+
{ ok: true, result: { rel_key_cnt: result[:rel_keys], rel_time: rel_time } }
|
264
272
|
end
|
265
273
|
|
266
274
|
private
|
data/redis_queued_locks.gemspec
CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
|
|
11
11
|
spec.email = ['iamdaiver@gmail.com']
|
12
12
|
|
13
13
|
spec.summary = 'Queued distributed locks based on Redis.'
|
14
|
-
spec.description = 'Distributed
|
14
|
+
spec.description = 'Distributed locks with "lock acquisition queue" ' \
|
15
15
|
'capabilities based on the Redis Database.'
|
16
16
|
spec.homepage = 'https://github.com/0exp/redis_queued_locks'
|
17
17
|
spec.license = 'MIT'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis_queued_locks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rustam Ibragimov
|
@@ -38,8 +38,8 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0.28'
|
41
|
-
description: Distributed
|
42
|
-
|
41
|
+
description: Distributed locks with "lock acquisition queue" capabilities based on
|
42
|
+
the Redis Database.
|
43
43
|
email:
|
44
44
|
- iamdaiver@gmail.com
|
45
45
|
executables: []
|