redis_queued_locks 1.7.0 → 1.8.0
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 +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
|