redis_queued_locks 1.1.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 +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
|