redis_queued_locks 1.1.0 → 1.2.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 +7 -3
- data/README.md +58 -15
- 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 +8 -8
- 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 +21 -20
- 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 +2 -2
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,10 @@
|
|
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
|
+
|
3
8
|
## [1.1.0] - 2024-04-01
|
4
9
|
### Added
|
5
10
|
- Documentation updates:
|
@@ -7,8 +12,7 @@
|
|
7
12
|
- added missing docs for `config.dead_request_ttl`;
|
8
13
|
- some minor updates;
|
9
14
|
### Changed
|
10
|
-
- `#clear_dead_requests`: `:scan_size` is equal to `config[:lock_release_batch_size]` now (instead of to `config[:key_extraction_batch_size]`);
|
11
|
-
cuz `#clear_dead_requests` works with lock releasing;
|
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;
|
12
16
|
|
13
17
|
## [1.0.0] - 2024-04-01
|
14
18
|
- First Major Release;
|
@@ -195,7 +199,7 @@
|
|
195
199
|
### Changed
|
196
200
|
- The lock acquier identifier (`acq_id`) now includes the fiber id, the ractor id and an unique per-process
|
197
201
|
10 byte string. It is added in order to prevent collisions between different processes/pods
|
198
|
-
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
|
199
203
|
to the same position with the same `acq_id` for different processes/pods in the lock request queue.
|
200
204
|
|
201
205
|
## [0.0.8] - 2024-02-27
|
data/README.md
CHANGED
@@ -150,8 +150,8 @@ 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)
|
@@ -324,10 +324,12 @@ def lock(
|
|
324
324
|
- should be logged the each try of lock acquiring (a lot of logs can be generated depending on your retry configurations);
|
325
325
|
- pre-configured in `config[:log_lock_try]`;
|
326
326
|
- `false` by default;
|
327
|
-
- `block` - `[Block]`
|
327
|
+
- `block` - (optional) `[Block]`
|
328
328
|
- A block of code that should be executed after the successfully acquired lock.
|
329
329
|
- If block is **passed** the obtained lock will be released after the block execution or it's ttl (what will happen first);
|
330
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) { ... }`)
|
331
333
|
|
332
334
|
Return value:
|
333
335
|
|
@@ -453,6 +455,43 @@ rql.lock_info("my_lock")
|
|
453
455
|
}
|
454
456
|
```
|
455
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
|
+
|
456
495
|
---
|
457
496
|
|
458
497
|
#### #lock! - exceptional lock obtaining
|
@@ -563,9 +602,9 @@ rql.queue_info("your_lock_name")
|
|
563
602
|
{
|
564
603
|
"lock_queue" => "rql:lock_queue:your_lock_name",
|
565
604
|
"queue" => [
|
566
|
-
{ "acq_id" => "rql:acq:123/456/567/678/fa76df9cc2", "score" =>
|
567
|
-
{ "acq_id" => "rql:acq:123/567/456/679/c7bfcaf4f9", "score" =>
|
568
|
-
{ "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},
|
569
608
|
# ...etc
|
570
609
|
]
|
571
610
|
}
|
@@ -587,6 +626,8 @@ rql.locked?("your_lock_name") # => true/false
|
|
587
626
|
|
588
627
|
#### #queued?
|
589
628
|
|
629
|
+
<sup>\[[back to top](#usage)\]</sup>
|
630
|
+
|
590
631
|
- is the lock queued for obtain / has requests for obtain?
|
591
632
|
|
592
633
|
```ruby
|
@@ -635,7 +676,7 @@ rql.unlock("your_lock_name")
|
|
635
676
|
result: {
|
636
677
|
rel_time: 0.02, # time spent to lock release (in seconds)
|
637
678
|
rel_key: "rql:lock:your_lock_name", # released lock key
|
638
|
-
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
|
639
680
|
queue_res: :released, # or :nothing_to_release
|
640
681
|
lock_res: :released # or :nothing_to_release
|
641
682
|
}
|
@@ -662,12 +703,11 @@ rql.unlock("your_lock_name")
|
|
662
703
|
- pre-configured value in `config[:isntrumenter]`;
|
663
704
|
- `:instrument` - (optional) `[NilClass,Any]`
|
664
705
|
- custom instrumentation data wich will be passed to the instrumenter's payload with `:instrument` key;
|
665
|
-
|
666
706
|
- returns:
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
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);
|
671
711
|
|
672
712
|
```ruby
|
673
713
|
rql.clear_locks
|
@@ -699,13 +739,14 @@ rql.clear_locks
|
|
699
739
|
- custom logger object;
|
700
740
|
- pre-configured in `config[:logger]`;
|
701
741
|
- returns `{ ok: true, result: :ttl_extended }` when ttl is extended;
|
702
|
-
- 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
|
703
743
|
some steps of invocation (see **Important** section below);
|
704
744
|
- **Important**:
|
705
745
|
- the method is non-atomic cuz redis does not provide an atomic function for TTL/PTTL extension;
|
706
746
|
- the method consists of two commands:
|
707
747
|
- (1) read current pttl;
|
708
748
|
- (2) set new ttl that is calculated as "current pttl + additional milliseconds";
|
749
|
+
- the method uses Redis'es **CAS** (check-and-set) behavior;
|
709
750
|
- what can happen during these steps:
|
710
751
|
- lock is expired between commands or before the first command;
|
711
752
|
- lock is expired before the second command;
|
@@ -908,7 +949,7 @@ Accepts:
|
|
908
949
|
- has a preconfigured value in `config[:dead_request_ttl]` (1 day by default);
|
909
950
|
- `:sacn_size` - (optional) `[Integer]`
|
910
951
|
- the batch of scanned keys for Redis'es SCAN command;
|
911
|
-
- has a preconfigured valie in `config[:
|
952
|
+
- has a preconfigured valie in `config[:lock_release_batch_size]`;
|
912
953
|
- `:logger` - (optional) `[::Logger,#debug]`
|
913
954
|
- custom logger object;
|
914
955
|
- pre-configured in `config[:logger]`;
|
@@ -994,7 +1035,7 @@ By default `RedisQueuedLocks::Client` is configured with the void notifier (whic
|
|
994
1035
|
|
995
1036
|
### Instrumentation Events
|
996
1037
|
|
997
|
-
<sup>\[[back to top](#instrumentation
|
1038
|
+
<sup>\[[back to top](#instrumentation)\]</sup>
|
998
1039
|
|
999
1040
|
List of instrumentation events
|
1000
1041
|
|
@@ -1052,6 +1093,7 @@ Detalized event semantics and payload structure:
|
|
1052
1093
|
<sup>\[[back to top](#table-of-contents)\]</sup>
|
1053
1094
|
|
1054
1095
|
- **strict redlock algorithm support** (support for many `RedisClient` instances);
|
1096
|
+
- deadlock detection (with some options for auto-resolving);
|
1055
1097
|
- Semantic Error objects for unexpected Redis errors;
|
1056
1098
|
- better specs with 100% test coverage (total rework);
|
1057
1099
|
- (non-`timed` locks): per-ruby-block-holding-the-lock sidecar `Ractor` and `in progress queue` in RedisDB that will extend
|
@@ -1059,6 +1101,7 @@ Detalized event semantics and payload structure:
|
|
1059
1101
|
whose ttl may expire before the block execution completes). It makes sense for non-`timed` locks *only*;
|
1060
1102
|
- lock request prioritization;
|
1061
1103
|
- support for LIFO strategy;
|
1104
|
+
- support for Random Access strategy;
|
1062
1105
|
- more structured logging (separated docs);
|
1063
1106
|
- `RedisQueuedLocks::Acquier::Try.try_to_lock` - detailed successful result analization;
|
1064
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,
|
@@ -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|
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api public
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
# rubocop:disable Metrics/ClassLength
|
6
6
|
class RedisQueuedLocks::Client
|
7
|
-
# @since
|
7
|
+
# @since 1.0.0
|
8
8
|
include Qonfig::Configurable
|
9
9
|
|
10
10
|
configuration do
|
@@ -41,7 +41,7 @@ class RedisQueuedLocks::Client
|
|
41
41
|
# @return [RedisClient]
|
42
42
|
#
|
43
43
|
# @api private
|
44
|
-
# @since
|
44
|
+
# @since 1.0.0
|
45
45
|
attr_reader :redis_client
|
46
46
|
|
47
47
|
# NOTE: attr_access here is chosen intentionally in order to have an ability to change
|
@@ -49,7 +49,7 @@ class RedisQueuedLocks::Client
|
|
49
49
|
# @return [String]
|
50
50
|
#
|
51
51
|
# @api private
|
52
|
-
# @since
|
52
|
+
# @since 1.0.0
|
53
53
|
attr_accessor :uniq_identity
|
54
54
|
|
55
55
|
# @param redis_client [RedisClient]
|
@@ -60,7 +60,7 @@ class RedisQueuedLocks::Client
|
|
60
60
|
# @return [void]
|
61
61
|
#
|
62
62
|
# @api public
|
63
|
-
# @since
|
63
|
+
# @since 1.0.0
|
64
64
|
def initialize(redis_client, &configs)
|
65
65
|
configure(&configs)
|
66
66
|
@uniq_identity = config[:uniq_identifier].call
|
@@ -116,7 +116,7 @@ class RedisQueuedLocks::Client
|
|
116
116
|
# - If block is given the result of block's yeld will be returned.
|
117
117
|
#
|
118
118
|
# @api public
|
119
|
-
# @since
|
119
|
+
# @since 1.0.0
|
120
120
|
def lock(
|
121
121
|
lock_name,
|
122
122
|
ttl: config[:default_lock_ttl],
|
@@ -164,7 +164,7 @@ class RedisQueuedLocks::Client
|
|
164
164
|
# @note See #lock method signature.
|
165
165
|
#
|
166
166
|
# @api public
|
167
|
-
# @since
|
167
|
+
# @since 1.0.0
|
168
168
|
def lock!(
|
169
169
|
lock_name,
|
170
170
|
ttl: config[:default_lock_ttl],
|
@@ -219,7 +219,7 @@ class RedisQueuedLocks::Client
|
|
219
219
|
# }
|
220
220
|
#
|
221
221
|
# @api public
|
222
|
-
# @since
|
222
|
+
# @since 1.0.0
|
223
223
|
def unlock(
|
224
224
|
lock_name,
|
225
225
|
logger: config[:logger],
|
@@ -238,7 +238,7 @@ class RedisQueuedLocks::Client
|
|
238
238
|
# @return [Boolean]
|
239
239
|
#
|
240
240
|
# @api public
|
241
|
-
# @since
|
241
|
+
# @since 1.0.0
|
242
242
|
def locked?(lock_name)
|
243
243
|
RedisQueuedLocks::Acquier::IsLocked.locked?(redis_client, lock_name)
|
244
244
|
end
|
@@ -247,7 +247,7 @@ class RedisQueuedLocks::Client
|
|
247
247
|
# @return [Boolean]
|
248
248
|
#
|
249
249
|
# @api public
|
250
|
-
# @since
|
250
|
+
# @since 1.0.0
|
251
251
|
def queued?(lock_name)
|
252
252
|
RedisQueuedLocks::Acquier::IsQueued.queued?(redis_client, lock_name)
|
253
253
|
end
|
@@ -256,7 +256,7 @@ class RedisQueuedLocks::Client
|
|
256
256
|
# @return [Hash<String,String|Numeric>,NilClass]
|
257
257
|
#
|
258
258
|
# @api public
|
259
|
-
# @since
|
259
|
+
# @since 1.0.0
|
260
260
|
def lock_info(lock_name)
|
261
261
|
RedisQueuedLocks::Acquier::LockInfo.lock_info(redis_client, lock_name)
|
262
262
|
end
|
@@ -265,7 +265,7 @@ class RedisQueuedLocks::Client
|
|
265
265
|
# @return [Hash<String|Array<Hash<String,String|Numeric>>,NilClass]
|
266
266
|
#
|
267
267
|
# @api public
|
268
|
-
# @since
|
268
|
+
# @since 1.0.0
|
269
269
|
def queue_info(lock_name)
|
270
270
|
RedisQueuedLocks::Acquier::QueueInfo.queue_info(redis_client, lock_name)
|
271
271
|
end
|
@@ -288,7 +288,7 @@ class RedisQueuedLocks::Client
|
|
288
288
|
# - { ok: false, result: :async_expire_or_no_lock }
|
289
289
|
#
|
290
290
|
# @api public
|
291
|
-
# @since
|
291
|
+
# @since 1.0.0
|
292
292
|
def extend_lock_ttl(lock_name, milliseconds, logger: config[:logger])
|
293
293
|
RedisQueuedLocks::Acquier::ExtendLockTTL.extend_lock_ttl(
|
294
294
|
redis_client,
|
@@ -311,7 +311,7 @@ class RedisQueuedLocks::Client
|
|
311
311
|
# Example: { ok: true, result { rel_key_cnt: 100, rel_time: 0.01 } }
|
312
312
|
#
|
313
313
|
# @api public
|
314
|
-
# @since
|
314
|
+
# @since 1.0.0
|
315
315
|
def clear_locks(
|
316
316
|
batch_size: config[:lock_release_batch_size],
|
317
317
|
logger: config[:logger],
|
@@ -345,7 +345,7 @@ class RedisQueuedLocks::Client
|
|
345
345
|
# @return [Set<String>,Set<Hash<Symbol,Any>>]
|
346
346
|
#
|
347
347
|
# @api public
|
348
|
-
# @since
|
348
|
+
# @since 1.0.0
|
349
349
|
def locks(scan_size: config[:key_extraction_batch_size], with_info: false)
|
350
350
|
RedisQueuedLocks::Acquier::Locks.locks(redis_client, scan_size:, with_info:)
|
351
351
|
end
|
@@ -356,7 +356,7 @@ class RedisQueuedLocks::Client
|
|
356
356
|
# @return [Set<Hash<String,Any>>]
|
357
357
|
#
|
358
358
|
# @api public
|
359
|
-
# @since
|
359
|
+
# @since 1.0.0
|
360
360
|
def locks_info(scan_size: config[:key_extraction_batch_size])
|
361
361
|
locks(scan_size:, with_info: true)
|
362
362
|
end
|
@@ -376,7 +376,7 @@ class RedisQueuedLocks::Client
|
|
376
376
|
# @return [Set<String>,String<Hash<Symbol,Any>>]
|
377
377
|
#
|
378
378
|
# @api public
|
379
|
-
# @since
|
379
|
+
# @since 1.0.0
|
380
380
|
def queues(scan_size: config[:key_extraction_batch_size], with_info: false)
|
381
381
|
RedisQueuedLocks::Acquier::Queues.queues(redis_client, scan_size:, with_info:)
|
382
382
|
end
|
@@ -387,7 +387,7 @@ class RedisQueuedLocks::Client
|
|
387
387
|
# @return [Set<Hash<Symbol,Any>>]
|
388
388
|
#
|
389
389
|
# @api public
|
390
|
-
# @since
|
390
|
+
# @since 1.0.0
|
391
391
|
def queues_info(scan_size: config[:key_extraction_batch_size])
|
392
392
|
queues(scan_size:, with_info: true)
|
393
393
|
end
|
@@ -396,7 +396,7 @@ class RedisQueuedLocks::Client
|
|
396
396
|
# @return [Set<String>]
|
397
397
|
#
|
398
398
|
# @api public
|
399
|
-
# @since
|
399
|
+
# @since 1.0.0
|
400
400
|
def keys(scan_size: config[:key_extraction_batch_size])
|
401
401
|
RedisQueuedLocks::Acquier::Keys.keys(redis_client, scan_size:)
|
402
402
|
end
|
@@ -415,7 +415,8 @@ class RedisQueuedLocks::Client
|
|
415
415
|
# Format: { ok: true, result: { processed_queus: Set<String> } }
|
416
416
|
#
|
417
417
|
# @api public
|
418
|
-
# @since
|
418
|
+
# @since 1.0.0
|
419
|
+
# @version 1.1.0
|
419
420
|
def clear_dead_requests(
|
420
421
|
dead_ttl: config[:dead_request_ttl],
|
421
422
|
scan_size: config[:lock_release_batch_size],
|
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Debugger::Interface
|
6
6
|
# @param message [String]
|
7
7
|
# @return [void]
|
8
8
|
#
|
9
9
|
# @api private
|
10
|
-
# @since
|
10
|
+
# @since 1.0.0
|
11
11
|
def debug(message)
|
12
12
|
RedisQueuedLocks::Debugger.debug(message)
|
13
13
|
end
|
@@ -15,7 +15,7 @@ module RedisQueuedLocks::Debugger::Interface
|
|
15
15
|
# @return [void]
|
16
16
|
#
|
17
17
|
# @api private
|
18
|
-
# @since
|
18
|
+
# @since 1.0.0
|
19
19
|
def enable_debugger!
|
20
20
|
RedisQueuedLocks::Debugger.enable!
|
21
21
|
end
|
@@ -23,7 +23,7 @@ module RedisQueuedLocks::Debugger::Interface
|
|
23
23
|
# @return [void]
|
24
24
|
#
|
25
25
|
# @api private
|
26
|
-
# @since
|
26
|
+
# @since 1.0.0
|
27
27
|
def disable_debugger!
|
28
28
|
RedisQueuedLocks::Debugger.disable!
|
29
29
|
end
|
@@ -1,14 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Debugger
|
6
6
|
require_relative 'debugger/interface'
|
7
7
|
|
8
8
|
# @return [String]
|
9
9
|
#
|
10
10
|
# @api private
|
11
|
-
# @since
|
11
|
+
# @since 1.0.0
|
12
12
|
DEBUG_ENABLED_METHOD = <<~METHOD_DECLARATION.strip.freeze
|
13
13
|
def debug(message) = STDOUT.write("#\{message}\n")
|
14
14
|
METHOD_DECLARATION
|
@@ -16,20 +16,20 @@ module RedisQueuedLocks::Debugger
|
|
16
16
|
# @return [String]
|
17
17
|
#
|
18
18
|
# @api private
|
19
|
-
# @since
|
19
|
+
# @since 1.0.0
|
20
20
|
DEBUG_DISABLED_MEHTOD = <<~METHOD_DECLARATION.strip.freeze
|
21
21
|
def debug(message); end
|
22
22
|
METHOD_DECLARATION
|
23
23
|
|
24
24
|
class << self
|
25
25
|
# @api private
|
26
|
-
# @since
|
26
|
+
# @since 1.0.0
|
27
27
|
instance_variable_set(:@enabled, false)
|
28
28
|
|
29
29
|
# @return [void]
|
30
30
|
#
|
31
31
|
# @api private
|
32
|
-
# @since
|
32
|
+
# @since 1.0.0
|
33
33
|
def enable!
|
34
34
|
@enabled = true
|
35
35
|
eval(DEBUG_ENABLED_METHOD)
|
@@ -38,7 +38,7 @@ module RedisQueuedLocks::Debugger
|
|
38
38
|
# @return [void]
|
39
39
|
#
|
40
40
|
# @api private
|
41
|
-
# @since
|
41
|
+
# @since 1.0.0
|
42
42
|
def disable!
|
43
43
|
@enabled = false
|
44
44
|
eval(DEBUG_DISABLED_MEHTOD)
|
@@ -47,7 +47,7 @@ module RedisQueuedLocks::Debugger
|
|
47
47
|
# @return [Boolean]
|
48
48
|
#
|
49
49
|
# @api private
|
50
|
-
# @since
|
50
|
+
# @since 1.0.0
|
51
51
|
def enabled?
|
52
52
|
@enabled
|
53
53
|
end
|
@@ -56,7 +56,7 @@ module RedisQueuedLocks::Debugger
|
|
56
56
|
# @return [void]
|
57
57
|
#
|
58
58
|
# @api private
|
59
|
-
# @since
|
59
|
+
# @since 1.0.0
|
60
60
|
def debug(message); end
|
61
61
|
end
|
62
62
|
end
|
@@ -2,26 +2,26 @@
|
|
2
2
|
|
3
3
|
module RedisQueuedLocks
|
4
4
|
# @api public
|
5
|
-
# @since
|
5
|
+
# @since 1.0.0
|
6
6
|
Error = Class.new(::StandardError)
|
7
7
|
|
8
8
|
# @api public
|
9
|
-
# @since
|
9
|
+
# @since 1.0.0
|
10
10
|
ArgumentError = Class.new(::ArgumentError)
|
11
11
|
|
12
12
|
# @api public
|
13
|
-
# @since
|
13
|
+
# @since 1.0.0
|
14
14
|
LockAlreadyObtainedError = Class.new(Error)
|
15
15
|
|
16
16
|
# @api public
|
17
|
-
# @since
|
17
|
+
# @since 1.0.0
|
18
18
|
LockAcquiermentTimeoutError = Class.new(Error)
|
19
19
|
|
20
20
|
# @api public
|
21
|
-
# @since
|
21
|
+
# @since 1.0.0
|
22
22
|
LockAcquiermentRetryLimitError = Class.new(Error)
|
23
23
|
|
24
24
|
# @api pulic
|
25
|
-
# @since
|
25
|
+
# @since 1.0.0
|
26
26
|
TimedLockTimeoutError = Class.new(Error)
|
27
27
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api public
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Instrument::ActiveSupport
|
6
6
|
class << self
|
7
7
|
# @param event [String]
|
@@ -9,7 +9,7 @@ module RedisQueuedLocks::Instrument::ActiveSupport
|
|
9
9
|
# @return [void]
|
10
10
|
#
|
11
11
|
# @api public
|
12
|
-
# @since
|
12
|
+
# @since 1.0.0
|
13
13
|
def notify(event, payload = {})
|
14
14
|
::ActiveSupport::Notifications.instrument(event, payload)
|
15
15
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api public
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Instrument::VoidNotifier
|
6
6
|
class << self
|
7
7
|
# @param event [String]
|
@@ -9,7 +9,7 @@ module RedisQueuedLocks::Instrument::VoidNotifier
|
|
9
9
|
# @return [void]
|
10
10
|
#
|
11
11
|
# @api public
|
12
|
-
# @since
|
12
|
+
# @since 1.0.0
|
13
13
|
def notify(event, payload = {}); end
|
14
14
|
end
|
15
15
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api public
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Instrument
|
6
6
|
require_relative 'instrument/void_notifier'
|
7
7
|
require_relative 'instrument/active_support'
|
@@ -11,7 +11,7 @@ module RedisQueuedLocks::Instrument
|
|
11
11
|
# @return [Boolean]
|
12
12
|
#
|
13
13
|
# @api public
|
14
|
-
# @since
|
14
|
+
# @since 1.0.0
|
15
15
|
def valid_interface?(instrumenter)
|
16
16
|
if instrumenter == RedisQueuedLocks::Instrument::ActiveSupport
|
17
17
|
# NOTE: active_support should be required in your app
|
@@ -1,43 +1,43 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api public
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Logging::VoidLogger
|
6
6
|
class << self
|
7
7
|
# @api public
|
8
|
-
# @since
|
8
|
+
# @since 1.0.0
|
9
9
|
def warn(progname = nil, &block); end
|
10
10
|
|
11
11
|
# @api public
|
12
|
-
# @since
|
12
|
+
# @since 1.0.0
|
13
13
|
def unknown(progname = nil, &block); end
|
14
14
|
|
15
15
|
# @api public
|
16
|
-
# @since
|
16
|
+
# @since 1.0.0
|
17
17
|
def log(progname = nil, &block); end
|
18
18
|
|
19
19
|
# @api public
|
20
|
-
# @since
|
20
|
+
# @since 1.0.0
|
21
21
|
def info(progname = nil, &block); end
|
22
22
|
|
23
23
|
# @api public
|
24
|
-
# @since
|
24
|
+
# @since 1.0.0
|
25
25
|
def error(progname = nil, &block); end
|
26
26
|
|
27
27
|
# @api public
|
28
|
-
# @since
|
28
|
+
# @since 1.0.0
|
29
29
|
def fatal(progname = nil, &block); end
|
30
30
|
|
31
31
|
# @api public
|
32
|
-
# @since
|
32
|
+
# @since 1.0.0
|
33
33
|
def debug(progname = nil, &block); end
|
34
34
|
|
35
35
|
# @api public
|
36
|
-
# @since
|
36
|
+
# @since 1.0.0
|
37
37
|
def add(*, &block); end
|
38
38
|
|
39
39
|
# @api public
|
40
|
-
# @since
|
40
|
+
# @since 1.0.0
|
41
41
|
def <<(message); end
|
42
42
|
end
|
43
43
|
end
|
@@ -1,19 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api public
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Logging
|
6
6
|
require_relative 'logging/void_logger'
|
7
7
|
|
8
8
|
class << self
|
9
|
-
# @param logger [::Logger,#debug]
|
9
|
+
# @param logger [::Logger,#debug,::SemanticLogger::Logger]
|
10
10
|
# @return [Boolean]
|
11
11
|
#
|
12
12
|
# @api public
|
13
|
-
# @since
|
13
|
+
# @since 1.0.0
|
14
|
+
# @version 1.2.0
|
14
15
|
def valid_interface?(logger)
|
15
16
|
return true if logger.is_a?(::Logger)
|
16
17
|
|
18
|
+
# NOTE:
|
19
|
+
# - convinient/conventional way to support the popular`semantic_logger` library
|
20
|
+
# - see https://logger.rocketjob.io/
|
21
|
+
# - see https://github.com/reidmorrison/semantic_logger
|
22
|
+
return true if defined?(::SemanticLogger::Logger) && logger.is_a?(::SemanticLogger::Logger)
|
23
|
+
|
17
24
|
# NOTE: should provide `#debug` method.
|
18
25
|
return false unless logger.respond_to?(:debug)
|
19
26
|
|
@@ -1,24 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
# @since
|
4
|
+
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Resource
|
6
6
|
# @return [String]
|
7
7
|
#
|
8
8
|
# @api private
|
9
|
-
# @since
|
9
|
+
# @since 1.0.0
|
10
10
|
KEY_PATTERN = 'rql:lock*'
|
11
11
|
|
12
12
|
# @return [String]
|
13
13
|
#
|
14
14
|
# @api private
|
15
|
-
# @since
|
15
|
+
# @since 1.0.0
|
16
16
|
LOCK_PATTERN = 'rql:lock:*'
|
17
17
|
|
18
18
|
# @return [String]
|
19
19
|
#
|
20
20
|
# @api private
|
21
|
-
# @since
|
21
|
+
# @since 1.0.0
|
22
22
|
LOCK_QUEUE_PATTERN = 'rql:lock_queue:*'
|
23
23
|
|
24
24
|
class << self
|
@@ -29,7 +29,7 @@ module RedisQueuedLocks::Resource
|
|
29
29
|
# @return [String]
|
30
30
|
#
|
31
31
|
# @api private
|
32
|
-
# @since
|
32
|
+
# @since 1.0.0
|
33
33
|
def calc_uniq_identity
|
34
34
|
SecureRandom.hex(8)
|
35
35
|
end
|
@@ -42,7 +42,7 @@ module RedisQueuedLocks::Resource
|
|
42
42
|
# @return [String]
|
43
43
|
#
|
44
44
|
# @api private
|
45
|
-
# @since
|
45
|
+
# @since 1.0.0
|
46
46
|
def acquier_identifier(process_id, thread_id, fiber_id, ractor_id, identity)
|
47
47
|
"rql:acq:#{process_id}/#{thread_id}/#{fiber_id}/#{ractor_id}/#{identity}"
|
48
48
|
end
|
@@ -51,7 +51,7 @@ module RedisQueuedLocks::Resource
|
|
51
51
|
# @return [String]
|
52
52
|
#
|
53
53
|
# @api private
|
54
|
-
# @since
|
54
|
+
# @since 1.0.0
|
55
55
|
def prepare_lock_key(lock_name)
|
56
56
|
"rql:lock:#{lock_name}"
|
57
57
|
end
|
@@ -60,7 +60,7 @@ module RedisQueuedLocks::Resource
|
|
60
60
|
# @return [String]
|
61
61
|
#
|
62
62
|
# @api private
|
63
|
-
# @since
|
63
|
+
# @since 1.0.0
|
64
64
|
def prepare_lock_queue(lock_name)
|
65
65
|
"rql:lock_queue:#{lock_name}"
|
66
66
|
end
|
@@ -68,7 +68,7 @@ module RedisQueuedLocks::Resource
|
|
68
68
|
# @return [Float] Redis's <Set> score that is calculated from the time (epoch) as a float.
|
69
69
|
#
|
70
70
|
# @api private
|
71
|
-
# @since
|
71
|
+
# @since 1.0.0
|
72
72
|
def calc_initial_acquier_position
|
73
73
|
Time.now.to_f
|
74
74
|
end
|
@@ -77,7 +77,7 @@ module RedisQueuedLocks::Resource
|
|
77
77
|
# @return [Float] Redis's <Set> score barrier for acquiers that should be removed from queue.
|
78
78
|
#
|
79
79
|
# @api private
|
80
|
-
# @since
|
80
|
+
# @since 1.0.0
|
81
81
|
def acquier_dead_score(queue_ttl)
|
82
82
|
Time.now.to_f - queue_ttl
|
83
83
|
end
|
@@ -91,7 +91,7 @@ module RedisQueuedLocks::Resource
|
|
91
91
|
# Is the lock request time limit has reached or not.
|
92
92
|
#
|
93
93
|
# @api private
|
94
|
-
# @since
|
94
|
+
# @since 1.0.0
|
95
95
|
def dead_score_reached?(acquier_position, queue_ttl)
|
96
96
|
(acquier_position + queue_ttl) < Time.now.to_f
|
97
97
|
end
|
@@ -100,7 +100,7 @@ module RedisQueuedLocks::Resource
|
|
100
100
|
# @return [String]
|
101
101
|
#
|
102
102
|
# @api private
|
103
|
-
# @since
|
103
|
+
# @since 1.0.0
|
104
104
|
def lock_key_from_queue(lock_queue)
|
105
105
|
# NOTE: 15 is a start position of the lock name
|
106
106
|
"rql:lock:#{lock_queue[15..]}"
|
@@ -109,7 +109,7 @@ module RedisQueuedLocks::Resource
|
|
109
109
|
# @return [Integer]
|
110
110
|
#
|
111
111
|
# @api private
|
112
|
-
# @since
|
112
|
+
# @since 1.0.0
|
113
113
|
def get_thread_id
|
114
114
|
::Thread.current.object_id
|
115
115
|
end
|
@@ -117,7 +117,7 @@ module RedisQueuedLocks::Resource
|
|
117
117
|
# @return [Integer]
|
118
118
|
#
|
119
119
|
# @api private
|
120
|
-
# @since
|
120
|
+
# @since 1.0.0
|
121
121
|
def get_fiber_id
|
122
122
|
::Fiber.current.object_id
|
123
123
|
end
|
@@ -125,7 +125,7 @@ module RedisQueuedLocks::Resource
|
|
125
125
|
# @return [Integer]
|
126
126
|
#
|
127
127
|
# @api private
|
128
|
-
# @since
|
128
|
+
# @since 1.0.0
|
129
129
|
def get_ractor_id
|
130
130
|
::Ractor.current.object_id
|
131
131
|
end
|
@@ -133,7 +133,7 @@ module RedisQueuedLocks::Resource
|
|
133
133
|
# @return [Integer]
|
134
134
|
#
|
135
135
|
# @api private
|
136
|
-
# @since
|
136
|
+
# @since 1.0.0
|
137
137
|
def get_process_id
|
138
138
|
::Process.pid
|
139
139
|
end
|
@@ -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::Utilities
|
6
6
|
module_function
|
7
7
|
|
@@ -9,7 +9,7 @@ module RedisQueuedLocks::Utilities
|
|
9
9
|
# @return [Any]
|
10
10
|
#
|
11
11
|
# @api private
|
12
|
-
# @since
|
12
|
+
# @since 1.0.0
|
13
13
|
def run_non_critical(&block)
|
14
14
|
yield rescue nil
|
15
15
|
end
|
data/lib/redis_queued_locks.rb
CHANGED
@@ -7,7 +7,7 @@ require 'securerandom'
|
|
7
7
|
require 'logger'
|
8
8
|
|
9
9
|
# @api public
|
10
|
-
# @since
|
10
|
+
# @since 1.0.0
|
11
11
|
module RedisQueuedLocks
|
12
12
|
require_relative 'redis_queued_locks/version'
|
13
13
|
require_relative 'redis_queued_locks/errors'
|
@@ -20,6 +20,6 @@ module RedisQueuedLocks
|
|
20
20
|
require_relative 'redis_queued_locks/instrument'
|
21
21
|
require_relative 'redis_queued_locks/client'
|
22
22
|
|
23
|
-
# @since
|
23
|
+
# @since 1.0.0
|
24
24
|
extend RedisQueuedLocks::Debugger::Interface
|
25
25
|
end
|
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.2.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-04-
|
11
|
+
date: 2024-04-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis-client
|