redis_queued_locks 1.7.0 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -1
- data/README.md +83 -0
- data/lib/redis_queued_locks/acquier/acquire_lock/with_acq_timeout.rb +41 -7
- data/lib/redis_queued_locks/acquier/acquire_lock/yield_expire.rb +13 -5
- data/lib/redis_queued_locks/acquier/acquire_lock.rb +18 -3
- data/lib/redis_queued_locks/client.rb +62 -2
- data/lib/redis_queued_locks/version.rb +2 -2
- data/redis_queued_locks.gemspec +6 -10
- metadata +13 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f8dd117ee12f1f598da555d02516bb950a2d9640b075a048612726b7a88a6ad
|
4
|
+
data.tar.gz: c0a5ac4a4dcb6f3b448dc23ffa8d0e0e13a726d50bdd98fc40193704aa6f967d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b35d0ef98da0e1ec2c2368b36064eb0501b45d26aaa1b7204230692a41025583366ce181b85eec09cff2d6fe4e93ec146280db78e08f8cbb232a559b91f6d489
|
7
|
+
data.tar.gz: 4776dd89a7bf1e27476877465faf3240988929efe398090fa4b8b4f604a976a861558e6f606278b91c2c95530be6c5205f8245c887befe92a75d4efde31820dc
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,16 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [1.8.0] - 2024-06-13
|
4
|
+
### Added
|
5
|
+
- A configurable option that enables the adding additional lock/queue data to "Acquirement Timeout"-related error messages for better debugging;
|
6
|
+
- Configurable option is used beacuse of the extra error data requires some additional Redis requests that can be costly for memory/cpu/etc resources;
|
7
|
+
- An ability to get the current acquirer id (`RedisQueuedLocks::Client#current_acquirer_id`);
|
8
|
+
### Changed
|
9
|
+
- Added additional lock information to some exceptions that does not require extra Redis requests;
|
10
|
+
|
3
11
|
## [1.7.0] - 2024-06-12
|
4
12
|
### Added
|
5
|
-
- New feature: **Lock Access Strategy**: you can obtain a lock in different ways: `queued` (classic queued FIFO), `random` (get the lock immideatly if lock is free)
|
13
|
+
- New feature: **Lock Access Strategy**: you can obtain a lock in different ways: `queued` (classic queued FIFO), `random` (get the lock immideatly if lock is free):
|
6
14
|
- `:queued` is used by default (classic `redis_queued_locks` behavior);
|
7
15
|
- `:random`: obtain a lock without checking the positions in the queue => if lock is free to obtain - it will be obtained;
|
8
16
|
### Changed
|
data/README.md
CHANGED
@@ -34,6 +34,7 @@ Provides flexible invocation flow, parametrized limits (lock request ttl, lock t
|
|
34
34
|
- [locks_info](#locks_info---get-list-of-locks-with-their-info)
|
35
35
|
- [queues_info](#queues_info---get-list-of-queues-with-their-info)
|
36
36
|
- [clear_dead_requests](#clear_dead_requests)
|
37
|
+
- [current_acquirer_id](#current_acquirer_id)
|
37
38
|
- [Lock Access Strategies](#lock-access-strategies)
|
38
39
|
- [queued](#lock-access-strategies)
|
39
40
|
- [random](#lock-access-strategies)
|
@@ -157,6 +158,29 @@ clinet = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
157
158
|
# - should be all blocks of code are timed by default;
|
158
159
|
config.is_timed_by_default = false
|
159
160
|
|
161
|
+
# (boolean) (default: false)
|
162
|
+
# - When the lock acquirement try reached the acquirement time limit (:timeout option) the
|
163
|
+
# `RedisQueuedLocks::LockAcquirementTimeoutError` is raised (when `raise_errors` option
|
164
|
+
# of the #lock method is set to `true`). The error message contains the lock key name and
|
165
|
+
# the timeout value).
|
166
|
+
# - <true> option adds the additional details to the error message:
|
167
|
+
# - current lock queue state (you can see which acquirer blocks your request and
|
168
|
+
# how much acquirers are in queue);
|
169
|
+
# - current lock data stored inside (for example: you can check the current acquirer and
|
170
|
+
# the lock meta state if you store some additional data there);
|
171
|
+
# - Realized as an option because of the additional lock data requires two additional Redis
|
172
|
+
# queries: (1) get the current lock from redis and (2) fetch the lock queue state;
|
173
|
+
# - These two additional Redis queries has async nature so you can receive
|
174
|
+
# inconsistent data of the lock and of the lock queue in your error emssage because:
|
175
|
+
# - required lock can be released after the error moment and before the error message build;
|
176
|
+
# - required lock can be obtained by other process after the error moment and
|
177
|
+
# before the error message build;
|
178
|
+
# - required lock queue can reach a state when the blocking acquirer start to obtain the lock
|
179
|
+
# and moved from the lock queue after the error moment and before the error message build;
|
180
|
+
# - You should consider the async nature of this error message and should use received data
|
181
|
+
# from error message correspondingly;
|
182
|
+
config.detailed_acq_timeout_error = false
|
183
|
+
|
160
184
|
# (symbol) (default: :queued)
|
161
185
|
# - Defines the way in which the lock should be obitained;
|
162
186
|
# - By default it is configured to obtain a lock in classic `queued` way:
|
@@ -316,6 +340,7 @@ end
|
|
316
340
|
- [locks_info](#locks_info---get-list-of-locks-with-their-info)
|
317
341
|
- [queues_info](#queues_info---get-list-of-queues-with-their-info)
|
318
342
|
- [clear_dead_requests](#clear_dead_requests)
|
343
|
+
- [current_acquirer_id](#current_acquirer_id)
|
319
344
|
|
320
345
|
---
|
321
346
|
|
@@ -348,6 +373,7 @@ def lock(
|
|
348
373
|
access_strategy: config[:default_access_strategy],
|
349
374
|
identity: uniq_identity, # (attr_accessor) calculated during client instantiation via config[:uniq_identifier] proc;
|
350
375
|
meta: nil,
|
376
|
+
detailed_acq_timeout_error: config[:detailed_acq_timeout_error],
|
351
377
|
instrument: nil,
|
352
378
|
instrumenter: config[:instrumenter],
|
353
379
|
logger: config[:logger],
|
@@ -433,6 +459,25 @@ def lock(
|
|
433
459
|
- A custom metadata wich will be passed to the lock data in addition to the existing data;
|
434
460
|
- Custom metadata can not contain reserved lock data keys (such as `lock_key`, `acq_id`, `ts`, `ini_ttl`, `rem_ttl`);
|
435
461
|
- `nil` by default (means "no metadata");
|
462
|
+
- `detailed_acq_timeout_error` - (optional) `[Boolean]`
|
463
|
+
- When the lock acquirement try reached the acquirement time limit (:timeout option) the
|
464
|
+
`RedisQueuedLocks::LockAcquirementTimeoutError` is raised (when `raise_errors` option
|
465
|
+
set to `true`). The error message contains the lock key name and the timeout value).
|
466
|
+
- <true> option adds the additional details to the error message:
|
467
|
+
- current lock queue state (you can see which acquirer blocks your request and how much acquirers are in queue);
|
468
|
+
- current lock data stored inside (for example: you can check the current acquirer and the lock meta state if you store some additional data there);
|
469
|
+
- Realized as an option because of the additional lock data requires two additional Redis
|
470
|
+
queries: (1) get the current lock from redis and (2) fetch the lock queue state;
|
471
|
+
- These two additional Redis queries has async nature so you can receive
|
472
|
+
inconsistent data of the lock and of the lock queue in your error emssage because:
|
473
|
+
- required lock can be released after the error moment and before the error message build;
|
474
|
+
- required lock can be obtained by other process after the error moment and
|
475
|
+
before the error message build;
|
476
|
+
- required lock queue can reach a state when the blocking acquirer start to obtain the lock
|
477
|
+
and moved from the lock queue after the error moment and before the error message build;
|
478
|
+
- You should consider the async nature of this error message and should use received data
|
479
|
+
from error message correspondingly;
|
480
|
+
- pre-configred in `config[:detailed_acq_timeout_error]`;
|
436
481
|
- `logger` - (optional) `[::Logger,#debug]`
|
437
482
|
- Logger object used for loggin internal mutation oeprations and opertioan results / process progress;
|
438
483
|
- pre-configured in `config[:logger]` with void logger `RedisQueuedLocks::Logging::VoidLogger`;
|
@@ -725,6 +770,7 @@ def lock!(
|
|
725
770
|
fail_fast: false,
|
726
771
|
identity: uniq_identity,
|
727
772
|
meta: nil,
|
773
|
+
detailed_acq_timeout_error: config[:detailed_acq_timeout_error]
|
728
774
|
logger: config[:logger],
|
729
775
|
log_lock_try: config[:log_lock_try],
|
730
776
|
instrument: nil,
|
@@ -1319,8 +1365,45 @@ rql.clear_dead_requests(dead_ttl: 60 * 60 * 1000) # 1 hour in milliseconds
|
|
1319
1365
|
|
1320
1366
|
---
|
1321
1367
|
|
1368
|
+
#### #current_acquirer_id
|
1369
|
+
|
1370
|
+
- get the current acquirer identifier in RQL notation that you can use for debugging purposes during the lock analyzation;
|
1371
|
+
- acquirer identifier format:
|
1372
|
+
```ruby
|
1373
|
+
"rql:acq:#{process_id}/#{thread_id}/#{fiber_id}/#{ractor_id}/#{identity}"
|
1374
|
+
```
|
1375
|
+
- because of the moment that `#lock`/`#lock!` gives you a possibility to customize `process_id`,
|
1376
|
+
`fiber_id`, `thread_id`, `ractor_id` and `unique identity` identifiers the `#current_acquirer_id` method provides this possibility too;
|
1377
|
+
|
1378
|
+
Accepts:
|
1379
|
+
|
1380
|
+
- `process_id:` - (optional) `[Integer,Any]`
|
1381
|
+
- `::Process.pid` by default;
|
1382
|
+
- `thread_id:` - (optional) `[Integer,Any]`;
|
1383
|
+
- `::Thread.current.object_id` by default;
|
1384
|
+
- `fiber_id:` - (optional) `[Integer,Any]`;
|
1385
|
+
- `::Fiber.current.object_id` by default;
|
1386
|
+
- `ractor_id:` - (optional) `[Integer,Any]`;
|
1387
|
+
- `::Ractor.current.object_id` by default;
|
1388
|
+
- `identity:` - (optional) `[String,Any]`;
|
1389
|
+
- this value is calculated once during `RedisQueuedLock::Client` instantiation and stored in `@uniq_identity`;
|
1390
|
+
- this value can be accessed from `RedisQueuedLock::Client#uniq_identity`;
|
1391
|
+
- [Configuration](#configuration) documentation: see `config[:uniq_identifier]`;
|
1392
|
+
- [#lock](#lock---obtain-a-lock) method documentation: see `uniq_identifier`;
|
1393
|
+
|
1394
|
+
```ruby
|
1395
|
+
rql.current_acquirer_id
|
1396
|
+
|
1397
|
+
# =>
|
1398
|
+
"rql:acq:38529/4500/4520/4360/66093702f24a3129"
|
1399
|
+
```
|
1400
|
+
|
1401
|
+
---
|
1402
|
+
|
1322
1403
|
## Lock Access Strategies
|
1323
1404
|
|
1405
|
+
<sup>\[[back to top](#table-of-contents)\]</sup>
|
1406
|
+
|
1324
1407
|
- **this documentation section is in progress**;
|
1325
1408
|
- (little details for a context of the current implementation and feautres):
|
1326
1409
|
- defines the way in which the lock should be obitained;
|
@@ -3,29 +3,63 @@
|
|
3
3
|
# @api private
|
4
4
|
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Acquier::AcquireLock::WithAcqTimeout
|
6
|
+
# @param redis [RedisClient]
|
7
|
+
# Redis connection manager required for additional data extraction for error message.
|
6
8
|
# @param timeout [NilClass,Integer]
|
7
9
|
# Time period after which the logic will fail with timeout error.
|
8
10
|
# @param lock_key [String]
|
9
|
-
# Lock name.
|
11
|
+
# Lock name in RQL notation (rql:lock:some-lock-name).
|
12
|
+
# @param lock_name [String]
|
13
|
+
# Original lock name passed by the businessl logic (without RQL notaiton parts).
|
10
14
|
# @param raise_errors [Boolean]
|
11
15
|
# Raise erros on exceptional cases.
|
16
|
+
# @param detailed_acq_timeout_error [Boolean]
|
17
|
+
# Add additional error data about lock queue and required lock to the timeout error or not.
|
12
18
|
# @option on_timeout [Proc,NilClass]
|
13
19
|
# Callback invoked on Timeout::Error.
|
14
20
|
# @return [Any]
|
15
21
|
#
|
16
22
|
# @api private
|
17
23
|
# @since 1.0.0
|
18
|
-
|
24
|
+
# @version 1.8.0
|
25
|
+
def with_acq_timeout(
|
26
|
+
redis,
|
27
|
+
timeout,
|
28
|
+
lock_key,
|
29
|
+
lock_name,
|
30
|
+
raise_errors,
|
31
|
+
detailed_acq_timeout_error,
|
32
|
+
on_timeout: nil,
|
33
|
+
&block
|
34
|
+
)
|
19
35
|
::Timeout.timeout(timeout, &block)
|
20
36
|
rescue ::Timeout::Error
|
21
37
|
on_timeout.call unless on_timeout == nil
|
22
38
|
|
23
39
|
if raise_errors
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
40
|
+
if detailed_acq_timeout_error
|
41
|
+
# TODO: rewrite these invocations to separated inner-AcquireLock-related modules
|
42
|
+
# in order to remove any dependencies from the other public RQL commands cuz
|
43
|
+
# all AcquireLock logic elements should be fully independent from others as a core;
|
44
|
+
lock_info = RedisQueuedLocks::Acquier::LockInfo.lock_info(redis, lock_name)
|
45
|
+
queue_info = RedisQueuedLocks::Acquier::QueueInfo.queue_info(redis, lock_name)
|
46
|
+
|
47
|
+
# rubocop:disable Metrics/BlockNesting
|
48
|
+
raise(
|
49
|
+
RedisQueuedLocks::LockAcquiermentTimeoutError,
|
50
|
+
"Failed to acquire the lock \"#{lock_key}\" " \
|
51
|
+
"for the given <#{timeout} seconds> timeout. Details: " \
|
52
|
+
"<Lock Data> => #{lock_info ? lock_info.inspect : '<no_data>'}; " \
|
53
|
+
"<Queue Data> => #{queue_info ? queue_info.inspect : '<no_data>'};"
|
54
|
+
)
|
55
|
+
# rubocop:enable Metrics/BlockNesting
|
56
|
+
else
|
57
|
+
raise(
|
58
|
+
RedisQueuedLocks::LockAcquiermentTimeoutError,
|
59
|
+
"Failed to acquire the lock \"#{lock_key}\" " \
|
60
|
+
"for the given <#{timeout} seconds> timeout."
|
61
|
+
)
|
62
|
+
end
|
29
63
|
end
|
30
64
|
end
|
31
65
|
end
|
@@ -25,6 +25,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldExpire
|
|
25
25
|
# @param ttl [Integer,NilClass] Lock's time to live (in ms). Nil means "without timeout".
|
26
26
|
# @param queue_ttl [Integer] Lock request lifetime.
|
27
27
|
# @param block [Block] Custom logic that should be invoked unter the obtained lock.
|
28
|
+
# @param meta [NilClass,Hash<String|Symbol,Any>] Custom metadata wich is passed to the lock data;
|
28
29
|
# @param log_sampled [Boolean] Should the logic be logged or not.
|
29
30
|
# @param instr_sampled [Boolean] Should the logic be instrumented or not.
|
30
31
|
# @param should_expire [Boolean] Should the lock be expired after the block invocation.
|
@@ -35,7 +36,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldExpire
|
|
35
36
|
#
|
36
37
|
# @api private
|
37
38
|
# @since 1.3.0
|
38
|
-
# @version 1.
|
39
|
+
# @version 1.8.0
|
39
40
|
# rubocop:disable Metrics/MethodLength
|
40
41
|
def yield_expire(
|
41
42
|
redis,
|
@@ -47,6 +48,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldExpire
|
|
47
48
|
ttl_shift,
|
48
49
|
ttl,
|
49
50
|
queue_ttl,
|
51
|
+
meta,
|
50
52
|
log_sampled,
|
51
53
|
instr_sampled,
|
52
54
|
should_expire,
|
@@ -62,7 +64,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldExpire
|
|
62
64
|
end
|
63
65
|
|
64
66
|
if timed && ttl != nil
|
65
|
-
yield_with_timeout(timeout, lock_key, ttl, &block)
|
67
|
+
yield_with_timeout(timeout, lock_key, ttl, acquier_id, meta, &block)
|
66
68
|
else
|
67
69
|
yield
|
68
70
|
end
|
@@ -97,18 +99,24 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldExpire
|
|
97
99
|
# @param timeout [Float]
|
98
100
|
# @parma lock_key [String]
|
99
101
|
# @param lock_ttl [Integer,NilClass]
|
102
|
+
# @param acquier_id [String]
|
103
|
+
# @param meta [NilClass,Hash<Symbol|String,Any>]
|
100
104
|
# @param block [Blcok]
|
101
105
|
# @return [Any]
|
102
106
|
#
|
103
107
|
# @api private
|
104
108
|
# @since 1.3.0
|
105
|
-
|
109
|
+
# @version 1.8.0
|
110
|
+
def yield_with_timeout(timeout, lock_key, lock_ttl, acquier_id, meta, &block)
|
106
111
|
::Timeout.timeout(timeout, &block)
|
107
112
|
rescue ::Timeout::Error
|
108
113
|
raise(
|
109
114
|
RedisQueuedLocks::TimedLockTimeoutError,
|
110
|
-
"Passed <timed> block of code exceeded " \
|
111
|
-
"
|
115
|
+
"Passed <timed> block of code exceeded the lock TTL " \
|
116
|
+
"(lock: \"#{lock_key}\", " \
|
117
|
+
"ttl: #{lock_ttl}, " \
|
118
|
+
"meta: #{meta ? meta.inspect : '<no-meta>'}, " \
|
119
|
+
"acq_id: \"#{acquier_id}\")"
|
112
120
|
)
|
113
121
|
end
|
114
122
|
end
|
@@ -69,6 +69,10 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
69
69
|
# @option meta [NilClass,Hash<String|Symbol,Any>]
|
70
70
|
# - A custom metadata wich will be passed to the lock data in addition to the existing data;
|
71
71
|
# - Metadata can not contain reserved lock data keys;
|
72
|
+
# @option detailed_acq_timeout_error [Boolean]
|
73
|
+
# - Add additional data to the acquirement timeout error such as the current lock queue state
|
74
|
+
# and the required lock state;
|
75
|
+
# - See `config[:detailed_acq_timeout_error]` for details;
|
72
76
|
# @option logger [::Logger,#debug]
|
73
77
|
# - Logger object used from the configuration layer (see config[:logger]);
|
74
78
|
# - See `RedisQueuedLocks::Logging::VoidLogger` for example;
|
@@ -143,7 +147,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
143
147
|
#
|
144
148
|
# @api private
|
145
149
|
# @since 1.0.0
|
146
|
-
# @version 1.
|
150
|
+
# @version 1.8.0
|
147
151
|
def acquire_lock(
|
148
152
|
redis,
|
149
153
|
lock_name,
|
@@ -163,6 +167,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
163
167
|
identity:,
|
164
168
|
fail_fast:,
|
165
169
|
meta:,
|
170
|
+
detailed_acq_timeout_error:,
|
166
171
|
instrument:,
|
167
172
|
logger:,
|
168
173
|
log_lock_try:,
|
@@ -246,7 +251,8 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
246
251
|
|
247
252
|
acq_dequeue = proc do
|
248
253
|
dequeue_from_lock_queue(
|
249
|
-
redis,
|
254
|
+
redis,
|
255
|
+
logger,
|
250
256
|
lock_key,
|
251
257
|
lock_key_queue,
|
252
258
|
queue_ttl,
|
@@ -263,7 +269,15 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
263
269
|
)
|
264
270
|
|
265
271
|
# Step 2: try to lock with timeout
|
266
|
-
with_acq_timeout(
|
272
|
+
with_acq_timeout(
|
273
|
+
redis,
|
274
|
+
timeout,
|
275
|
+
lock_key,
|
276
|
+
lock_name,
|
277
|
+
raise_errors,
|
278
|
+
detailed_acq_timeout_error,
|
279
|
+
on_timeout: acq_dequeue
|
280
|
+
) do
|
267
281
|
acq_start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
|
268
282
|
|
269
283
|
# Step 2.1: cyclically try to obtain the lock
|
@@ -455,6 +469,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
455
469
|
ttl_shift,
|
456
470
|
ttl,
|
457
471
|
queue_ttl,
|
472
|
+
meta,
|
458
473
|
log_sampled,
|
459
474
|
instr_sampled,
|
460
475
|
should_expire, # NOTE: should expire the lock after the block execution
|
@@ -14,6 +14,7 @@ class RedisQueuedLocks::Client
|
|
14
14
|
setting :try_to_lock_timeout, 10 # NOTE: in seconds
|
15
15
|
setting :default_lock_ttl, 5_000 # NOTE: in milliseconds
|
16
16
|
setting :default_queue_ttl, 15 # NOTE: in seconds
|
17
|
+
setting :detailed_acq_timeout_error, false
|
17
18
|
setting :lock_release_batch_size, 100
|
18
19
|
setting :key_extraction_batch_size, 500
|
19
20
|
setting :instrumenter, RedisQueuedLocks::Instrument::VoidNotifier
|
@@ -37,6 +38,7 @@ class RedisQueuedLocks::Client
|
|
37
38
|
validate('try_to_lock_timeout') { |val| val == nil || (val.is_a?(::Integer) && val >= 0) }
|
38
39
|
validate('default_lock_tt', :integer)
|
39
40
|
validate('default_queue_ttl', :integer)
|
41
|
+
validate('detailed_acq_timeout_error', :boolean)
|
40
42
|
validate('lock_release_batch_size', :integer)
|
41
43
|
validate('instrumenter') { |val| RedisQueuedLocks::Instrument.valid_interface?(val) }
|
42
44
|
validate('uniq_identifier', :proc)
|
@@ -95,6 +97,39 @@ class RedisQueuedLocks::Client
|
|
95
97
|
@redis_client = redis_client
|
96
98
|
end
|
97
99
|
|
100
|
+
# Retrun the current acquirer identifier.
|
101
|
+
#
|
102
|
+
# @option process_id [Integer,Any] Process identifier.
|
103
|
+
# @option thread_id [Integer,Any] Thread identifier.
|
104
|
+
# @option fiber_id [Integer,Any] Fiber identifier.
|
105
|
+
# @option ractor_id [Integer,Any] Ractor identifier.
|
106
|
+
# @option identity [String] Unique per-process string. See `config[:uniq_identifier]`.
|
107
|
+
# @return [String]
|
108
|
+
#
|
109
|
+
# @see RedisQueuedLocks::Resource.get_process_id
|
110
|
+
# @see RedisQueuedLocks::Resource.get_thread_id
|
111
|
+
# @see RedisQueuedLocks::Resource.get_fiber_id
|
112
|
+
# @see RedisQueuedLocks::Resource.get_ractor_id
|
113
|
+
# @see RedisQueuedLocks::Client#uniq_identity
|
114
|
+
#
|
115
|
+
# @api public
|
116
|
+
# @since 1.8.0
|
117
|
+
def current_acquier_id(
|
118
|
+
process_id: RedisQueuedLocks::Resource.get_process_id,
|
119
|
+
thread_id: RedisQueuedLocks::Resource.get_thread_id,
|
120
|
+
fiber_id: RedisQueuedLocks::Resource.get_fiber_id,
|
121
|
+
ractor_id: RedisQueuedLocks::Resource.get_ractor_id,
|
122
|
+
identity: uniq_identity
|
123
|
+
)
|
124
|
+
RedisQueuedLocks::Resource.acquier_identifier(
|
125
|
+
process_id,
|
126
|
+
thread_id,
|
127
|
+
fiber_id,
|
128
|
+
ractor_id,
|
129
|
+
identity
|
130
|
+
)
|
131
|
+
end
|
132
|
+
|
98
133
|
# @param lock_name [String]
|
99
134
|
# Lock name to be obtained.
|
100
135
|
# @option ttl [Integer]
|
@@ -146,6 +181,27 @@ class RedisQueuedLocks::Client
|
|
146
181
|
# @option meta [NilClass,Hash<String|Symbol,Any>]
|
147
182
|
# - A custom metadata wich will be passed to the lock data in addition to the existing data;
|
148
183
|
# - Metadata can not contain reserved lock data keys;
|
184
|
+
# @option detailed_acq_timeout_error [Boolean]
|
185
|
+
# - When the lock acquirement try reached the acquirement time limit (:timeout option) the
|
186
|
+
# `RedisQueuedLocks::LockAcquirementTimeoutError` is raised (when `raise_errors` option
|
187
|
+
# set to `true`). The error message contains the lock key name and the timeout value).
|
188
|
+
# - <true> option adds the additional details to the error message:
|
189
|
+
# - current lock queue state (you can see which acquirer blocks your request and
|
190
|
+
# how much acquirers are in queue);
|
191
|
+
# - current lock data stored inside (for example: you can check the current acquirer and
|
192
|
+
# the lock meta state if you store some additional data there);
|
193
|
+
# - Realized as an option because of the additional lock data requires two additional Redis
|
194
|
+
# queries: (1) get the current lock from redis and (2) fetch the lock queue state;
|
195
|
+
# - These two additional Redis queries has async nature so you can receive
|
196
|
+
# inconsistent data of the lock and of the lock queue in your error emssage because:
|
197
|
+
# - required lock can be released after the error moment and before the error message build;
|
198
|
+
# - required lock can be obtained by other process after the error moment and
|
199
|
+
# before the error message build;
|
200
|
+
# - required lock queue can reach a state when the blocking acquirer start to obtain the lock
|
201
|
+
# and moved from the lock queue after the error moment and before the error message build;
|
202
|
+
# - You should consider the async nature of this error message and should use received data
|
203
|
+
# from error message correspondingly;
|
204
|
+
# - pre-configred in `config[:detailed_acq_timeout_error]`;
|
149
205
|
# @option logger [::Logger,#debug]
|
150
206
|
# - Logger object used from the configuration layer (see config[:logger]);
|
151
207
|
# - See `RedisQueuedLocks::Logging::VoidLogger` for example;
|
@@ -206,7 +262,7 @@ class RedisQueuedLocks::Client
|
|
206
262
|
#
|
207
263
|
# @api public
|
208
264
|
# @since 1.0.0
|
209
|
-
# @version 1.
|
265
|
+
# @version 1.8.0
|
210
266
|
# rubocop:disable Metrics/MethodLength
|
211
267
|
def lock(
|
212
268
|
lock_name,
|
@@ -223,6 +279,7 @@ class RedisQueuedLocks::Client
|
|
223
279
|
access_strategy: config[:default_access_strategy],
|
224
280
|
identity: uniq_identity,
|
225
281
|
meta: nil,
|
282
|
+
detailed_acq_timeout_error: config[:detailed_acq_timeout_error],
|
226
283
|
logger: config[:logger],
|
227
284
|
log_lock_try: config[:log_lock_try],
|
228
285
|
instrumenter: config[:instrumenter],
|
@@ -256,6 +313,7 @@ class RedisQueuedLocks::Client
|
|
256
313
|
conflict_strategy:,
|
257
314
|
access_strategy:,
|
258
315
|
meta:,
|
316
|
+
detailed_acq_timeout_error:,
|
259
317
|
logger:,
|
260
318
|
log_lock_try:,
|
261
319
|
instrument:,
|
@@ -274,7 +332,7 @@ class RedisQueuedLocks::Client
|
|
274
332
|
#
|
275
333
|
# @api public
|
276
334
|
# @since 1.0.0
|
277
|
-
# @version 1.
|
335
|
+
# @version 1.8.0
|
278
336
|
# rubocop:disable Metrics/MethodLength
|
279
337
|
def lock!(
|
280
338
|
lock_name,
|
@@ -291,6 +349,7 @@ class RedisQueuedLocks::Client
|
|
291
349
|
identity: uniq_identity,
|
292
350
|
instrumenter: config[:instrumenter],
|
293
351
|
meta: nil,
|
352
|
+
detailed_acq_timeout_error: config[:detailed_acq_timeout_error],
|
294
353
|
logger: config[:logger],
|
295
354
|
log_lock_try: config[:log_lock_try],
|
296
355
|
instrument: nil,
|
@@ -317,6 +376,7 @@ class RedisQueuedLocks::Client
|
|
317
376
|
logger:,
|
318
377
|
log_lock_try:,
|
319
378
|
meta:,
|
379
|
+
detailed_acq_timeout_error:,
|
320
380
|
instrument:,
|
321
381
|
instrumenter:,
|
322
382
|
conflict_strategy:,
|
data/redis_queued_locks.gemspec
CHANGED
@@ -15,21 +15,17 @@ Gem::Specification.new do |spec|
|
|
15
15
|
'capabilities based on the Redis Database.'
|
16
16
|
|
17
17
|
spec.description =
|
18
|
-
'Distributed locks with "prioritized lock acquisition queue" capabilities ' \
|
18
|
+
'|> Distributed locks with "prioritized lock acquisition queue" capabilities ' \
|
19
19
|
'based on the Redis Database. ' \
|
20
|
-
|
21
|
-
'
|
22
|
-
"(each lock is hosted by it's own queue separately from other queues) and processed " \
|
20
|
+
'|> Each lock request is put into the request queue ' \
|
21
|
+
'(each lock is hosted by it\'s own queue separately from other queues) and processed ' \
|
23
22
|
'in order of their priority (FIFO). ' \
|
24
|
-
|
25
|
-
'Each lock request lives some period of time (RTTL) ' \
|
23
|
+
'|> Each lock request lives some period of time (RTTL) ' \
|
26
24
|
'(with requeue capabilities) which guarantees the request queue will never be stacked. ' \
|
27
|
-
|
28
|
-
'In addition to the classic `queued` (FIFO) strategy RQL supports ' \
|
25
|
+
'|> In addition to the classic `queued` (FIFO) strategy RQL supports ' \
|
29
26
|
'`random` (RANDOM) lock obtaining strategy when any acquirer from the lock queue ' \
|
30
27
|
'can obtain the lock regardless the position in the queue. ' \
|
31
|
-
|
32
|
-
'Provides flexible invocation flow, parametrized limits ' \
|
28
|
+
'|> Provides flexible invocation flow, parametrized limits ' \
|
33
29
|
'(lock request ttl, lock ttl, queue ttl, lock attempts limit, fast failing, etc), ' \
|
34
30
|
'logging and instrumentation.'
|
35
31
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis_queued_locks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rustam Ibragimov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-06-
|
11
|
+
date: 2024-06-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis-client
|
@@ -38,8 +38,16 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0.28'
|
41
|
-
description: 'Distributed locks with "prioritized lock acquisition queue" capabilities
|
42
|
-
based on the Redis Database.
|
41
|
+
description: '|> Distributed locks with "prioritized lock acquisition queue" capabilities
|
42
|
+
based on the Redis Database. |> Each lock request is put into the request queue
|
43
|
+
(each lock is hosted by it''s own queue separately from other queues) and processed
|
44
|
+
in order of their priority (FIFO). |> Each lock request lives some period of time
|
45
|
+
(RTTL) (with requeue capabilities) which guarantees the request queue will never
|
46
|
+
be stacked. |> In addition to the classic `queued` (FIFO) strategy RQL supports
|
47
|
+
`random` (RANDOM) lock obtaining strategy when any acquirer from the lock queue
|
48
|
+
can obtain the lock regardless the position in the queue. |> Provides flexible invocation
|
49
|
+
flow, parametrized limits (lock request ttl, lock ttl, queue ttl, lock attempts
|
50
|
+
limit, fast failing, etc), logging and instrumentation.'
|
43
51
|
email:
|
44
52
|
- iamdaiver@gmail.com
|
45
53
|
executables: []
|
@@ -117,7 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
125
|
- !ruby/object:Gem::Version
|
118
126
|
version: '0'
|
119
127
|
requirements: []
|
120
|
-
rubygems_version: 3.
|
128
|
+
rubygems_version: 3.5.1
|
121
129
|
signing_key:
|
122
130
|
specification_version: 4
|
123
131
|
summary: Distributed locks with "prioritized lock acquisition queue" capabilities
|