redis_queued_locks 1.15.1 → 1.16.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 +11 -1
- data/README.md +74 -0
- data/lib/redis_queued_locks/acquirer/acquire_lock/instr_visitor.rb +24 -1
- data/lib/redis_queued_locks/acquirer/acquire_lock/log_visitor.rb +51 -1
- data/lib/redis_queued_locks/acquirer/acquire_lock/with_acq_timeout.rb +2 -1
- data/lib/redis_queued_locks/acquirer/acquire_lock/yield_expire/log_visitor.rb +25 -1
- data/lib/redis_queued_locks/acquirer/acquire_lock/yield_expire.rb +3 -2
- data/lib/redis_queued_locks/acquirer/acquire_lock.rb +3 -3
- data/lib/redis_queued_locks/acquirer/lock_series_poc.rb +303 -0
- data/lib/redis_queued_locks/acquirer/queues.rb +1 -1
- data/lib/redis_queued_locks/acquirer/release_all_locks.rb +1 -1
- data/lib/redis_queued_locks/acquirer/release_lock.rb +1 -1
- data/lib/redis_queued_locks/acquirer/release_locks_of.rb +1 -1
- data/lib/redis_queued_locks/acquirer.rb +1 -0
- data/lib/redis_queued_locks/client.rb +149 -5
- data/lib/redis_queued_locks/config/dsl.rb +2 -2
- data/lib/redis_queued_locks/logging/void_logger.rb +17 -9
- data/lib/redis_queued_locks/logging.rb +0 -2
- data/lib/redis_queued_locks/resource.rb +1 -1
- data/lib/redis_queued_locks/swarm/flush_zombies.rb +1 -1
- data/lib/redis_queued_locks/swarm/swarm_element/isolated.rb +6 -3
- data/lib/redis_queued_locks/utilities/lock.rb +2 -1
- data/lib/redis_queued_locks/utilities.rb +17 -2
- data/lib/redis_queued_locks/version.rb +2 -2
- data/sig/redis_queued_locks/utilities.rbs +2 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 38fff24c73bc7a295a40e9cf9bd2b83ab59c9881690992ff8f697f01332423e6
|
|
4
|
+
data.tar.gz: abd042d5ef25b0d8745d4551ace657fbf622aab09afbc6a2162a8fea9100143a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c06c9a81b8809bc67a7003d479efb3aba3f57a595fd05fe8e53ac94bc0150023e0b5249c1f64130b224c31e3bb08c243679e763f916ab7f3baac2feaa05a49eb
|
|
7
|
+
data.tar.gz: 869b6a0a2c2e6249a944f62f258772ca2f3346fe613597bc97090adaa2d7867f1ec2bc936690b2822f06e6d3842880a9bec601af0ff06c57a0fab008b00878be
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
## [
|
|
1
|
+
## [1.16.0]
|
|
2
|
+
### Added
|
|
3
|
+
- Brand new Feature **"Lock series of locks"**
|
|
4
|
+
- (*Proof of Concpet*-based implementation at the moment)
|
|
5
|
+
- lock series of lock simultainously (and release them all at the finish of your the invocation of block of code under the obtained locks);
|
|
6
|
+
- exmaple: `client.lock_series("a", "b", "c")` (or in exceptional-based way `client.lock_series!("a", "b", "c")`;
|
|
7
|
+
### Changed
|
|
8
|
+
- YARDOC documentation updates in order to prearet to the 100% yardoc-coverage;
|
|
9
|
+
- updated dev deps;
|
|
10
|
+
### Fixed
|
|
11
|
+
- fixed some yardoc documentation typos;
|
|
2
12
|
|
|
3
13
|
## [1.15.1] - 2025-10-22
|
|
4
14
|
### Fixed
|
data/README.md
CHANGED
|
@@ -23,6 +23,8 @@ Provides flexible invocation flow, parametrized limits (lock request ttl, lock t
|
|
|
23
23
|
- [Usage](#usage)
|
|
24
24
|
- [lock](#lock---obtain-a-lock)
|
|
25
25
|
- [lock!](#lock---exceptional-lock-obtaining)
|
|
26
|
+
- [lock_series (PoC)](#lock_series---poc-acquire-a-series-of-locks)
|
|
27
|
+
- [lock_series! (PoC)](#lock_series---poc-exceptional-lock_series)
|
|
26
28
|
- [lock_info](#lock_info)
|
|
27
29
|
- [queue_info](#queue_info)
|
|
28
30
|
- [locked?](#locked)
|
|
@@ -850,6 +852,74 @@ See `#lock` method [documentation](#lock---obtain-a-lock).
|
|
|
850
852
|
|
|
851
853
|
---
|
|
852
854
|
|
|
855
|
+
#### #lock_series - (PoC) acquire a series of locks
|
|
856
|
+
|
|
857
|
+
> (IMPORTANT!) "Proof of Concept" realization. Will be reworked in the future (very-very soon)
|
|
858
|
+
|
|
859
|
+
`lock_series` - acquire a series of locks simultaniously and hold them all while your block of code is executing
|
|
860
|
+
or (if block is not passed) while all locks will not expire.
|
|
861
|
+
|
|
862
|
+
**IMPORTANT DETAILS AND LIMITATIONS**:
|
|
863
|
+
- (this behavior will be reworked with another algorithm);
|
|
864
|
+
- each lock will be released with the next formula:
|
|
865
|
+
- `lock position with reverse numbering` * `ttl`
|
|
866
|
+
- (positions exmaple: ["a", "b", "c"] => "a" position is 3, "b" position is 2, "c" position is 1);
|
|
867
|
+
- this means taht in case of 3 locks (`lock_series("x", "y", "z", ttl: 15_000`) with `5 seconds TTL` your locks will be with the next TTL:
|
|
868
|
+
- `"x"` - 15 seconds
|
|
869
|
+
- `"y"` - 10 seconds
|
|
870
|
+
- `"z"` - 5 seconds
|
|
871
|
+
- when your block of code has completed their execution - all locks will be released immediatly;
|
|
872
|
+
- if you did not pass a block - all locks will be released with their forumla-based own TTL described above;
|
|
873
|
+
- **HOW WILL THIS CASE WORK IN FUTURE RELESAE**: all locks will have the same TTL (`5 seconds` for our example, not `15/10/5 seconds`);
|
|
874
|
+
|
|
875
|
+
###### How to use:
|
|
876
|
+
|
|
877
|
+
```ruby
|
|
878
|
+
# typical logic-oriented way
|
|
879
|
+
client.lock_series("a", "b", "c") { ...some_code... }
|
|
880
|
+
|
|
881
|
+
# result-oriented way
|
|
882
|
+
detailed_result = client.lock_series('x', 'y', 'z', detailed_result: true) { 1 + 2 }
|
|
883
|
+
detailed_result # =>
|
|
884
|
+
{
|
|
885
|
+
yield_result: 3, # result of your block of code
|
|
886
|
+
locks_released_at: 1769506100.676884, # (instrumentation) release time, epoch
|
|
887
|
+
locks_acq_time: 7.0, # (instrumentation) time spent to acquire all locks, milliseconds
|
|
888
|
+
locks_hold_time: 0.65, # (instrumentation) lock hold period, milliseconds
|
|
889
|
+
lock_series: ["x", "y", "z"], # your locks
|
|
890
|
+
rql_lock_series: ["rql:lock:x", "rql:lock:y", "rql:lock:z"] # internal RQL lock-keys of your locks
|
|
891
|
+
}
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
###### New instrumentaiton events and logs:
|
|
895
|
+
|
|
896
|
+
```ruby
|
|
897
|
+
# NEW instrumentation events (examples)
|
|
898
|
+
"redis_queued_locks.lock_series_hold_and_release" => {hold_time: 6.41, ttl: 5000, acq_id: "rql:acq:57147/1696/1704/1712/70ff69bc025ace02", hst_id: "rql:hst:57147/1696/1712/70ff69bc025ace02", ts: 1769505674.958648, lock_keys: ["rql:lock:a", "rql:lock:b"], acq_time: 2.35, instrument: nil}
|
|
899
|
+
"redis_queued_locks.lock_obtained" => {lock_key: "rql:lock:x", ttl: 15675, acq_id: "rql:acq:57685/1696/1704/1712/b5b8c38227c18e8a", hst_id: "rql:hst:57685/1696/1712/b5b8c38227c18e8a", ts: 1769505795.984393, acq_time: 4.15, instrument: nil}
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
```shell
|
|
903
|
+
# NEW logs (examples)
|
|
904
|
+
[redis_queued_locks.start_lock_series_obtaining] lock_keys => '["rql:lock:x", "rql:lock:y", "rql:lock:c"]'queue_ttl => 15 acq_id => 'rql:acq:57685/1696/1704/1712/b5b8c38227c18e8a' hst_id => 'rql:hst:57685/1696/1712/b5b8c38227c18e8a' acs_strat => 'queued'
|
|
905
|
+
[redis_queued_locks.lock_series_obtained] lock_keys => '["rql:lock:x", "rql:lock:y", "rql:lock:z"]' queue_ttl => 15 acq_id => 'rql:acq:57685/1696/1704/1712/b5b8c38227c18e8a' hst_id => 'rql:hst:57685/1696/1712/b5b8c38227c18e8a' acs_strat => 'queued' acq_time => 7.02 (ms)
|
|
906
|
+
[redis_queued_locks.expire_lock_series] lock_keys => '["rql:lock:x", "rql:lock:y", "rql:lock:z"]' queue_ttl => 15 acq_id => 'rql:acq:57685/1696/1704/1712/b5b8c38227c18e8a' hst_id => 'rql:hst:57685/1696/1712/b5b8c38227c18e8a' acs_strat => 'queued'
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
----
|
|
910
|
+
|
|
911
|
+
#### #lock_series! - (PoC) exceptional `lock_series`
|
|
912
|
+
|
|
913
|
+
> (IMPORTANT!) "Proof of Concept" realization. Will be reworked in the future (very-very soon)
|
|
914
|
+
|
|
915
|
+
For more details see `lock_series` [docs](#lock_series---poc-acquire-a-series-of-locks)
|
|
916
|
+
|
|
917
|
+
```ruby
|
|
918
|
+
client.lock_series!("x", "y", "z")
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
----
|
|
922
|
+
|
|
853
923
|
#### #lock_info
|
|
854
924
|
|
|
855
925
|
<sup>\[[back to top](#usage)\]</sup>
|
|
@@ -2328,6 +2398,8 @@ Detalized event semantics and payload structure:
|
|
|
2328
2398
|
- `write` - waits - `write`;
|
|
2329
2399
|
- **write** mode is a default behavior for all RQL locks;
|
|
2330
2400
|
- **Minor**:
|
|
2401
|
+
- an ability to return all insturmentation metrics from the `lock` invocation (and after block `yield`ing);
|
|
2402
|
+
- think about "thread priority" configuration :thinking:;
|
|
2331
2403
|
- add `hst_id` to all methods that works with queues info;
|
|
2332
2404
|
- try to return the `fiber object id` to the lock host identifier (we cant use fiber object id cuz `ObjectSpace` has no access to the fiber object space after the any ractor object initialization)
|
|
2333
2405
|
- named RQL's threads (`Thread#name`) and RQL's ractors (`Ractor#name`) in order to have an ability to find and work with RQL's threads and ractors outside of RQL logic (stop threads before process forking, for example);
|
|
@@ -2358,6 +2430,8 @@ Detalized event semantics and payload structure:
|
|
|
2358
2430
|
- **Research**: support for `Valkey` database backend (https://github.com/valkey-io/valkey) (https://valkey.io/);
|
|
2359
2431
|
- **Research**: support for `Garnet` database backend (https://microsoft.github.io/) (https://github.com/microsoft/garnet);
|
|
2360
2432
|
- add a library-level exception, when RQL-related key in Redis (required for its logic) has incompatible type (means: some other program uses our key with their own type and logic and RQL can't work properly);
|
|
2433
|
+
- yardoc docs with CI check (full doc coverage check);
|
|
2434
|
+
|
|
2361
2435
|
---
|
|
2362
2436
|
|
|
2363
2437
|
## Build and Develop
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# @api private
|
|
4
|
-
# @since 1.
|
|
4
|
+
# @since 1.16.0
|
|
5
|
+
# rubocop:disable Metrics/ModuleLength
|
|
5
6
|
module RedisQueuedLocks::Acquirer::AcquireLock::InstrVisitor
|
|
6
7
|
class << self
|
|
7
8
|
# @param instrumenter [#notify]
|
|
@@ -162,5 +163,27 @@ module RedisQueuedLocks::Acquirer::AcquireLock::InstrVisitor
|
|
|
162
163
|
hold_time:, ttl:, acq_id:, hst_id:, ts:, lock_key:, acq_time:, instrument:
|
|
163
164
|
}) rescue nil
|
|
164
165
|
end
|
|
166
|
+
|
|
167
|
+
# NOTE: Lock Series PoC
|
|
168
|
+
# @api private
|
|
169
|
+
# @since 1.16.0
|
|
170
|
+
def lock_series_hold_and_release( # steep:ignore
|
|
171
|
+
instrumenter,
|
|
172
|
+
instr_sampled,
|
|
173
|
+
lock_keys,
|
|
174
|
+
ttl,
|
|
175
|
+
acq_id,
|
|
176
|
+
hst_id,
|
|
177
|
+
ts,
|
|
178
|
+
acq_time,
|
|
179
|
+
hold_time,
|
|
180
|
+
instrument
|
|
181
|
+
)
|
|
182
|
+
return unless instr_sampled
|
|
183
|
+
instrumenter.notify('redis_queued_locks.lock_series_hold_and_release', {
|
|
184
|
+
hold_time:, ttl:, acq_id:, hst_id:, ts:, lock_keys:, acq_time:, instrument:
|
|
185
|
+
}) # rescue nil
|
|
186
|
+
end
|
|
165
187
|
end
|
|
166
188
|
end
|
|
189
|
+
# rubocop:enable Metrics/ModuleLength
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# @api private
|
|
4
|
-
# @since 1.
|
|
4
|
+
# @since 1.16.0
|
|
5
5
|
# rubocop:disable Metrics/ModuleLength
|
|
6
6
|
module RedisQueuedLocks::Acquirer::AcquireLock::LogVisitor
|
|
7
7
|
# rubocop:disable Metrics/ClassLength
|
|
@@ -39,6 +39,30 @@ module RedisQueuedLocks::Acquirer::AcquireLock::LogVisitor
|
|
|
39
39
|
end rescue nil
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
# NOTE: Lock Series PoC
|
|
43
|
+
# @api private
|
|
44
|
+
# @since 1.16.0
|
|
45
|
+
def start_lock_series_obtaining( # steep:ignore
|
|
46
|
+
logger,
|
|
47
|
+
log_sampled,
|
|
48
|
+
lock_keys,
|
|
49
|
+
queue_ttl,
|
|
50
|
+
acquirer_id,
|
|
51
|
+
host_id,
|
|
52
|
+
access_strategy
|
|
53
|
+
)
|
|
54
|
+
return unless log_sampled
|
|
55
|
+
|
|
56
|
+
logger.debug do
|
|
57
|
+
"[redis_queued_locks.start_lock_series_obtaining] " \
|
|
58
|
+
"lock_keys => '#{lock_keys.inspect}'" \
|
|
59
|
+
"queue_ttl => #{queue_ttl} " \
|
|
60
|
+
"acq_id => '#{acquirer_id}' " \
|
|
61
|
+
"hst_id => '#{host_id}' " \
|
|
62
|
+
"acs_strat => '#{access_strategy}'"
|
|
63
|
+
end # rescue nil
|
|
64
|
+
end
|
|
65
|
+
|
|
42
66
|
# @param logger [::Logger,#debug]
|
|
43
67
|
# @param log_sampled [Boolean]
|
|
44
68
|
# @param lock_key [String]
|
|
@@ -212,6 +236,32 @@ module RedisQueuedLocks::Acquirer::AcquireLock::LogVisitor
|
|
|
212
236
|
"acq_time => #{acq_time} (ms)"
|
|
213
237
|
end rescue nil
|
|
214
238
|
end
|
|
239
|
+
|
|
240
|
+
# NOTE: Lock Series PoC
|
|
241
|
+
# @api private
|
|
242
|
+
# @since 1.16.0
|
|
243
|
+
def lock_series_obtained( # steep:ignore
|
|
244
|
+
logger,
|
|
245
|
+
log_sampled,
|
|
246
|
+
lock_keys,
|
|
247
|
+
queue_ttl,
|
|
248
|
+
acquirer_id,
|
|
249
|
+
host_id,
|
|
250
|
+
acq_time,
|
|
251
|
+
access_strategy
|
|
252
|
+
)
|
|
253
|
+
return unless log_sampled
|
|
254
|
+
|
|
255
|
+
logger.debug do
|
|
256
|
+
"[redis_queued_locks.lock_series_obtained] " \
|
|
257
|
+
"lock_keys => '#{lock_keys.inspect}' " \
|
|
258
|
+
"queue_ttl => #{queue_ttl} " \
|
|
259
|
+
"acq_id => '#{acquirer_id}' " \
|
|
260
|
+
"hst_id => '#{host_id}' " \
|
|
261
|
+
"acs_strat => '#{access_strategy}' " \
|
|
262
|
+
"acq_time => #{acq_time} (ms)"
|
|
263
|
+
end # rescue nil
|
|
264
|
+
end
|
|
215
265
|
end
|
|
216
266
|
# rubocop:enable Metrics/ClassLength
|
|
217
267
|
end
|
|
@@ -17,7 +17,8 @@ module RedisQueuedLocks::Acquirer::AcquireLock::WithAcqTimeout
|
|
|
17
17
|
# Add additional error data about lock queue and required lock to the timeout error or not.
|
|
18
18
|
# @option on_timeout [Proc,NilClass]
|
|
19
19
|
# Callback invoked on Timeout::Error.
|
|
20
|
-
# @
|
|
20
|
+
# @yield Custom logic that should be invoked under the obtained lock.
|
|
21
|
+
# @yieldreturn [Any]
|
|
21
22
|
# @return [Any]
|
|
22
23
|
#
|
|
23
24
|
# @raise [RedisQueuedLocks::LockAcquirementIntermediateTimeoutError]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# @api private
|
|
4
|
-
# @since 1.
|
|
4
|
+
# @since 1.16.0
|
|
5
5
|
module RedisQueuedLocks::Acquirer::AcquireLock::YieldExpire::LogVisitor
|
|
6
6
|
class << self
|
|
7
7
|
# @param logger [::Logger,#debug]
|
|
@@ -37,6 +37,30 @@ module RedisQueuedLocks::Acquirer::AcquireLock::YieldExpire::LogVisitor
|
|
|
37
37
|
end rescue nil
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
+
# NOTE: Lock Series PoC
|
|
41
|
+
# @api private
|
|
42
|
+
# @since 1.16.0
|
|
43
|
+
def expire_lock_series( # steep:ignore
|
|
44
|
+
logger,
|
|
45
|
+
log_sampled,
|
|
46
|
+
lock_series,
|
|
47
|
+
queue_ttl,
|
|
48
|
+
acquirer_id,
|
|
49
|
+
host_id,
|
|
50
|
+
access_strategy
|
|
51
|
+
)
|
|
52
|
+
return unless log_sampled
|
|
53
|
+
|
|
54
|
+
logger.debug do
|
|
55
|
+
"[redis_queued_locks.expire_lock_series] " \
|
|
56
|
+
"lock_keys => '#{lock_series.inspect}' " \
|
|
57
|
+
"queue_ttl => #{queue_ttl} " \
|
|
58
|
+
"acq_id => '#{acquirer_id}' " \
|
|
59
|
+
"hst_id => '#{host_id}' " \
|
|
60
|
+
"acs_strat => '#{access_strategy}'"
|
|
61
|
+
end rescue nil
|
|
62
|
+
end
|
|
63
|
+
|
|
40
64
|
# @param logger [::Logger,#debug]
|
|
41
65
|
# @param log_sampled [Boolean]
|
|
42
66
|
# @param lock_key [String]
|
|
@@ -32,7 +32,8 @@ module RedisQueuedLocks::Acquirer::AcquireLock::YieldExpire
|
|
|
32
32
|
# @param should_decrease [Boolean]
|
|
33
33
|
# - Should decrease the lock TTL after the lock invocation;
|
|
34
34
|
# - It is suitable for extendable reentrant locks;
|
|
35
|
-
# @
|
|
35
|
+
# @yield Custom logic that should be invoked under the obtained lock.
|
|
36
|
+
# @yieldreturn [Any]
|
|
36
37
|
# @return [Any,NilClass] nil is returned when no block parametr is provided.
|
|
37
38
|
#
|
|
38
39
|
# @api private
|
|
@@ -122,7 +123,7 @@ module RedisQueuedLocks::Acquirer::AcquireLock::YieldExpire
|
|
|
122
123
|
private
|
|
123
124
|
|
|
124
125
|
# @param timeout [Float]
|
|
125
|
-
# @
|
|
126
|
+
# @param lock_key [String]
|
|
126
127
|
# @param lock_ttl [Integer,NilClass]
|
|
127
128
|
# @param acquirer_id [String]
|
|
128
129
|
# @param host_id [String]
|
|
@@ -151,11 +151,11 @@ module RedisQueuedLocks::Acquirer::AcquireLock
|
|
|
151
151
|
# - marks the method that everything should be instrumneted
|
|
152
152
|
# despite the enabled instrumentation sampling;
|
|
153
153
|
# - makes sense when instrumentation sampling is enabled;
|
|
154
|
-
# @
|
|
155
|
-
#
|
|
154
|
+
# @yield A block of code that should be executed after the successfully acquired lock.
|
|
155
|
+
# @yieldreturn [Any]
|
|
156
156
|
# @return [Hash<Symbol,Any>,yield]
|
|
157
157
|
# - Format: { ok: true/false, result: Any }
|
|
158
|
-
# - If block is given the result of block's
|
|
158
|
+
# - If block is given the result of block's yield will be returned.
|
|
159
159
|
#
|
|
160
160
|
# @api private
|
|
161
161
|
# @since 1.0.0
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# NOTE: Lock Series PoC
|
|
4
|
+
# steep:ignore
|
|
5
|
+
# rubocop:disable all
|
|
6
|
+
# @api private
|
|
7
|
+
# @since 1.16.0
|
|
8
|
+
module RedisQueuedLocks::Acquirer::LockSeriesPoC # steep:ignore
|
|
9
|
+
# @since 1.16.0
|
|
10
|
+
extend RedisQueuedLocks::Acquirer::AcquireLock::YieldExpire
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
# @api private
|
|
14
|
+
# @since 1.16.0
|
|
15
|
+
def lock_series_poc( # steep:ignore
|
|
16
|
+
redis,
|
|
17
|
+
lock_names,
|
|
18
|
+
detailed_result:,
|
|
19
|
+
process_id:,
|
|
20
|
+
thread_id:,
|
|
21
|
+
fiber_id:,
|
|
22
|
+
ractor_id:,
|
|
23
|
+
ttl:,
|
|
24
|
+
queue_ttl:,
|
|
25
|
+
timeout:,
|
|
26
|
+
timed:,
|
|
27
|
+
retry_count:,
|
|
28
|
+
retry_delay:,
|
|
29
|
+
retry_jitter:,
|
|
30
|
+
raise_errors:,
|
|
31
|
+
instrumenter:,
|
|
32
|
+
identity:,
|
|
33
|
+
fail_fast:,
|
|
34
|
+
meta:,
|
|
35
|
+
detailed_acq_timeout_error:,
|
|
36
|
+
instrument:,
|
|
37
|
+
logger:,
|
|
38
|
+
log_lock_try:,
|
|
39
|
+
conflict_strategy:,
|
|
40
|
+
read_write_mode:,
|
|
41
|
+
access_strategy:,
|
|
42
|
+
log_sampling_enabled:,
|
|
43
|
+
log_sampling_percent:,
|
|
44
|
+
log_sampler:,
|
|
45
|
+
log_sample_this:,
|
|
46
|
+
instr_sampling_enabled:,
|
|
47
|
+
instr_sampling_percent:,
|
|
48
|
+
instr_sampler:,
|
|
49
|
+
instr_sample_this:,
|
|
50
|
+
&block
|
|
51
|
+
)
|
|
52
|
+
case meta
|
|
53
|
+
when Hash, NilClass then nil
|
|
54
|
+
else
|
|
55
|
+
raise(
|
|
56
|
+
RedisQueuedLocks::ArgumentError,
|
|
57
|
+
"`:meta` argument should be a type of NilClass or Hash, got #{meta.class}."
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
if meta.is_a?(::Hash) && (meta.any? do |key, _value|
|
|
62
|
+
key == 'acq_id' ||
|
|
63
|
+
key == 'hst_id' ||
|
|
64
|
+
key == 'ts' ||
|
|
65
|
+
key == 'ini_ttl' ||
|
|
66
|
+
key == 'lock_key' ||
|
|
67
|
+
key == 'rem_ttl' ||
|
|
68
|
+
key == 'spc_ext_ttl' ||
|
|
69
|
+
key == 'spc_cnt' ||
|
|
70
|
+
key == 'l_spc_ext_ini_ttl' ||
|
|
71
|
+
key == 'l_spc_ext_ts' ||
|
|
72
|
+
key == 'l_spc_ts'
|
|
73
|
+
end)
|
|
74
|
+
raise(
|
|
75
|
+
RedisQueuedLocks::ArgumentError,
|
|
76
|
+
'`:meta` keys can not overlap reserved lock data keys ' \
|
|
77
|
+
'"acq_id", "hst_id", "ts", "ini_ttl", "lock_key", "rem_ttl", "spc_cnt", ' \
|
|
78
|
+
'"spc_ext_ttl", "l_spc_ext_ini_ttl", "l_spc_ext_ts", "l_spc_ts"'
|
|
79
|
+
)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
locks_count = lock_names.size
|
|
83
|
+
|
|
84
|
+
lock_acquirement_operations = lock_names.each_with_index.map do |lock_name, position|
|
|
85
|
+
lock_ttl =
|
|
86
|
+
ttl * (locks_count - position) +
|
|
87
|
+
retry_delay * (locks_count - position) +
|
|
88
|
+
retry_jitter * (locks_count - position)
|
|
89
|
+
|
|
90
|
+
{ lock_name: lock_name, ttl: lock_ttl }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
log_sampled = RedisQueuedLocks::Logging.should_log?(
|
|
94
|
+
log_sampling_enabled,
|
|
95
|
+
log_sample_this,
|
|
96
|
+
log_sampling_percent,
|
|
97
|
+
log_sampler
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
instr_sampled = RedisQueuedLocks::Instrument.should_instrument?(
|
|
101
|
+
instr_sampling_enabled,
|
|
102
|
+
instr_sample_this,
|
|
103
|
+
instr_sampling_percent,
|
|
104
|
+
instr_sampler
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
lock_keys_for_instrumentation = lock_names.map do |lock_name|
|
|
108
|
+
RedisQueuedLocks::Resource.prepare_lock_key(lock_name)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
acquirer_id_for_instrumentation = RedisQueuedLocks::Resource.acquirer_identifier(
|
|
112
|
+
process_id,
|
|
113
|
+
thread_id,
|
|
114
|
+
fiber_id,
|
|
115
|
+
ractor_id,
|
|
116
|
+
identity
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
host_id_for_instrumentation = RedisQueuedLocks::Resource.host_identifier(
|
|
120
|
+
process_id,
|
|
121
|
+
thread_id,
|
|
122
|
+
ractor_id,
|
|
123
|
+
identity
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
RedisQueuedLocks::Acquirer::AcquireLock::LogVisitor.start_lock_series_obtaining( # steep:ignore
|
|
127
|
+
logger, log_sampled, lock_keys_for_instrumentation,
|
|
128
|
+
queue_ttl, acquirer_id_for_instrumentation, host_id_for_instrumentation, access_strategy
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
acq_start_time = RedisQueuedLocks::Utilities.clock_gettime
|
|
132
|
+
successfully_acquired_locks = [] # steep:ignore
|
|
133
|
+
failed_locks_and_errors = {} # steep:ignore
|
|
134
|
+
failed_on_lock = nil
|
|
135
|
+
|
|
136
|
+
lock_acquirement_operations.map do |lock_operation_options|
|
|
137
|
+
result =
|
|
138
|
+
begin
|
|
139
|
+
RedisQueuedLocks::Acquirer::AcquireLock.acquire_lock(
|
|
140
|
+
redis,
|
|
141
|
+
lock_operation_options[:lock_name],
|
|
142
|
+
process_id:,
|
|
143
|
+
thread_id:,
|
|
144
|
+
fiber_id:,
|
|
145
|
+
ractor_id:,
|
|
146
|
+
ttl: lock_operation_options[:ttl],
|
|
147
|
+
queue_ttl:,
|
|
148
|
+
timeout:,
|
|
149
|
+
timed:,
|
|
150
|
+
retry_count:,
|
|
151
|
+
retry_delay:,
|
|
152
|
+
retry_jitter:,
|
|
153
|
+
raise_errors:,
|
|
154
|
+
instrumenter:,
|
|
155
|
+
identity:,
|
|
156
|
+
fail_fast:,
|
|
157
|
+
meta:,
|
|
158
|
+
detailed_acq_timeout_error:,
|
|
159
|
+
instrument:,
|
|
160
|
+
logger:,
|
|
161
|
+
log_lock_try:,
|
|
162
|
+
conflict_strategy:,
|
|
163
|
+
read_write_mode:,
|
|
164
|
+
access_strategy:,
|
|
165
|
+
log_sampling_enabled:,
|
|
166
|
+
log_sampling_percent:,
|
|
167
|
+
log_sampler:,
|
|
168
|
+
log_sample_this:,
|
|
169
|
+
instr_sampling_enabled:,
|
|
170
|
+
instr_sampling_percent:,
|
|
171
|
+
instr_sampler:,
|
|
172
|
+
instr_sample_this:
|
|
173
|
+
)
|
|
174
|
+
rescue => error
|
|
175
|
+
if raise_errors && successfully_acquired_locks.any?
|
|
176
|
+
# NOTE: release all previously acquired locks if any next lock is already locked
|
|
177
|
+
successfully_acquired_locks.each do |operation_result|
|
|
178
|
+
lock_key = RedisQueuedLocks::Resource.prepare_lock_key(operation_result[:lock_name])
|
|
179
|
+
redis.with do |conn|
|
|
180
|
+
conn.multi(watch: [lock_key]) do |transact|
|
|
181
|
+
transact.call('DEL', lock_key)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
raise(error)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
if result[:ok]
|
|
190
|
+
successfully_acquired_locks << {
|
|
191
|
+
lock_name: lock_operation_options[:lock_name],
|
|
192
|
+
ok: result[:ok],
|
|
193
|
+
result: result[:result]
|
|
194
|
+
}
|
|
195
|
+
else
|
|
196
|
+
failed_on_lock = lock_operation_options[:lock_name]
|
|
197
|
+
failed_locks_and_errors[lock_operation_options[:lock_name]] = result
|
|
198
|
+
break
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
if (successfully_acquired_locks.size == lock_names.size && (successfully_acquired_locks.all? { |res| res[:ok] }))
|
|
203
|
+
acq_end_time = RedisQueuedLocks::Utilities.clock_gettime
|
|
204
|
+
acq_time = ((acq_end_time - acq_start_time) / 1_000.0).ceil(2)
|
|
205
|
+
|
|
206
|
+
RedisQueuedLocks::Acquirer::AcquireLock::LogVisitor.lock_series_obtained( # steep:ignore
|
|
207
|
+
logger, log_sampled, lock_keys_for_instrumentation,
|
|
208
|
+
queue_ttl, acquirer_id_for_instrumentation, host_id_for_instrumentation,
|
|
209
|
+
acq_time, access_strategy
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
yield_time = RedisQueuedLocks::Utilities.clock_gettime
|
|
213
|
+
ttl_shift = (
|
|
214
|
+
(yield_time - acq_end_time) / 1_000.0 -
|
|
215
|
+
RedisQueuedLocks::Resource::REDIS_TIMESHIFT_ERROR
|
|
216
|
+
).ceil(2)
|
|
217
|
+
|
|
218
|
+
yield_result = yield_expire( # steep:ignore
|
|
219
|
+
redis,
|
|
220
|
+
logger,
|
|
221
|
+
lock_keys_for_instrumentation.last,
|
|
222
|
+
acquirer_id_for_instrumentation,
|
|
223
|
+
host_id_for_instrumentation,
|
|
224
|
+
access_strategy,
|
|
225
|
+
timed,
|
|
226
|
+
ttl_shift,
|
|
227
|
+
ttl,
|
|
228
|
+
queue_ttl,
|
|
229
|
+
meta,
|
|
230
|
+
log_sampled,
|
|
231
|
+
instr_sampled,
|
|
232
|
+
false, # should_expire (expire manually)
|
|
233
|
+
false, # should_decrease (expire manually)
|
|
234
|
+
&block
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
# expire locks manually
|
|
238
|
+
if block_given?
|
|
239
|
+
redis.with do |conn|
|
|
240
|
+
# use transaction in order to exclude any cross-locking during the group expiration
|
|
241
|
+
conn.multi(watch: lock_keys_for_instrumentation) do |transaction|
|
|
242
|
+
lock_keys_for_instrumentation.each do |lock_key|
|
|
243
|
+
transaction.call('EXPIRE', lock_key, '0')
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
rel_time = RedisQueuedLocks::Utilities.clock_gettime
|
|
250
|
+
hold_time = ((rel_time - acq_end_time) / 1_000.0).ceil(2)
|
|
251
|
+
ts = Time.now.to_f
|
|
252
|
+
|
|
253
|
+
RedisQueuedLocks::Acquirer::AcquireLock::YieldExpire::LogVisitor.expire_lock_series( # steep:ignore
|
|
254
|
+
logger, log_sampled, lock_keys_for_instrumentation,
|
|
255
|
+
queue_ttl, acquirer_id_for_instrumentation, host_id_for_instrumentation, access_strategy
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
RedisQueuedLocks::Acquirer::AcquireLock::InstrVisitor.lock_series_hold_and_release( # steep:ignore
|
|
259
|
+
instrumenter,
|
|
260
|
+
instr_sampled,
|
|
261
|
+
lock_keys_for_instrumentation,
|
|
262
|
+
ttl,
|
|
263
|
+
acquirer_id_for_instrumentation,
|
|
264
|
+
host_id_for_instrumentation,
|
|
265
|
+
ts,
|
|
266
|
+
acq_time,
|
|
267
|
+
hold_time,
|
|
268
|
+
instrument
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
if detailed_result
|
|
272
|
+
{
|
|
273
|
+
yield_result: yield_result,
|
|
274
|
+
locks_released_at: ts,
|
|
275
|
+
locks_acq_time: acq_time,
|
|
276
|
+
locks_hold_time: hold_time,
|
|
277
|
+
lock_series: lock_names,
|
|
278
|
+
rql_lock_series: lock_keys_for_instrumentation
|
|
279
|
+
}
|
|
280
|
+
else
|
|
281
|
+
yield_result
|
|
282
|
+
end
|
|
283
|
+
else
|
|
284
|
+
acquired_locks = successfully_acquired_locks.map { |state| state[:lock_name] }
|
|
285
|
+
missing_locks = lock_names - acquired_locks
|
|
286
|
+
|
|
287
|
+
{
|
|
288
|
+
ok: false,
|
|
289
|
+
result: {
|
|
290
|
+
error: :failed_to_acquire_lock_series,
|
|
291
|
+
detailed_errors: failed_locks_and_errors,
|
|
292
|
+
lock_series: lock_names,
|
|
293
|
+
acquired_locks:,
|
|
294
|
+
missing_locks:,
|
|
295
|
+
failed_on_lock:,
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
# steep:ignore
|
|
303
|
+
# rubocop:enable all
|
|
@@ -18,7 +18,7 @@ module RedisQueuedLocks::Acquirer::ReleaseAllLocks
|
|
|
18
18
|
# @param logger [::Logger,#debug]
|
|
19
19
|
# - Logger object used from `configuration` layer (see config['logger']);
|
|
20
20
|
# - See RedisQueuedLocks::Logging::VoidLogger for example;
|
|
21
|
-
# @param
|
|
21
|
+
# @param instrumenter [#notify]
|
|
22
22
|
# See RedisQueuedLocks::Instrument::ActiveSupport for example.
|
|
23
23
|
# @param instrument [NilClass,Any]
|
|
24
24
|
# - Custom instrumentation data wich will be passed to the instrumenter's payload
|
|
@@ -18,7 +18,7 @@ module RedisQueuedLocks::Acquirer::ReleaseLock
|
|
|
18
18
|
# Redis connection client.
|
|
19
19
|
# @param lock_name [String]
|
|
20
20
|
# The lock name that should be released.
|
|
21
|
-
# @param
|
|
21
|
+
# @param instrumenter [#notify]
|
|
22
22
|
# See RedisQueuedLocks::Instrument::ActiveSupport for example.
|
|
23
23
|
# @param logger [::Logger,#debug]
|
|
24
24
|
# - Logger object used from `configuration` layer (see config['logger']);
|
|
@@ -24,7 +24,7 @@ module RedisQueuedLocks::Acquirer::ReleaseLocksOf
|
|
|
24
24
|
# @param logger [::Logger,#debug]
|
|
25
25
|
# - Logger object used from `configuration` layer (see config['logger']);
|
|
26
26
|
# - See RedisQueuedLocks::Logging::VoidLogger for example;
|
|
27
|
-
# @param
|
|
27
|
+
# @param instrumenter [#notify]
|
|
28
28
|
# See RedisQueuedLocks::Instrument::ActiveSupport for example.
|
|
29
29
|
# @param instrument [NilClass,Any]
|
|
30
30
|
# - Custom instrumentation data wich will be passed to the instrumenter's payload
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
# @since 1.0.0
|
|
5
5
|
module RedisQueuedLocks::Acquirer
|
|
6
6
|
require_relative 'acquirer/acquire_lock'
|
|
7
|
+
require_relative 'acquirer/lock_series_poc'
|
|
7
8
|
require_relative 'acquirer/release_lock'
|
|
8
9
|
require_relative 'acquirer/release_all_locks'
|
|
9
10
|
require_relative 'acquirer/release_locks_of'
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# @api public
|
|
4
4
|
# @since 1.0.0
|
|
5
|
-
# @version 1.
|
|
5
|
+
# @version 1.16.0
|
|
6
6
|
# rubocop:disable Metrics/ClassLength
|
|
7
7
|
class RedisQueuedLocks::Client
|
|
8
8
|
# @return [RedisClient]
|
|
@@ -301,11 +301,11 @@ class RedisQueuedLocks::Client
|
|
|
301
301
|
# - marks the method that everything should be instrumneted
|
|
302
302
|
# despite the enabled instrumentation sampling;
|
|
303
303
|
# - makes sense when instrumentation sampling is enabled;
|
|
304
|
-
# @
|
|
305
|
-
#
|
|
304
|
+
# @yield A block of code that should be executed after the successfully acquired lock.
|
|
305
|
+
# @yieldreturn [Any]
|
|
306
306
|
# @return [Hash<Symbol,Any>,yield]
|
|
307
307
|
# - Format: { ok: true/false, result: Symbol/Hash }.
|
|
308
|
-
# - If block is given the result of block's
|
|
308
|
+
# - If block is given the result of block's yield will be returned.
|
|
309
309
|
#
|
|
310
310
|
# @api public
|
|
311
311
|
# @since 1.0.0
|
|
@@ -381,12 +381,156 @@ class RedisQueuedLocks::Client
|
|
|
381
381
|
end
|
|
382
382
|
# rubocop:enable Metrics/MethodLength
|
|
383
383
|
|
|
384
|
+
# NOTE: Lock Series PoC
|
|
385
|
+
# rubocop:disable all
|
|
386
|
+
# @api public
|
|
387
|
+
# @since 1.16.0
|
|
388
|
+
def lock_series( # steep:ignore
|
|
389
|
+
*lock_names,
|
|
390
|
+
detailed_result: false,
|
|
391
|
+
ttl: config['default_lock_ttl'], # steep:ignore
|
|
392
|
+
queue_ttl: config['default_queue_ttl'], # steep:ignore
|
|
393
|
+
timeout: config['try_to_lock_timeout'], # steep:ignore
|
|
394
|
+
timed: config['is_timed_by_default'], # steep:ignore
|
|
395
|
+
retry_count: config['retry_count'], # steep:ignore
|
|
396
|
+
retry_delay: config['retry_delay'], # steep:ignore
|
|
397
|
+
retry_jitter: config['retry_jitter'], # steep:ignore
|
|
398
|
+
raise_errors: false,
|
|
399
|
+
fail_fast: false,
|
|
400
|
+
conflict_strategy: config['default_conflict_strategy'], # steep:ignore
|
|
401
|
+
read_write_mode: :write,
|
|
402
|
+
access_strategy: config['default_access_strategy'], # steep:ignore
|
|
403
|
+
identity: uniq_identity,
|
|
404
|
+
meta: nil,
|
|
405
|
+
detailed_acq_timeout_error: config['detailed_acq_timeout_error'], # steep:ignore
|
|
406
|
+
logger: config['logger'], # steep:ignore
|
|
407
|
+
log_lock_try: config['log_lock_try'], # steep:ignore
|
|
408
|
+
instrumenter: config['instrumenter'], # steep:ignore
|
|
409
|
+
instrument: nil,
|
|
410
|
+
log_sampling_enabled: config['log_sampling_enabled'], # steep:ignore
|
|
411
|
+
log_sampling_percent: config['log_sampling_percent'], # steep:ignore
|
|
412
|
+
log_sampler: config['log_sampler'], # steep:ignore
|
|
413
|
+
log_sample_this: false,
|
|
414
|
+
instr_sampling_enabled: config['instr_sampling_enabled'], # steep:ignore
|
|
415
|
+
instr_sampling_percent: config['instr_sampling_percent'], # steep:ignore
|
|
416
|
+
instr_sampler: config['instr_sampler'], # steep:ignore
|
|
417
|
+
instr_sample_this: false,
|
|
418
|
+
&block
|
|
419
|
+
)
|
|
420
|
+
RedisQueuedLocks::Acquirer::LockSeriesPoC.lock_series_poc( # steep:ignore
|
|
421
|
+
redis_client,
|
|
422
|
+
lock_names,
|
|
423
|
+
detailed_result:,
|
|
424
|
+
process_id: RedisQueuedLocks::Resource.get_process_id,
|
|
425
|
+
thread_id: RedisQueuedLocks::Resource.get_thread_id,
|
|
426
|
+
fiber_id: RedisQueuedLocks::Resource.get_fiber_id,
|
|
427
|
+
ractor_id: RedisQueuedLocks::Resource.get_ractor_id,
|
|
428
|
+
ttl:,
|
|
429
|
+
queue_ttl:,
|
|
430
|
+
timeout:,
|
|
431
|
+
timed:,
|
|
432
|
+
retry_count:,
|
|
433
|
+
retry_delay:,
|
|
434
|
+
retry_jitter:,
|
|
435
|
+
raise_errors:,
|
|
436
|
+
instrumenter:,
|
|
437
|
+
identity:,
|
|
438
|
+
fail_fast:,
|
|
439
|
+
conflict_strategy:,
|
|
440
|
+
read_write_mode:,
|
|
441
|
+
access_strategy:,
|
|
442
|
+
meta:,
|
|
443
|
+
detailed_acq_timeout_error:,
|
|
444
|
+
logger:,
|
|
445
|
+
log_lock_try:,
|
|
446
|
+
instrument:,
|
|
447
|
+
log_sampling_enabled:,
|
|
448
|
+
log_sampling_percent:,
|
|
449
|
+
log_sampler:,
|
|
450
|
+
log_sample_this:,
|
|
451
|
+
instr_sampling_enabled:,
|
|
452
|
+
instr_sampling_percent:,
|
|
453
|
+
instr_sampler:,
|
|
454
|
+
instr_sample_this:,
|
|
455
|
+
&block
|
|
456
|
+
)
|
|
457
|
+
end
|
|
458
|
+
# rubocop:enable all
|
|
459
|
+
|
|
460
|
+
# NOTE: Lock Series PoC
|
|
461
|
+
# rubocop:disable all
|
|
462
|
+
# @api public
|
|
463
|
+
# @since 1.16.0
|
|
464
|
+
def lock_series!( # steep:ignore
|
|
465
|
+
*lock_names,
|
|
466
|
+
detailed_result: false,
|
|
467
|
+
ttl: config['default_lock_ttl'], # steep:ignore
|
|
468
|
+
queue_ttl: config['default_queue_ttl'], # steep:ignore
|
|
469
|
+
timeout: config['try_to_lock_timeout'], # steep:ignore
|
|
470
|
+
timed: config['is_timed_by_default'], # steep:ignore
|
|
471
|
+
retry_count: config['retry_count'], # steep:ignore
|
|
472
|
+
retry_delay: config['retry_delay'], # steep:ignore
|
|
473
|
+
retry_jitter: config['retry_jitter'], # steep:ignore
|
|
474
|
+
fail_fast: false,
|
|
475
|
+
conflict_strategy: config['default_conflict_strategy'], # steep:ignore
|
|
476
|
+
read_write_mode: :write,
|
|
477
|
+
access_strategy: config['default_access_strategy'], # steep:ignore
|
|
478
|
+
identity: uniq_identity,
|
|
479
|
+
meta: nil,
|
|
480
|
+
detailed_acq_timeout_error: config['detailed_acq_timeout_error'], # steep:ignore
|
|
481
|
+
logger: config['logger'], # steep:ignore
|
|
482
|
+
log_lock_try: config['log_lock_try'], # steep:ignore
|
|
483
|
+
instrumenter: config['instrumenter'], # steep:ignore
|
|
484
|
+
instrument: nil,
|
|
485
|
+
log_sampling_enabled: config['log_sampling_enabled'], # steep:ignore
|
|
486
|
+
log_sampling_percent: config['log_sampling_percent'], # steep:ignore
|
|
487
|
+
log_sampler: config['log_sampler'], # steep:ignore
|
|
488
|
+
log_sample_this: false,
|
|
489
|
+
instr_sampling_enabled: config['instr_sampling_enabled'], # steep:ignore
|
|
490
|
+
instr_sampling_percent: config['instr_sampling_percent'], # steep:ignore
|
|
491
|
+
instr_sampler: config['instr_sampler'], # steep:ignore
|
|
492
|
+
instr_sample_this: false,
|
|
493
|
+
&block
|
|
494
|
+
)
|
|
495
|
+
lock_series( # steep:ignore
|
|
496
|
+
*lock_names, # steep:ignore
|
|
497
|
+
detailed_result:,
|
|
498
|
+
ttl:,
|
|
499
|
+
queue_ttl:,
|
|
500
|
+
timeout:,
|
|
501
|
+
timed:,
|
|
502
|
+
retry_count:,
|
|
503
|
+
retry_delay:,
|
|
504
|
+
retry_jitter:,
|
|
505
|
+
raise_errors: true,
|
|
506
|
+
fail_fast:,
|
|
507
|
+
conflict_strategy:,
|
|
508
|
+
read_write_mode:,
|
|
509
|
+
access_strategy:,
|
|
510
|
+
identity:,
|
|
511
|
+
meta:,
|
|
512
|
+
detailed_acq_timeout_error:,
|
|
513
|
+
logger:,
|
|
514
|
+
log_lock_try:,
|
|
515
|
+
instrumenter:,
|
|
516
|
+
instrument:,
|
|
517
|
+
log_sampling_enabled:,
|
|
518
|
+
log_sampling_percent:,
|
|
519
|
+
log_sampler:,
|
|
520
|
+
log_sample_this:,
|
|
521
|
+
instr_sampling_enabled:,
|
|
522
|
+
instr_sampling_percent:,
|
|
523
|
+
instr_sampler:,
|
|
524
|
+
instr_sample_this:,
|
|
525
|
+
&block
|
|
526
|
+
)
|
|
527
|
+
end
|
|
528
|
+
|
|
384
529
|
# @note See #lock method signature.
|
|
385
530
|
#
|
|
386
531
|
# @api public
|
|
387
532
|
# @since 1.0.0
|
|
388
533
|
# @version 1.13.0
|
|
389
|
-
# rubocop:disable Metrics/MethodLength
|
|
390
534
|
def lock!(
|
|
391
535
|
lock_name,
|
|
392
536
|
ttl: config['default_lock_ttl'], # steep:ignore
|
|
@@ -16,7 +16,7 @@ module RedisQueuedLocks::Config::DSL
|
|
|
16
16
|
#
|
|
17
17
|
# @api private
|
|
18
18
|
# @since 1.13.0
|
|
19
|
-
def config_setters()= @config_setters
|
|
19
|
+
def config_setters()= @config_setters
|
|
20
20
|
|
|
21
21
|
# NOTE:
|
|
22
22
|
# 1. Style/DefWithParentheses rubocop's cop incorrectly drops `()` from method definition
|
|
@@ -28,7 +28,7 @@ module RedisQueuedLocks::Config::DSL
|
|
|
28
28
|
#
|
|
29
29
|
# @api private
|
|
30
30
|
# @since 1.13.0
|
|
31
|
-
def config_validators()= @config_validators
|
|
31
|
+
def config_validators()= @config_validators
|
|
32
32
|
|
|
33
33
|
# @param config_key [String]
|
|
34
34
|
# @param validator [Block]
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
module RedisQueuedLocks::Logging::VoidLogger
|
|
6
6
|
class << self
|
|
7
7
|
# @param progname [Any]
|
|
8
|
-
# @
|
|
8
|
+
# @yield
|
|
9
|
+
# @yieldreturn [Any]
|
|
9
10
|
# @return [void]
|
|
10
11
|
#
|
|
11
12
|
# @api public
|
|
@@ -13,7 +14,8 @@ module RedisQueuedLocks::Logging::VoidLogger
|
|
|
13
14
|
def warn(progname = nil, &block); end
|
|
14
15
|
|
|
15
16
|
# @param progname [Any]
|
|
16
|
-
# @
|
|
17
|
+
# @yield
|
|
18
|
+
# @yieldreturn [Any]
|
|
17
19
|
# @return [void]
|
|
18
20
|
#
|
|
19
21
|
# @api public
|
|
@@ -21,7 +23,8 @@ module RedisQueuedLocks::Logging::VoidLogger
|
|
|
21
23
|
def unknown(progname = nil, &block); end
|
|
22
24
|
|
|
23
25
|
# @param progname [Any]
|
|
24
|
-
# @
|
|
26
|
+
# @yield
|
|
27
|
+
# @yieldreturn [Any]
|
|
25
28
|
# @return [void]
|
|
26
29
|
#
|
|
27
30
|
# @api public
|
|
@@ -29,7 +32,8 @@ module RedisQueuedLocks::Logging::VoidLogger
|
|
|
29
32
|
def log(progname = nil, &block); end
|
|
30
33
|
|
|
31
34
|
# @param progname [Any]
|
|
32
|
-
# @
|
|
35
|
+
# @yield
|
|
36
|
+
# @yieldreturn [Any]
|
|
33
37
|
# @return [void]
|
|
34
38
|
#
|
|
35
39
|
# @api public
|
|
@@ -37,7 +41,8 @@ module RedisQueuedLocks::Logging::VoidLogger
|
|
|
37
41
|
def info(progname = nil, &block); end
|
|
38
42
|
|
|
39
43
|
# @param progname [Any]
|
|
40
|
-
# @
|
|
44
|
+
# @yield
|
|
45
|
+
# @yieldreturn [Any]
|
|
41
46
|
# @return [void]
|
|
42
47
|
#
|
|
43
48
|
# @api public
|
|
@@ -45,7 +50,8 @@ module RedisQueuedLocks::Logging::VoidLogger
|
|
|
45
50
|
def error(progname = nil, &block); end
|
|
46
51
|
|
|
47
52
|
# @param progname [Any]
|
|
48
|
-
# @
|
|
53
|
+
# @yield
|
|
54
|
+
# @yieldreturn [Any]
|
|
49
55
|
# @return [void]
|
|
50
56
|
#
|
|
51
57
|
# @api public
|
|
@@ -53,7 +59,8 @@ module RedisQueuedLocks::Logging::VoidLogger
|
|
|
53
59
|
def fatal(progname = nil, &block); end
|
|
54
60
|
|
|
55
61
|
# @param progname [Any]
|
|
56
|
-
# @
|
|
62
|
+
# @yield
|
|
63
|
+
# @yieldreturn [Any]
|
|
57
64
|
# @return [void]
|
|
58
65
|
#
|
|
59
66
|
# @api public
|
|
@@ -63,7 +70,8 @@ module RedisQueuedLocks::Logging::VoidLogger
|
|
|
63
70
|
# @param severity [Any]
|
|
64
71
|
# @param message [Any]
|
|
65
72
|
# @param progname [Any]
|
|
66
|
-
# @
|
|
73
|
+
# @yield
|
|
74
|
+
# @yieldreturn [Any]
|
|
67
75
|
# @return [void]
|
|
68
76
|
#
|
|
69
77
|
# @api public
|
|
@@ -71,7 +79,7 @@ module RedisQueuedLocks::Logging::VoidLogger
|
|
|
71
79
|
def add(severity = nil, message = nil, progname = nil, &block); end
|
|
72
80
|
|
|
73
81
|
# @param message [Any]
|
|
74
|
-
# @
|
|
82
|
+
# @return [void]
|
|
75
83
|
#
|
|
76
84
|
# @api public
|
|
77
85
|
# @since 1.0.0
|
|
@@ -75,10 +75,8 @@ module RedisQueuedLocks::Logging
|
|
|
75
75
|
# - see https://github.com/reidmorrison/semantic_logger
|
|
76
76
|
# - convinient/conventional way to support the popular `broadcast` RubyOnRails's logger;
|
|
77
77
|
# - see https://api.rubyonrails.org/classes/ActiveSupport/BroadcastLogger.html
|
|
78
|
-
# rubocop:disable Layout/LineLength
|
|
79
78
|
return true if defined?(::SemanticLogger::Logger) && logger.is_a?(::SemanticLogger::Logger)
|
|
80
79
|
return true if defined?(::ActiveSupport::BroadcastLogger) && logger.is_a?(::ActiveSupport::BroadcastLogger)
|
|
81
|
-
# rubocop:enable Layout/LineLength
|
|
82
80
|
|
|
83
81
|
# NOTE: should provide `#debug` method.
|
|
84
82
|
return false unless logger.respond_to?(:debug)
|
|
@@ -152,7 +152,7 @@ module RedisQueuedLocks::Resource
|
|
|
152
152
|
# @param acquirer_position [Float]
|
|
153
153
|
# A time (epoch, seconds.milliseconds) that represents
|
|
154
154
|
# the acquirer position in lock request queue.
|
|
155
|
-
# @
|
|
155
|
+
# @param queue_ttl [Integer]
|
|
156
156
|
# In second.
|
|
157
157
|
# @return [Boolean]
|
|
158
158
|
# Is the lock request time limit has reached or not.
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
class RedisQueuedLocks::Swarm::FlushZombies < RedisQueuedLocks::Swarm::SwarmElement::Isolated
|
|
6
6
|
class << self
|
|
7
7
|
# @param redis_client [RedisClient]
|
|
8
|
-
# @
|
|
8
|
+
# @param zombie_ttl [Integer]
|
|
9
9
|
# @param lock_scan_size [Integer]
|
|
10
10
|
# @param queue_scan_size [Integer]
|
|
11
11
|
# @return [Hash<Symbol,Boolean|Set<String>]] Format:
|
|
@@ -154,7 +154,7 @@ class RedisQueuedLocks::Swarm::SwarmElement::Isolated
|
|
|
154
154
|
#
|
|
155
155
|
# @api private
|
|
156
156
|
# @since 1.9.0
|
|
157
|
-
# rubocop:disable Layout/ClassStructure, Lint/IneffectiveAccessModifier
|
|
157
|
+
# rubocop:disable Layout/ClassStructure, Lint/IneffectiveAccessModifier, Metrics/MethodLength
|
|
158
158
|
def self.swarm_loop(&main_loop_spawner)
|
|
159
159
|
# NOTE:
|
|
160
160
|
# This self.-related part of code is placed in the middle of class in order
|
|
@@ -177,7 +177,10 @@ class RedisQueuedLocks::Swarm::SwarmElement::Isolated
|
|
|
177
177
|
# @type var main_loop: Thread
|
|
178
178
|
RedisQueuedLocks::Utilities.thread_state(main_loop)
|
|
179
179
|
end
|
|
180
|
-
|
|
180
|
+
# NOTE: (will be reworked with Ractor::Port in the next RQL release (~1.17))
|
|
181
|
+
Ractor.yield({ # steep:ignore
|
|
182
|
+
main_loop: { alive: main_loop_alive, state: main_loop_state }
|
|
183
|
+
})
|
|
181
184
|
when :is_active
|
|
182
185
|
Ractor.yield(main_loop != nil && main_loop.alive?) # steep:ignore
|
|
183
186
|
when :start
|
|
@@ -191,7 +194,7 @@ class RedisQueuedLocks::Swarm::SwarmElement::Isolated
|
|
|
191
194
|
end
|
|
192
195
|
end
|
|
193
196
|
end
|
|
194
|
-
# rubocop:enable Layout/ClassStructure, Lint/IneffectiveAccessModifier
|
|
197
|
+
# rubocop:enable Layout/ClassStructure, Lint/IneffectiveAccessModifier, Metrics/MethodLength
|
|
195
198
|
|
|
196
199
|
# @return [Boolean]
|
|
197
200
|
#
|
|
@@ -36,7 +36,14 @@ module RedisQueuedLocks::Utilities
|
|
|
36
36
|
# @since 1.9.0
|
|
37
37
|
RACTOR_STATUS_PATTERN = /\A.*?\s(?<status>\w+)>\z/i
|
|
38
38
|
|
|
39
|
-
# @
|
|
39
|
+
# @return [Boolean]
|
|
40
|
+
#
|
|
41
|
+
# @api public
|
|
42
|
+
# @since ?.?.?
|
|
43
|
+
RUBY_OVER_4 = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('4.0.0')
|
|
44
|
+
|
|
45
|
+
# @yield
|
|
46
|
+
# @yieldreturn [Any]
|
|
40
47
|
# @return [Any]
|
|
41
48
|
#
|
|
42
49
|
# @api private
|
|
@@ -86,7 +93,7 @@ module RedisQueuedLocks::Utilities
|
|
|
86
93
|
# - "failed" (thread is terminated with an exception);
|
|
87
94
|
# See Thread#status official documentation.
|
|
88
95
|
#
|
|
89
|
-
# @param [Thread]
|
|
96
|
+
# @param thread [Thread]
|
|
90
97
|
# @return [String]
|
|
91
98
|
#
|
|
92
99
|
# @api private
|
|
@@ -97,4 +104,12 @@ module RedisQueuedLocks::Utilities
|
|
|
97
104
|
return 'failed' if status == nil
|
|
98
105
|
status #: String
|
|
99
106
|
end
|
|
107
|
+
|
|
108
|
+
# @return [Boolean]
|
|
109
|
+
#
|
|
110
|
+
# @api public
|
|
111
|
+
# @since 1.16.0
|
|
112
|
+
def ruby_over_4?
|
|
113
|
+
RUBY_OVER_4
|
|
114
|
+
end
|
|
100
115
|
end
|
|
@@ -2,11 +2,13 @@ module RedisQueuedLocks
|
|
|
2
2
|
module Utilities
|
|
3
3
|
RACTOR_LIVENESS_PATTERN: Regexp
|
|
4
4
|
RACTOR_STATUS_PATTERN: Regexp
|
|
5
|
+
RUBY_OVER_4: bool
|
|
5
6
|
|
|
6
7
|
def self?.run_non_critical: () { (?) -> untyped } -> untyped?
|
|
7
8
|
def self?.ractor_status: (Ractor ractor) -> String
|
|
8
9
|
def self?.ractor_alive?: (Ractor ractor) -> bool
|
|
9
10
|
def self?.thread_state: (Thread thread) -> String
|
|
10
11
|
def self?.clock_gettime: () -> Integer
|
|
12
|
+
def self?.ruby_over_4?: () -> bool
|
|
11
13
|
end
|
|
12
14
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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.16.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rustam Ibragimov
|
|
@@ -65,6 +65,7 @@ files:
|
|
|
65
65
|
- lib/redis_queued_locks/acquirer/is_queued.rb
|
|
66
66
|
- lib/redis_queued_locks/acquirer/keys.rb
|
|
67
67
|
- lib/redis_queued_locks/acquirer/lock_info.rb
|
|
68
|
+
- lib/redis_queued_locks/acquirer/lock_series_poc.rb
|
|
68
69
|
- lib/redis_queued_locks/acquirer/locks.rb
|
|
69
70
|
- lib/redis_queued_locks/acquirer/queue_info.rb
|
|
70
71
|
- lib/redis_queued_locks/acquirer/queues.rb
|