redis_queued_locks 0.0.5 → 0.0.7
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 +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: []
|