redis_queued_locks 1.0.0 → 1.2.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 +15 -1
- data/README.md +84 -17
- data/lib/redis_queued_locks/acquier/acquire_lock/delay_execution.rb +2 -2
- data/lib/redis_queued_locks/acquier/acquire_lock/try_to_lock.rb +4 -4
- data/lib/redis_queued_locks/acquier/acquire_lock/with_acq_timeout.rb +2 -2
- data/lib/redis_queued_locks/acquier/acquire_lock/yield_with_expire.rb +4 -4
- data/lib/redis_queued_locks/acquier/acquire_lock.rb +9 -9
- data/lib/redis_queued_locks/acquier/clear_dead_requests.rb +3 -3
- data/lib/redis_queued_locks/acquier/extend_lock_ttl.rb +3 -3
- data/lib/redis_queued_locks/acquier/is_locked.rb +2 -2
- data/lib/redis_queued_locks/acquier/is_queued.rb +2 -2
- data/lib/redis_queued_locks/acquier/keys.rb +2 -2
- data/lib/redis_queued_locks/acquier/lock_info.rb +2 -2
- data/lib/redis_queued_locks/acquier/locks.rb +4 -4
- data/lib/redis_queued_locks/acquier/queue_info.rb +2 -2
- data/lib/redis_queued_locks/acquier/queues.rb +4 -4
- data/lib/redis_queued_locks/acquier/release_all_locks.rb +4 -4
- data/lib/redis_queued_locks/acquier/release_lock.rb +4 -4
- data/lib/redis_queued_locks/acquier.rb +1 -1
- data/lib/redis_queued_locks/client.rb +27 -25
- data/lib/redis_queued_locks/debugger/interface.rb +4 -4
- data/lib/redis_queued_locks/debugger.rb +8 -8
- data/lib/redis_queued_locks/errors.rb +6 -6
- data/lib/redis_queued_locks/instrument/active_support.rb +2 -2
- data/lib/redis_queued_locks/instrument/void_notifier.rb +2 -2
- data/lib/redis_queued_locks/instrument.rb +2 -2
- data/lib/redis_queued_locks/logging/void_logger.rb +10 -10
- data/lib/redis_queued_locks/logging.rb +10 -3
- data/lib/redis_queued_locks/resource.rb +16 -16
- data/lib/redis_queued_locks/utilities.rb +2 -2
- data/lib/redis_queued_locks/version.rb +2 -2
- data/lib/redis_queued_locks.rb +2 -2
- 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: 6b1c8d60f41d2dc4f3eda798efa02a4764b877e6666e8babd7cf1a7827a4b288
|
4
|
+
data.tar.gz: 983e142251b344055013aa2aac2965ad542d44f5c8f81ac410f97cdef7b216b3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 88b91754923e546767d03999601fb98bd04b00fb426e50691e2fd4ce1bf13e345c19179b534660a639d0a885d1fdad4db94ffda0fa4383d7d9dcf53981ca481b
|
7
|
+
data.tar.gz: f9368018c66eee962a9dd6be1b38efe7bb4359cdfb262956f4e2e43622aaf747b67b4aa6b7a9bcda6c8449e011cb1c2954d5a6e5324afdbfbb8eea0ca96085b0
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [1.2.0] - 2024-04-27
|
4
|
+
### Added
|
5
|
+
- Documentation updates;
|
6
|
+
- Logging: support for `semantic_logger` loggers (see: https://logger.rocketjob.io/) (https://github.com/reidmorrison/semantic_logger)
|
7
|
+
|
8
|
+
## [1.1.0] - 2024-04-01
|
9
|
+
### Added
|
10
|
+
- Documentation updates:
|
11
|
+
- more `#lock` examples;
|
12
|
+
- added missing docs for `config.dead_request_ttl`;
|
13
|
+
- some minor updates;
|
14
|
+
### Changed
|
15
|
+
- `#clear_dead_requests`: `:scan_size` is equal to `config[:lock_release_batch_size]` now (instead of to `config[:key_extraction_batch_size]`), cuz `#clear_dead_requests` works with lock releasing;
|
16
|
+
|
3
17
|
## [1.0.0] - 2024-04-01
|
4
18
|
- First Major Release;
|
5
19
|
|
@@ -185,7 +199,7 @@
|
|
185
199
|
### Changed
|
186
200
|
- The lock acquier identifier (`acq_id`) now includes the fiber id, the ractor id and an unique per-process
|
187
201
|
10 byte string. It is added in order to prevent collisions between different processes/pods
|
188
|
-
that will have the same
|
202
|
+
that will have the same process id / thread id identifiers (cuz it is an object_id integers) that can lead
|
189
203
|
to the same position with the same `acq_id` for different processes/pods in the lock request queue.
|
190
204
|
|
191
205
|
## [0.0.8] - 2024-02-27
|
data/README.md
CHANGED
@@ -150,15 +150,21 @@ clinet = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
150
150
|
config.is_timed_by_default = false
|
151
151
|
|
152
152
|
# (default: 100)
|
153
|
-
# - how many items will be released at a time in
|
154
|
-
# - affects the
|
153
|
+
# - how many items will be released at a time in #clear_locks and in #clear_dead_requests (uses SCAN);
|
154
|
+
# - affects the performance of your Redis and Ruby Application (configure thoughtfully);
|
155
155
|
config.lock_release_batch_size = 100
|
156
156
|
|
157
157
|
# (default: 500)
|
158
|
-
# - how many items should be extracted from redis during the #locks, #queues
|
158
|
+
# - how many items should be extracted from redis during the #locks, #queues, #keys
|
159
|
+
# #locks_info, and #queues_info operations (uses SCAN);
|
159
160
|
# - affects the performance of your Redis and Ruby Application (configure thoughtfully;)
|
160
161
|
config.key_extraction_batch_size = 500
|
161
162
|
|
163
|
+
# (default: 1 day)
|
164
|
+
# - the default period of time (in milliseconds) after which a lock request is considered dead;
|
165
|
+
# - used for `#clear_dead_requests` as default vaule of `:dead_ttl` option;
|
166
|
+
config.dead_request_ttl = (1 * 24 * 60 * 60 * 1000) # one day in milliseconds
|
167
|
+
|
162
168
|
# (default: RedisQueuedLocks::Instrument::VoidNotifier)
|
163
169
|
# - instrumentation layer;
|
164
170
|
# - you can provde your own instrumenter with `#notify(event, payload = {})`
|
@@ -245,7 +251,7 @@ def lock(
|
|
245
251
|
ttl: config[:default_lock_ttl],
|
246
252
|
queue_ttl: config[:default_queue_ttl],
|
247
253
|
timeout: config[:try_to_lock_timeout],
|
248
|
-
timed:
|
254
|
+
timed: config[:is_timed_by_default],
|
249
255
|
retry_count: config[:retry_count],
|
250
256
|
retry_delay: config[:retry_delay],
|
251
257
|
retry_jitter: config[:retry_jitter],
|
@@ -318,10 +324,12 @@ def lock(
|
|
318
324
|
- should be logged the each try of lock acquiring (a lot of logs can be generated depending on your retry configurations);
|
319
325
|
- pre-configured in `config[:log_lock_try]`;
|
320
326
|
- `false` by default;
|
321
|
-
- `block` - `[Block]`
|
327
|
+
- `block` - (optional) `[Block]`
|
322
328
|
- A block of code that should be executed after the successfully acquired lock.
|
323
329
|
- If block is **passed** the obtained lock will be released after the block execution or it's ttl (what will happen first);
|
324
330
|
- If block is **not passed** the obtained lock will be released after it's ttl;
|
331
|
+
- If you want the block to have a TTL too and this TTL to be the same as TTL of the lock
|
332
|
+
use `timed: true` option (`rql.lock("my_lock", timed: true, ttl: 5_000) { ... }`)
|
325
333
|
|
326
334
|
Return value:
|
327
335
|
|
@@ -429,6 +437,61 @@ rql.lock("my_lock", ttl: 6_500) # blocks execution until the lock is obtained
|
|
429
437
|
puts "Let's go" # will be called immediately after the lock is obtained
|
430
438
|
```
|
431
439
|
|
440
|
+
- add custom metadata to the lock (via `:meta` option):
|
441
|
+
|
442
|
+
```ruby
|
443
|
+
rql.lock("my_lock", ttl: 123456, meta: { "some" => "data", key: 123.456 })
|
444
|
+
|
445
|
+
rql.lock_info("my_lock")
|
446
|
+
# =>
|
447
|
+
{
|
448
|
+
"lock_key" => "rql:lock:my_lock",
|
449
|
+
"acq_id" => "rql:acq:123/456/567/678/374dd74324",
|
450
|
+
"ts" => 123456789,
|
451
|
+
"ini_ttl" => 123456,
|
452
|
+
"rem_ttl" => 123440,
|
453
|
+
"some" => "data",
|
454
|
+
"key" => "123.456" # NOTE: returned as a raw string directly from Redis
|
455
|
+
}
|
456
|
+
```
|
457
|
+
|
458
|
+
- (`:queue_ttl`) setting a short limit of time to the lock request queue position (if a process fails to acquire
|
459
|
+
the lock within this period of time (and before timeout/retry_count limits occurs of course) -
|
460
|
+
it's lock request will be moved to the end of queue):
|
461
|
+
|
462
|
+
```ruby
|
463
|
+
rql.lock("my_lock", queue_ttl: 5, timeout: 10_000, retry_count: nil)
|
464
|
+
# "queue_ttl: 5": 5 seconds time slot before the lock request moves to the end of queue;
|
465
|
+
# "timeout" and "retry_count" is used as "endless lock try attempts" example to show the lock queue behavior;
|
466
|
+
|
467
|
+
# lock queue: =>
|
468
|
+
[
|
469
|
+
"rql:acq:123/456/567/676/374dd74324",
|
470
|
+
"rql:acq:123/456/567/677/374dd74322", # <- long living lock
|
471
|
+
"rql:acq:123/456/567/679/374dd74321",
|
472
|
+
"rql:acq:123/456/567/683/374dd74322", # <== we are here
|
473
|
+
"rql:acq:123/456/567/685/374dd74329", # some other waiting process
|
474
|
+
]
|
475
|
+
|
476
|
+
# ... some period of time (2 seconds later)
|
477
|
+
# lock queue: =>
|
478
|
+
[
|
479
|
+
"rql:acq:123/456/567/677/374dd74322", # <- long living lock
|
480
|
+
"rql:acq:123/456/567/679/374dd74321",
|
481
|
+
"rql:acq:123/456/567/683/374dd74322", # <== we are here
|
482
|
+
"rql:acq:123/456/567/685/374dd74329", # some other waiting process
|
483
|
+
]
|
484
|
+
|
485
|
+
# ... some period of time (3 seconds later)
|
486
|
+
# ... queue_ttl time limit is reached
|
487
|
+
# lock queue: =>
|
488
|
+
[
|
489
|
+
"rql:acq:123/456/567/685/374dd74329", # some other waiting process
|
490
|
+
"rql:acq:123/456/567/683/374dd74322", # <== we are here (moved to the end of the queue)
|
491
|
+
]
|
492
|
+
|
493
|
+
```
|
494
|
+
|
432
495
|
---
|
433
496
|
|
434
497
|
#### #lock! - exceptional lock obtaining
|
@@ -539,9 +602,9 @@ rql.queue_info("your_lock_name")
|
|
539
602
|
{
|
540
603
|
"lock_queue" => "rql:lock_queue:your_lock_name",
|
541
604
|
"queue" => [
|
542
|
-
{ "acq_id" => "rql:acq:123/456/567/678/fa76df9cc2", "score" =>
|
543
|
-
{ "acq_id" => "rql:acq:123/567/456/679/c7bfcaf4f9", "score" =>
|
544
|
-
{ "acq_id" => "rql:acq:555/329/523/127/7329553b11", "score" =>
|
605
|
+
{ "acq_id" => "rql:acq:123/456/567/678/fa76df9cc2", "score" => 1711606640.540842},
|
606
|
+
{ "acq_id" => "rql:acq:123/567/456/679/c7bfcaf4f9", "score" => 1711606640.540906},
|
607
|
+
{ "acq_id" => "rql:acq:555/329/523/127/7329553b11", "score" => 1711606640.540963},
|
545
608
|
# ...etc
|
546
609
|
]
|
547
610
|
}
|
@@ -563,6 +626,8 @@ rql.locked?("your_lock_name") # => true/false
|
|
563
626
|
|
564
627
|
#### #queued?
|
565
628
|
|
629
|
+
<sup>\[[back to top](#usage)\]</sup>
|
630
|
+
|
566
631
|
- is the lock queued for obtain / has requests for obtain?
|
567
632
|
|
568
633
|
```ruby
|
@@ -611,7 +676,7 @@ rql.unlock("your_lock_name")
|
|
611
676
|
result: {
|
612
677
|
rel_time: 0.02, # time spent to lock release (in seconds)
|
613
678
|
rel_key: "rql:lock:your_lock_name", # released lock key
|
614
|
-
rel_queue: "rql:lock_queue:your_lock_name" # released lock key queue
|
679
|
+
rel_queue: "rql:lock_queue:your_lock_name", # released lock key queue
|
615
680
|
queue_res: :released, # or :nothing_to_release
|
616
681
|
lock_res: :released # or :nothing_to_release
|
617
682
|
}
|
@@ -638,12 +703,11 @@ rql.unlock("your_lock_name")
|
|
638
703
|
- pre-configured value in `config[:isntrumenter]`;
|
639
704
|
- `:instrument` - (optional) `[NilClass,Any]`
|
640
705
|
- custom instrumentation data wich will be passed to the instrumenter's payload with `:instrument` key;
|
641
|
-
|
642
706
|
- returns:
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
707
|
+
- `[Hash<Symbol,Numeric>]` - Format: `{ ok: true, result: Hash<Symbol,Numeric> }`;
|
708
|
+
- result data:
|
709
|
+
- `:rel_time` - `Numeric` - time spent to release all locks and related queus;
|
710
|
+
- `:rel_key_cnt` - `Integer` - the number of released Redis keys (queues+locks);
|
647
711
|
|
648
712
|
```ruby
|
649
713
|
rql.clear_locks
|
@@ -675,13 +739,14 @@ rql.clear_locks
|
|
675
739
|
- custom logger object;
|
676
740
|
- pre-configured in `config[:logger]`;
|
677
741
|
- returns `{ ok: true, result: :ttl_extended }` when ttl is extended;
|
678
|
-
- returns `{ ok: false, result: :async_expire_or_no_lock }` when lock not found or lock is expired during
|
742
|
+
- returns `{ ok: false, result: :async_expire_or_no_lock }` when a lock not found or a lock is already expired during
|
679
743
|
some steps of invocation (see **Important** section below);
|
680
744
|
- **Important**:
|
681
745
|
- the method is non-atomic cuz redis does not provide an atomic function for TTL/PTTL extension;
|
682
746
|
- the method consists of two commands:
|
683
747
|
- (1) read current pttl;
|
684
748
|
- (2) set new ttl that is calculated as "current pttl + additional milliseconds";
|
749
|
+
- the method uses Redis'es **CAS** (check-and-set) behavior;
|
685
750
|
- what can happen during these steps:
|
686
751
|
- lock is expired between commands or before the first command;
|
687
752
|
- lock is expired before the second command;
|
@@ -884,7 +949,7 @@ Accepts:
|
|
884
949
|
- has a preconfigured value in `config[:dead_request_ttl]` (1 day by default);
|
885
950
|
- `:sacn_size` - (optional) `[Integer]`
|
886
951
|
- the batch of scanned keys for Redis'es SCAN command;
|
887
|
-
- has a preconfigured valie in `config[:
|
952
|
+
- has a preconfigured valie in `config[:lock_release_batch_size]`;
|
888
953
|
- `:logger` - (optional) `[::Logger,#debug]`
|
889
954
|
- custom logger object;
|
890
955
|
- pre-configured in `config[:logger]`;
|
@@ -970,7 +1035,7 @@ By default `RedisQueuedLocks::Client` is configured with the void notifier (whic
|
|
970
1035
|
|
971
1036
|
### Instrumentation Events
|
972
1037
|
|
973
|
-
<sup>\[[back to top](#instrumentation
|
1038
|
+
<sup>\[[back to top](#instrumentation)\]</sup>
|
974
1039
|
|
975
1040
|
List of instrumentation events
|
976
1041
|
|
@@ -1028,6 +1093,7 @@ Detalized event semantics and payload structure:
|
|
1028
1093
|
<sup>\[[back to top](#table-of-contents)\]</sup>
|
1029
1094
|
|
1030
1095
|
- **strict redlock algorithm support** (support for many `RedisClient` instances);
|
1096
|
+
- deadlock detection (with some options for auto-resolving);
|
1031
1097
|
- Semantic Error objects for unexpected Redis errors;
|
1032
1098
|
- better specs with 100% test coverage (total rework);
|
1033
1099
|
- (non-`timed` locks): per-ruby-block-holding-the-lock sidecar `Ractor` and `in progress queue` in RedisDB that will extend
|
@@ -1035,6 +1101,7 @@ Detalized event semantics and payload structure:
|
|
1035
1101
|
whose ttl may expire before the block execution completes). It makes sense for non-`timed` locks *only*;
|
1036
1102
|
- lock request prioritization;
|
1037
1103
|
- support for LIFO strategy;
|
1104
|
+
- support for Random Access strategy;
|
1038
1105
|
- more structured logging (separated docs);
|
1039
1106
|
- `RedisQueuedLocks::Acquier::Try.try_to_lock` - detailed successful result analization;
|
1040
1107
|
- better code stylization (+ some refactorings);
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Acquier::AcquireLock::DelayExecution
|
6
6
|
# Sleep with random time-shifting (it is necessary for empty lock-acquirement time slots).
|
7
7
|
#
|
@@ -10,7 +10,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::DelayExecution
|
|
10
10
|
# @return [void]
|
11
11
|
#
|
12
12
|
# @api private
|
13
|
-
# @since
|
13
|
+
# @since 1.0.0
|
14
14
|
def delay_execution(retry_delay, retry_jitter)
|
15
15
|
delay = (retry_delay + rand(retry_jitter)).to_f / 1_000
|
16
16
|
sleep(delay)
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
# rubocop:disable Metrics/ModuleLength, Metrics/BlockNesting
|
6
6
|
module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
7
|
-
# @since
|
7
|
+
# @since 1.0.0
|
8
8
|
extend RedisQueuedLocks::Utilities
|
9
9
|
|
10
10
|
# @param redis [RedisClient]
|
@@ -21,7 +21,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
21
21
|
# @return [Hash<Symbol,Any>] Format: { ok: true/false, result: Symbol|Hash<Symbol,Any> }
|
22
22
|
#
|
23
23
|
# @api private
|
24
|
-
# @since
|
24
|
+
# @since 1.0.0
|
25
25
|
# rubocop:disable Metrics/MethodLength
|
26
26
|
def try_to_lock(
|
27
27
|
redis,
|
@@ -259,7 +259,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
259
259
|
# @return [Hash<Symbol,Any>] Format: { ok: true/false, result: Any }
|
260
260
|
#
|
261
261
|
# @api private
|
262
|
-
# @since
|
262
|
+
# @since 1.0.0
|
263
263
|
def dequeue_from_lock_queue(redis, logger, lock_key, lock_key_queue, queue_ttl, acquier_id)
|
264
264
|
result = redis.call('ZREM', lock_key_queue, acquier_id)
|
265
265
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Acquier::AcquireLock::WithAcqTimeout
|
6
6
|
# @param timeout [NilClass,Integer]
|
7
7
|
# Time period after which the logic will fail with timeout error.
|
@@ -14,7 +14,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::WithAcqTimeout
|
|
14
14
|
# @return [Any]
|
15
15
|
#
|
16
16
|
# @api private
|
17
|
-
# @since
|
17
|
+
# @since 1.0.0
|
18
18
|
def with_acq_timeout(timeout, lock_key, raise_errors, on_timeout: nil, &block)
|
19
19
|
::Timeout.timeout(timeout, &block)
|
20
20
|
rescue ::Timeout::Error
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Acquier::AcquireLock::YieldWithExpire
|
6
|
-
# @since
|
6
|
+
# @since 1.0.0
|
7
7
|
extend RedisQueuedLocks::Utilities
|
8
8
|
|
9
9
|
# @param redis [RedisClient] Redis connection manager.
|
@@ -18,7 +18,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldWithExpire
|
|
18
18
|
# @return [Any,NilClass] nil is returned no block parametr is provided.
|
19
19
|
#
|
20
20
|
# @api private
|
21
|
-
# @since
|
21
|
+
# @since 1.0.0
|
22
22
|
def yield_with_expire(
|
23
23
|
redis,
|
24
24
|
logger,
|
@@ -59,7 +59,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldWithExpire
|
|
59
59
|
# @return [Any]
|
60
60
|
#
|
61
61
|
# @api private
|
62
|
-
# @since
|
62
|
+
# @since 1.0.0
|
63
63
|
def yield_with_timeout(timeout, lock_key, lock_ttl, &block)
|
64
64
|
::Timeout.timeout(timeout, &block)
|
65
65
|
rescue ::Timeout::Error
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
# rubocop:disable Metrics/ModuleLength
|
6
6
|
# rubocop:disable Metrics/MethodLength
|
7
7
|
# rubocop:disable Metrics/ClassLength
|
@@ -12,21 +12,21 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
12
12
|
require_relative 'acquire_lock/yield_with_expire'
|
13
13
|
require_relative 'acquire_lock/try_to_lock'
|
14
14
|
|
15
|
-
# @since
|
15
|
+
# @since 1.0.0
|
16
16
|
extend TryToLock
|
17
|
-
# @since
|
17
|
+
# @since 1.0.0
|
18
18
|
extend DelayExecution
|
19
|
-
# @since
|
19
|
+
# @since 1.0.0
|
20
20
|
extend YieldWithExpire
|
21
|
-
# @since
|
21
|
+
# @since 1.0.0
|
22
22
|
extend WithAcqTimeout
|
23
|
-
# @since
|
23
|
+
# @since 1.0.0
|
24
24
|
extend RedisQueuedLocks::Utilities
|
25
25
|
|
26
26
|
# @return [Integer] Redis time error (in milliseconds).
|
27
27
|
#
|
28
28
|
# @api private
|
29
|
-
# @since
|
29
|
+
# @since 1.0.0
|
30
30
|
REDIS_TIMESHIFT_ERROR = 2
|
31
31
|
|
32
32
|
class << self
|
@@ -87,7 +87,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
87
87
|
# - If block is given the result of block's yeld will be returned.
|
88
88
|
#
|
89
89
|
# @api private
|
90
|
-
# @since
|
90
|
+
# @since 1.0.0
|
91
91
|
def acquire_lock(
|
92
92
|
redis,
|
93
93
|
lock_name,
|
@@ -186,7 +186,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
186
186
|
with_acq_timeout(timeout, lock_key, raise_errors, on_timeout: acq_dequeue) do
|
187
187
|
acq_start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
188
188
|
|
189
|
-
# Step 2.1:
|
189
|
+
# Step 2.1: cyclically try to obtain the lock
|
190
190
|
while acq_process[:should_try]
|
191
191
|
run_non_critical do
|
192
192
|
logger.debug do
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Acquier::ClearDeadRequests
|
6
6
|
class << self
|
7
7
|
# @param redis_client [RedisClient]
|
@@ -13,7 +13,7 @@ module RedisQueuedLocks::Acquier::ClearDeadRequests
|
|
13
13
|
# @return [Hash<Symbol,Boolean|Hash<Symbol,Set<String>>>]
|
14
14
|
#
|
15
15
|
# @api private
|
16
|
-
# @since
|
16
|
+
# @since 1.0.0
|
17
17
|
def clear_dead_requests(redis_client, scan_size, dead_ttl, logger, instrumenter, instrument)
|
18
18
|
dead_score = RedisQueuedLocks::Resource.acquier_dead_score(dead_ttl / 1000.0)
|
19
19
|
|
@@ -39,7 +39,7 @@ module RedisQueuedLocks::Acquier::ClearDeadRequests
|
|
39
39
|
# @return [Enumerator]
|
40
40
|
#
|
41
41
|
# @api private
|
42
|
-
# @since
|
42
|
+
# @since 1.0.0
|
43
43
|
def each_lock_queue(redis_client, scan_size, &block)
|
44
44
|
redis_client.scan(
|
45
45
|
'MATCH',
|
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Acquier::ExtendLockTTL
|
6
6
|
# @return [String]
|
7
7
|
#
|
8
8
|
# @api private
|
9
|
-
# @since
|
9
|
+
# @since 1.0.0
|
10
10
|
EXTEND_LOCK_PTTL = <<~LUA_SCRIPT.strip.tr("\n", '').freeze
|
11
11
|
local new_lock_pttl = redis.call("PTTL", KEYS[1]) + ARGV[1];
|
12
12
|
return redis.call("PEXPIRE", KEYS[1], new_lock_pttl);
|
@@ -20,7 +20,7 @@ module RedisQueuedLocks::Acquier::ExtendLockTTL
|
|
20
20
|
# @return [Hash<Symbol,Boolean|Symbol>]
|
21
21
|
#
|
22
22
|
# @api private
|
23
|
-
# @since
|
23
|
+
# @since 1.0.0
|
24
24
|
def extend_lock_ttl(redis_client, lock_name, milliseconds, logger)
|
25
25
|
lock_key = RedisQueuedLocks::Resource.prepare_lock_key(lock_name)
|
26
26
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Acquier::IsLocked
|
6
6
|
class << self
|
7
7
|
# @param redis_client [RedisClient]
|
@@ -9,7 +9,7 @@ module RedisQueuedLocks::Acquier::IsLocked
|
|
9
9
|
# @return [Boolean]
|
10
10
|
#
|
11
11
|
# @api private
|
12
|
-
# @since
|
12
|
+
# @since 1.0.0
|
13
13
|
def locked?(redis_client, lock_name)
|
14
14
|
lock_key = RedisQueuedLocks::Resource.prepare_lock_key(lock_name)
|
15
15
|
redis_client.call('EXISTS', lock_key) == 1
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Acquier::IsQueued
|
6
6
|
class << self
|
7
7
|
# @param redis_client [RedisClient]
|
@@ -9,7 +9,7 @@ module RedisQueuedLocks::Acquier::IsQueued
|
|
9
9
|
# @return [Boolean]
|
10
10
|
#
|
11
11
|
# @api private
|
12
|
-
# @since
|
12
|
+
# @since 1.0.0
|
13
13
|
def queued?(redis_client, lock_name)
|
14
14
|
lock_key_queue = RedisQueuedLocks::Resource.prepare_lock_queue(lock_name)
|
15
15
|
redis_client.call('EXISTS', lock_key_queue) == 1
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Acquier::Keys
|
6
6
|
class << self
|
7
7
|
# @param redis_client [RedisClient]
|
@@ -9,7 +9,7 @@ module RedisQueuedLocks::Acquier::Keys
|
|
9
9
|
# @return [Array<String>]
|
10
10
|
#
|
11
11
|
# @api private
|
12
|
-
# @since
|
12
|
+
# @since 1.0.0
|
13
13
|
def keys(redis_client, scan_size:)
|
14
14
|
Set.new.tap do |keys|
|
15
15
|
redis_client.scan(
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Acquier::LockInfo
|
6
6
|
class << self
|
7
7
|
# @param redis_client [RedisClient]
|
@@ -17,7 +17,7 @@ module RedisQueuedLocks::Acquier::LockInfo
|
|
17
17
|
# }
|
18
18
|
#
|
19
19
|
# @api private
|
20
|
-
# @since
|
20
|
+
# @since 1.0.0
|
21
21
|
def lock_info(redis_client, lock_name)
|
22
22
|
lock_key = RedisQueuedLocks::Resource.prepare_lock_key(lock_name)
|
23
23
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Acquier::Locks
|
6
6
|
class << self
|
7
7
|
# @param redis_client [RedisClient]
|
@@ -10,7 +10,7 @@ module RedisQueuedLocks::Acquier::Locks
|
|
10
10
|
# @return [Set<String>,Set<Hash<Symbol,Any>>]
|
11
11
|
#
|
12
12
|
# @api private
|
13
|
-
# @since
|
13
|
+
# @since 1.0.0
|
14
14
|
def locks(redis_client, scan_size:, with_info:)
|
15
15
|
redis_client.with do |rconn|
|
16
16
|
lock_keys = scan_locks(rconn, scan_size)
|
@@ -25,7 +25,7 @@ module RedisQueuedLocks::Acquier::Locks
|
|
25
25
|
# @return [Set<String>]
|
26
26
|
#
|
27
27
|
# @api private
|
28
|
-
# @since
|
28
|
+
# @since 1.0.0
|
29
29
|
def scan_locks(redis_client, scan_size)
|
30
30
|
Set.new.tap do |lock_keys|
|
31
31
|
redis_client.scan(
|
@@ -44,7 +44,7 @@ module RedisQueuedLocks::Acquier::Locks
|
|
44
44
|
# @return [Set<Hash<Symbol,Any>>]
|
45
45
|
#
|
46
46
|
# @api private
|
47
|
-
# @since
|
47
|
+
# @since 1.0.0
|
48
48
|
# rubocop:disable Metrics/MethodLength
|
49
49
|
def extract_locks_info(redis_client, lock_keys)
|
50
50
|
# TODO: refactor with RedisQueuedLocks::Acquier::LockInfo
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Acquier::QueueInfo
|
6
6
|
class << self
|
7
7
|
# Returns an information about the required lock queue by the lock name. The result
|
@@ -23,7 +23,7 @@ module RedisQueuedLocks::Acquier::QueueInfo
|
|
23
23
|
# }
|
24
24
|
#
|
25
25
|
# @api private
|
26
|
-
# @since
|
26
|
+
# @since 1.0.0
|
27
27
|
def queue_info(redis_client, lock_name)
|
28
28
|
lock_key_queue = RedisQueuedLocks::Resource.prepare_lock_queue(lock_name)
|
29
29
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Acquier::Queues
|
6
6
|
class << self
|
7
7
|
# @param redis_client [RedisClient]
|
@@ -10,7 +10,7 @@ module RedisQueuedLocks::Acquier::Queues
|
|
10
10
|
# @return [Set<String>,Set<Hash<Symbol,Any>>]
|
11
11
|
#
|
12
12
|
# @api private
|
13
|
-
# @since
|
13
|
+
# @since 1.0.0
|
14
14
|
def queues(redis_client, scan_size:, with_info:)
|
15
15
|
redis_client.with do |rconn|
|
16
16
|
lock_queues = scan_queues(rconn, scan_size)
|
@@ -25,7 +25,7 @@ module RedisQueuedLocks::Acquier::Queues
|
|
25
25
|
# @return [Set<String>]
|
26
26
|
#
|
27
27
|
# @api private
|
28
|
-
# @since
|
28
|
+
# @since 1.0.0
|
29
29
|
def scan_queues(redis_client, scan_size)
|
30
30
|
Set.new.tap do |lock_queues|
|
31
31
|
redis_client.scan(
|
@@ -44,7 +44,7 @@ module RedisQueuedLocks::Acquier::Queues
|
|
44
44
|
# @return [Set<Hash<Symbol,Any>>]
|
45
45
|
#
|
46
46
|
# @api private
|
47
|
-
# @since
|
47
|
+
# @since 1.0.0
|
48
48
|
def extract_queues_info(redis_client, lock_queues)
|
49
49
|
# TODO: refactor with RedisQueuedLocks::Acquier::QueueInfo
|
50
50
|
Set.new.tap do |seeded_queues|
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Acquier::ReleaseAllLocks
|
6
|
-
# @since
|
6
|
+
# @since 1.0.0
|
7
7
|
extend RedisQueuedLocks::Utilities
|
8
8
|
|
9
9
|
class << self
|
@@ -27,7 +27,7 @@ module RedisQueuedLocks::Acquier::ReleaseAllLocks
|
|
27
27
|
# Format: { ok: true, result: Hash<Symbol,Numeric> }
|
28
28
|
#
|
29
29
|
# @api private
|
30
|
-
# @since
|
30
|
+
# @since 1.0.0
|
31
31
|
def release_all_locks(redis, batch_size, logger, instrumenter, instrument)
|
32
32
|
rel_start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
33
33
|
fully_release_all_locks(redis, batch_size) => { ok:, result: }
|
@@ -59,7 +59,7 @@ module RedisQueuedLocks::Acquier::ReleaseAllLocks
|
|
59
59
|
# - Exmaple: { ok: true, result: { rel_key_cnt: 12345 } }
|
60
60
|
#
|
61
61
|
# @api private
|
62
|
-
# @since
|
62
|
+
# @since 1.0.0
|
63
63
|
def fully_release_all_locks(redis, batch_size)
|
64
64
|
result = redis.with do |rconn|
|
65
65
|
rconn.pipelined do |pipeline|
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Acquier::ReleaseLock
|
6
|
-
# @since
|
6
|
+
# @since 1.0.0
|
7
7
|
extend RedisQueuedLocks::Utilities
|
8
8
|
|
9
9
|
class << self
|
@@ -27,7 +27,7 @@ module RedisQueuedLocks::Acquier::ReleaseLock
|
|
27
27
|
# Format: { ok: true/false, result: Hash<Symbol,Numeric|String|Symbol> }
|
28
28
|
#
|
29
29
|
# @api private
|
30
|
-
# @since
|
30
|
+
# @since 1.0.0
|
31
31
|
def release_lock(redis, lock_name, instrumenter, logger)
|
32
32
|
lock_key = RedisQueuedLocks::Resource.prepare_lock_key(lock_name)
|
33
33
|
lock_key_queue = RedisQueuedLocks::Resource.prepare_lock_queue(lock_name)
|
@@ -76,7 +76,7 @@ module RedisQueuedLocks::Acquier::ReleaseLock
|
|
76
76
|
# }
|
77
77
|
#
|
78
78
|
# @api private
|
79
|
-
# @since
|
79
|
+
# @since 1.0.0
|
80
80
|
def fully_release_lock(redis, lock_key, lock_key_queue)
|
81
81
|
result = redis.with do |rconn|
|
82
82
|
rconn.multi do |transact|
|