redis_queued_locks 1.6.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -1
- data/README.md +224 -53
- data/lib/redis_queued_locks/acquier/acquire_lock/dequeue_from_lock_queue/log_visitor.rb +36 -0
- data/lib/redis_queued_locks/acquier/acquire_lock/dequeue_from_lock_queue.rb +39 -0
- data/lib/redis_queued_locks/acquier/acquire_lock/instr_visitor.rb +151 -0
- data/lib/redis_queued_locks/acquier/acquire_lock/log_visitor.rb +192 -0
- data/lib/redis_queued_locks/acquier/acquire_lock/try_to_lock/log_visitor.rb +485 -0
- data/lib/redis_queued_locks/acquier/acquire_lock/try_to_lock.rb +73 -198
- data/lib/redis_queued_locks/acquier/acquire_lock/with_acq_timeout.rb +41 -7
- data/lib/redis_queued_locks/acquier/acquire_lock/yield_expire/log_visitor.rb +68 -0
- data/lib/redis_queued_locks/acquier/acquire_lock/yield_expire.rb +25 -26
- data/lib/redis_queued_locks/acquier/acquire_lock.rb +100 -112
- data/lib/redis_queued_locks/client.rb +83 -2
- data/lib/redis_queued_locks/utilities.rb +0 -1
- data/lib/redis_queued_locks/version.rb +2 -2
- data/lib/redis_queued_locks/watcher.rb +1 -0
- data/redis_queued_locks.gemspec +19 -3
- metadata +22 -6
@@ -2,16 +2,20 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
# @since 1.0.0
|
5
|
+
# @version 1.7.0
|
5
6
|
# rubocop:disable Metrics/ModuleLength
|
6
7
|
# rubocop:disable Metrics/MethodLength
|
7
8
|
# rubocop:disable Metrics/ClassLength
|
8
9
|
# rubocop:disable Metrics/BlockNesting
|
9
10
|
# rubocop:disable Style/IfInsideElse
|
10
11
|
module RedisQueuedLocks::Acquier::AcquireLock
|
12
|
+
require_relative 'acquire_lock/log_visitor'
|
13
|
+
require_relative 'acquire_lock/instr_visitor'
|
11
14
|
require_relative 'acquire_lock/delay_execution'
|
12
15
|
require_relative 'acquire_lock/with_acq_timeout'
|
13
16
|
require_relative 'acquire_lock/yield_expire'
|
14
17
|
require_relative 'acquire_lock/try_to_lock'
|
18
|
+
require_relative 'acquire_lock/dequeue_from_lock_queue'
|
15
19
|
|
16
20
|
# @since 1.0.0
|
17
21
|
extend TryToLock
|
@@ -21,8 +25,8 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
21
25
|
extend YieldExpire
|
22
26
|
# @since 1.0.0
|
23
27
|
extend WithAcqTimeout
|
24
|
-
# @since 1.
|
25
|
-
extend
|
28
|
+
# @since 1.7.0
|
29
|
+
extend DequeueFromLockQueue
|
26
30
|
|
27
31
|
class << self
|
28
32
|
# @param redis [RedisClient]
|
@@ -65,6 +69,10 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
65
69
|
# @option meta [NilClass,Hash<String|Symbol,Any>]
|
66
70
|
# - A custom metadata wich will be passed to the lock data in addition to the existing data;
|
67
71
|
# - Metadata can not contain reserved lock data keys;
|
72
|
+
# @option detailed_acq_timeout_error [Boolean]
|
73
|
+
# - Add additional data to the acquirement timeout error such as the current lock queue state
|
74
|
+
# and the required lock state;
|
75
|
+
# - See `config[:detailed_acq_timeout_error]` for details;
|
68
76
|
# @option logger [::Logger,#debug]
|
69
77
|
# - Logger object used from the configuration layer (see config[:logger]);
|
70
78
|
# - See `RedisQueuedLocks::Logging::VoidLogger` for example;
|
@@ -86,6 +94,16 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
86
94
|
# - `:extendable_work_through`;
|
87
95
|
# - `:wait_for_lock`;
|
88
96
|
# - `:dead_locking`;
|
97
|
+
# @option access_strategy [Symbol]
|
98
|
+
# - The way in which the lock will be obtained;
|
99
|
+
# - By default it uses `:queued` strategy;
|
100
|
+
# - Supports following strategies:
|
101
|
+
# - `:queued` (FIFO): the classic queued behavior (default), your lock will be
|
102
|
+
# obitaned if you are first in queue and the required lock is free;
|
103
|
+
# - `:random` (RANDOM): obtain a lock without checking the positions in the queue
|
104
|
+
# (but with checking the limist, retries, timeouts and so on). if lock is
|
105
|
+
# free to obtain - it will be obtained;
|
106
|
+
# - pre-configured in `config[:default_access_strategy]`;
|
89
107
|
# @option log_sampling_enabled [Boolean]
|
90
108
|
# - enables <log sampling>: only the configured percent of RQL cases will be logged;
|
91
109
|
# - disabled by default;
|
@@ -129,7 +147,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
129
147
|
#
|
130
148
|
# @api private
|
131
149
|
# @since 1.0.0
|
132
|
-
# @version 1.
|
150
|
+
# @version 1.8.0
|
133
151
|
def acquire_lock(
|
134
152
|
redis,
|
135
153
|
lock_name,
|
@@ -149,10 +167,12 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
149
167
|
identity:,
|
150
168
|
fail_fast:,
|
151
169
|
meta:,
|
170
|
+
detailed_acq_timeout_error:,
|
152
171
|
instrument:,
|
153
172
|
logger:,
|
154
173
|
log_lock_try:,
|
155
174
|
conflict_strategy:,
|
175
|
+
access_strategy:,
|
156
176
|
log_sampling_enabled:,
|
157
177
|
log_sampling_percent:,
|
158
178
|
log_sampler:,
|
@@ -231,53 +251,52 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
231
251
|
|
232
252
|
acq_dequeue = proc do
|
233
253
|
dequeue_from_lock_queue(
|
234
|
-
redis,
|
254
|
+
redis,
|
255
|
+
logger,
|
235
256
|
lock_key,
|
236
257
|
lock_key_queue,
|
237
258
|
queue_ttl,
|
238
259
|
acquier_id,
|
260
|
+
access_strategy,
|
239
261
|
log_sampled,
|
240
262
|
instr_sampled
|
241
263
|
)
|
242
264
|
end
|
243
265
|
|
244
|
-
|
245
|
-
logger
|
246
|
-
|
247
|
-
|
248
|
-
"queue_ttl => #{queue_ttl} " \
|
249
|
-
"acq_id => '#{acquier_id}'"
|
250
|
-
end
|
251
|
-
end if log_sampled
|
266
|
+
LogVisitor.start_lock_obtaining(
|
267
|
+
logger, log_sampled,
|
268
|
+
lock_key, queue_ttl, acquier_id, access_strategy
|
269
|
+
)
|
252
270
|
|
253
271
|
# Step 2: try to lock with timeout
|
254
|
-
with_acq_timeout(
|
272
|
+
with_acq_timeout(
|
273
|
+
redis,
|
274
|
+
timeout,
|
275
|
+
lock_key,
|
276
|
+
lock_name,
|
277
|
+
raise_errors,
|
278
|
+
detailed_acq_timeout_error,
|
279
|
+
on_timeout: acq_dequeue
|
280
|
+
) do
|
255
281
|
acq_start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
|
256
282
|
|
257
283
|
# Step 2.1: cyclically try to obtain the lock
|
258
284
|
while acq_process[:should_try]
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
"acq_id => '{#{acquier_id}'"
|
265
|
-
end
|
266
|
-
end if log_sampled
|
285
|
+
|
286
|
+
LogVisitor.start_try_to_lock_cycle(
|
287
|
+
logger, log_sampled,
|
288
|
+
lock_key, queue_ttl, acquier_id, access_strategy
|
289
|
+
)
|
267
290
|
|
268
291
|
# Step 2.X: check the actual score: is it in queue ttl limit or not?
|
269
292
|
if RedisQueuedLocks::Resource.dead_score_reached?(acquier_position, queue_ttl)
|
270
293
|
# Step 2.X.X: dead score reached => re-queue the lock request with the new score;
|
271
294
|
acquier_position = RedisQueuedLocks::Resource.calc_initial_acquier_position
|
272
295
|
|
273
|
-
|
274
|
-
logger
|
275
|
-
|
276
|
-
|
277
|
-
"queue_ttl => #{queue_ttl} " \
|
278
|
-
"acq_id => '#{acquier_id}'"
|
279
|
-
end
|
280
|
-
end if log_sampled
|
296
|
+
LogVisitor.dead_score_reached__reset_acquier_position(
|
297
|
+
logger, log_sampled,
|
298
|
+
lock_key, queue_ttl, acquier_id, access_strategy
|
299
|
+
)
|
281
300
|
end
|
282
301
|
|
283
302
|
try_to_lock(
|
@@ -292,6 +311,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
292
311
|
queue_ttl,
|
293
312
|
fail_fast,
|
294
313
|
conflict_strategy,
|
314
|
+
access_strategy,
|
295
315
|
meta,
|
296
316
|
log_sampled,
|
297
317
|
instr_sampled
|
@@ -309,72 +329,38 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
309
329
|
# Step X: (instrumentation)
|
310
330
|
if acq_process[:result][:process] == :extendable_conflict_work_through
|
311
331
|
# instrumetnation: (reentrant lock with ttl extension)
|
312
|
-
|
313
|
-
logger
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
run_non_critical do
|
323
|
-
instrumenter.notify('redis_queued_locks.extendable_reentrant_lock_obtained', {
|
324
|
-
lock_key: result[:lock_key],
|
325
|
-
ttl: result[:ttl],
|
326
|
-
acq_id: result[:acq_id],
|
327
|
-
ts: result[:ts],
|
328
|
-
acq_time: acq_time,
|
329
|
-
instrument:
|
330
|
-
})
|
331
|
-
end if instr_sampled
|
332
|
+
LogVisitor.extendable_reentrant_lock_obtained(
|
333
|
+
logger, log_sampled,
|
334
|
+
result[:lock_key], queue_ttl, acquier_id, acq_time, access_strategy
|
335
|
+
)
|
336
|
+
InstrVisitor.extendable_reentrant_lock_obtained(
|
337
|
+
instrumenter, instr_sampled,
|
338
|
+
result[:lock_key], result[:ttl], result[:acq_id], result[:ts], acq_time,
|
339
|
+
instrument
|
340
|
+
)
|
332
341
|
elsif acq_process[:result][:process] == :conflict_work_through
|
333
342
|
# instrumetnation: (reentrant lock without ttl extension)
|
334
|
-
|
335
|
-
logger
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
run_non_critical do
|
345
|
-
instrumenter.notify('redis_queued_locks.reentrant_lock_obtained', {
|
346
|
-
lock_key: result[:lock_key],
|
347
|
-
ttl: result[:ttl],
|
348
|
-
acq_id: result[:acq_id],
|
349
|
-
ts: result[:ts],
|
350
|
-
acq_time: acq_time,
|
351
|
-
instrument:
|
352
|
-
})
|
353
|
-
end if instr_sampled
|
343
|
+
LogVisitor.reentrant_lock_obtained(
|
344
|
+
logger, log_sampled,
|
345
|
+
result[:lock_key], queue_ttl, acquier_id, acq_time, access_strategy
|
346
|
+
)
|
347
|
+
InstrVisitor.reentrant_lock_obtained(
|
348
|
+
instrumenter, instr_sampled,
|
349
|
+
result[:lock_key], result[:ttl], result[:acq_id], result[:ts], acq_time,
|
350
|
+
instrument
|
351
|
+
)
|
354
352
|
else
|
355
353
|
# instrumentation: (classic lock obtain)
|
356
354
|
# NOTE: classic is: acq_process[:result][:process] == :lock_obtaining
|
357
|
-
|
358
|
-
logger
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
# Step X (instrumentation): lock obtained
|
368
|
-
run_non_critical do
|
369
|
-
instrumenter.notify('redis_queued_locks.lock_obtained', {
|
370
|
-
lock_key: result[:lock_key],
|
371
|
-
ttl: result[:ttl],
|
372
|
-
acq_id: result[:acq_id],
|
373
|
-
ts: result[:ts],
|
374
|
-
acq_time: acq_time,
|
375
|
-
instrument:
|
376
|
-
})
|
377
|
-
end if instr_sampled
|
355
|
+
LogVisitor.lock_obtained(
|
356
|
+
logger, log_sampled,
|
357
|
+
result[:lock_key], queue_ttl, acquier_id, acq_time, access_strategy
|
358
|
+
)
|
359
|
+
InstrVisitor.lock_obtained(
|
360
|
+
instrumenter, instr_sampled,
|
361
|
+
result[:lock_key], result[:ttl], result[:acq_id], result[:ts], acq_time,
|
362
|
+
instrument
|
363
|
+
)
|
378
364
|
end
|
379
365
|
|
380
366
|
# Step 2.1.a: successfully acquired => build the result
|
@@ -478,10 +464,12 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
478
464
|
logger,
|
479
465
|
lock_key,
|
480
466
|
acquier_id,
|
467
|
+
access_strategy,
|
481
468
|
timed,
|
482
469
|
ttl_shift,
|
483
470
|
ttl,
|
484
471
|
queue_ttl,
|
472
|
+
meta,
|
485
473
|
log_sampled,
|
486
474
|
instr_sampled,
|
487
475
|
should_expire, # NOTE: should expire the lock after the block execution
|
@@ -499,30 +487,30 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
499
487
|
if acq_process[:result][:process] == :extendable_conflict_work_through ||
|
500
488
|
acq_process[:result][:process] == :conflict_work_through
|
501
489
|
# Step X (instrumentation): reentrant_lock_hold_completes
|
502
|
-
|
503
|
-
instrumenter
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
490
|
+
InstrVisitor.reentrant_lock_hold_completes(
|
491
|
+
instrumenter,
|
492
|
+
instr_sampled,
|
493
|
+
acq_process[:lock_info][:lock_key],
|
494
|
+
acq_process[:lock_info][:ttl],
|
495
|
+
acq_process[:lock_info][:acq_id],
|
496
|
+
acq_process[:lock_info][:ts],
|
497
|
+
acq_process[:acq_time],
|
498
|
+
acq_process[:hold_time],
|
499
|
+
instrument
|
500
|
+
)
|
513
501
|
else
|
514
502
|
# Step X (instrumentation): lock_hold_and_release
|
515
|
-
|
516
|
-
instrumenter
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
503
|
+
InstrVisitor.lock_hold_and_release(
|
504
|
+
instrumenter,
|
505
|
+
instr_sampled,
|
506
|
+
acq_process[:lock_info][:lock_key],
|
507
|
+
acq_process[:lock_info][:ttl],
|
508
|
+
acq_process[:lock_info][:acq_id],
|
509
|
+
acq_process[:lock_info][:ts],
|
510
|
+
acq_process[:lock_info][:lock_key],
|
511
|
+
acq_process[:acq_time],
|
512
|
+
instrument
|
513
|
+
)
|
526
514
|
end
|
527
515
|
end
|
528
516
|
else
|
@@ -14,6 +14,7 @@ class RedisQueuedLocks::Client
|
|
14
14
|
setting :try_to_lock_timeout, 10 # NOTE: in seconds
|
15
15
|
setting :default_lock_ttl, 5_000 # NOTE: in milliseconds
|
16
16
|
setting :default_queue_ttl, 15 # NOTE: in seconds
|
17
|
+
setting :detailed_acq_timeout_error, false
|
17
18
|
setting :lock_release_batch_size, 100
|
18
19
|
setting :key_extraction_batch_size, 500
|
19
20
|
setting :instrumenter, RedisQueuedLocks::Instrument::VoidNotifier
|
@@ -23,6 +24,7 @@ class RedisQueuedLocks::Client
|
|
23
24
|
setting :dead_request_ttl, (1 * 24 * 60 * 60 * 1000) # NOTE: 1 day in milliseconds
|
24
25
|
setting :is_timed_by_default, false
|
25
26
|
setting :default_conflict_strategy, :wait_for_lock
|
27
|
+
setting :default_access_strategy, :queued
|
26
28
|
setting :log_sampling_enabled, false
|
27
29
|
setting :log_sampling_percent, 15
|
28
30
|
setting :log_sampler, RedisQueuedLocks::Logging::Sampler
|
@@ -36,6 +38,7 @@ class RedisQueuedLocks::Client
|
|
36
38
|
validate('try_to_lock_timeout') { |val| val == nil || (val.is_a?(::Integer) && val >= 0) }
|
37
39
|
validate('default_lock_tt', :integer)
|
38
40
|
validate('default_queue_ttl', :integer)
|
41
|
+
validate('detailed_acq_timeout_error', :boolean)
|
39
42
|
validate('lock_release_batch_size', :integer)
|
40
43
|
validate('instrumenter') { |val| RedisQueuedLocks::Instrument.valid_interface?(val) }
|
41
44
|
validate('uniq_identifier', :proc)
|
@@ -57,6 +60,12 @@ class RedisQueuedLocks::Client
|
|
57
60
|
val == :dead_locking
|
58
61
|
# rubocop:enable Layout/MultilineOperationIndentation
|
59
62
|
end
|
63
|
+
validate('default_access_strategy') do |val|
|
64
|
+
# rubocop:disable Layout/MultilineOperationIndentation
|
65
|
+
val == :queued ||
|
66
|
+
val == :random
|
67
|
+
# rubocop:enable Layout/MultilineOperationIndentation
|
68
|
+
end
|
60
69
|
end
|
61
70
|
|
62
71
|
# @return [RedisClient]
|
@@ -88,6 +97,39 @@ class RedisQueuedLocks::Client
|
|
88
97
|
@redis_client = redis_client
|
89
98
|
end
|
90
99
|
|
100
|
+
# Retrun the current acquirer identifier.
|
101
|
+
#
|
102
|
+
# @option process_id [Integer,Any] Process identifier.
|
103
|
+
# @option thread_id [Integer,Any] Thread identifier.
|
104
|
+
# @option fiber_id [Integer,Any] Fiber identifier.
|
105
|
+
# @option ractor_id [Integer,Any] Ractor identifier.
|
106
|
+
# @option identity [String] Unique per-process string. See `config[:uniq_identifier]`.
|
107
|
+
# @return [String]
|
108
|
+
#
|
109
|
+
# @see RedisQueuedLocks::Resource.get_process_id
|
110
|
+
# @see RedisQueuedLocks::Resource.get_thread_id
|
111
|
+
# @see RedisQueuedLocks::Resource.get_fiber_id
|
112
|
+
# @see RedisQueuedLocks::Resource.get_ractor_id
|
113
|
+
# @see RedisQueuedLocks::Client#uniq_identity
|
114
|
+
#
|
115
|
+
# @api public
|
116
|
+
# @since 1.8.0
|
117
|
+
def current_acquier_id(
|
118
|
+
process_id: RedisQueuedLocks::Resource.get_process_id,
|
119
|
+
thread_id: RedisQueuedLocks::Resource.get_thread_id,
|
120
|
+
fiber_id: RedisQueuedLocks::Resource.get_fiber_id,
|
121
|
+
ractor_id: RedisQueuedLocks::Resource.get_ractor_id,
|
122
|
+
identity: uniq_identity
|
123
|
+
)
|
124
|
+
RedisQueuedLocks::Resource.acquier_identifier(
|
125
|
+
process_id,
|
126
|
+
thread_id,
|
127
|
+
fiber_id,
|
128
|
+
ractor_id,
|
129
|
+
identity
|
130
|
+
)
|
131
|
+
end
|
132
|
+
|
91
133
|
# @param lock_name [String]
|
92
134
|
# Lock name to be obtained.
|
93
135
|
# @option ttl [Integer]
|
@@ -126,9 +168,40 @@ class RedisQueuedLocks::Client
|
|
126
168
|
# - `:wait_for_lock` - (default) - work in classic way
|
127
169
|
# (with timeouts, retry delays, retry limits, etc - in classic way :));
|
128
170
|
# - `:dead_locking` - fail with deadlock exception;
|
171
|
+
# @option access_strategy [Symbol]
|
172
|
+
# - The way in which the lock will be obtained;
|
173
|
+
# - By default it uses `:queued` strategy;
|
174
|
+
# - Supports following strategies:
|
175
|
+
# - `:queued` (FIFO): the classic queued behavior (default), your lock will be
|
176
|
+
# obitaned if you are first in queue and the required lock is free;
|
177
|
+
# - `:random` (RANDOM): obtain a lock without checking the positions in the queue
|
178
|
+
# (but with checking the limist, retries, timeouts and so on). if lock is
|
179
|
+
# free to obtain - it will be obtained;
|
180
|
+
# - pre-configured in `config[:default_access_strategy]`;
|
129
181
|
# @option meta [NilClass,Hash<String|Symbol,Any>]
|
130
182
|
# - A custom metadata wich will be passed to the lock data in addition to the existing data;
|
131
183
|
# - Metadata can not contain reserved lock data keys;
|
184
|
+
# @option detailed_acq_timeout_error [Boolean]
|
185
|
+
# - When the lock acquirement try reached the acquirement time limit (:timeout option) the
|
186
|
+
# `RedisQueuedLocks::LockAcquirementTimeoutError` is raised (when `raise_errors` option
|
187
|
+
# set to `true`). The error message contains the lock key name and the timeout value).
|
188
|
+
# - <true> option adds the additional details to the error message:
|
189
|
+
# - current lock queue state (you can see which acquirer blocks your request and
|
190
|
+
# how much acquirers are in queue);
|
191
|
+
# - current lock data stored inside (for example: you can check the current acquirer and
|
192
|
+
# the lock meta state if you store some additional data there);
|
193
|
+
# - Realized as an option because of the additional lock data requires two additional Redis
|
194
|
+
# queries: (1) get the current lock from redis and (2) fetch the lock queue state;
|
195
|
+
# - These two additional Redis queries has async nature so you can receive
|
196
|
+
# inconsistent data of the lock and of the lock queue in your error emssage because:
|
197
|
+
# - required lock can be released after the error moment and before the error message build;
|
198
|
+
# - required lock can be obtained by other process after the error moment and
|
199
|
+
# before the error message build;
|
200
|
+
# - required lock queue can reach a state when the blocking acquirer start to obtain the lock
|
201
|
+
# and moved from the lock queue after the error moment and before the error message build;
|
202
|
+
# - You should consider the async nature of this error message and should use received data
|
203
|
+
# from error message correspondingly;
|
204
|
+
# - pre-configred in `config[:detailed_acq_timeout_error]`;
|
132
205
|
# @option logger [::Logger,#debug]
|
133
206
|
# - Logger object used from the configuration layer (see config[:logger]);
|
134
207
|
# - See `RedisQueuedLocks::Logging::VoidLogger` for example;
|
@@ -189,7 +262,7 @@ class RedisQueuedLocks::Client
|
|
189
262
|
#
|
190
263
|
# @api public
|
191
264
|
# @since 1.0.0
|
192
|
-
# @version 1.
|
265
|
+
# @version 1.8.0
|
193
266
|
# rubocop:disable Metrics/MethodLength
|
194
267
|
def lock(
|
195
268
|
lock_name,
|
@@ -203,8 +276,10 @@ class RedisQueuedLocks::Client
|
|
203
276
|
raise_errors: false,
|
204
277
|
fail_fast: false,
|
205
278
|
conflict_strategy: config[:default_conflict_strategy],
|
279
|
+
access_strategy: config[:default_access_strategy],
|
206
280
|
identity: uniq_identity,
|
207
281
|
meta: nil,
|
282
|
+
detailed_acq_timeout_error: config[:detailed_acq_timeout_error],
|
208
283
|
logger: config[:logger],
|
209
284
|
log_lock_try: config[:log_lock_try],
|
210
285
|
instrumenter: config[:instrumenter],
|
@@ -236,7 +311,9 @@ class RedisQueuedLocks::Client
|
|
236
311
|
identity:,
|
237
312
|
fail_fast:,
|
238
313
|
conflict_strategy:,
|
314
|
+
access_strategy:,
|
239
315
|
meta:,
|
316
|
+
detailed_acq_timeout_error:,
|
240
317
|
logger:,
|
241
318
|
log_lock_try:,
|
242
319
|
instrument:,
|
@@ -255,7 +332,7 @@ class RedisQueuedLocks::Client
|
|
255
332
|
#
|
256
333
|
# @api public
|
257
334
|
# @since 1.0.0
|
258
|
-
# @version 1.
|
335
|
+
# @version 1.8.0
|
259
336
|
# rubocop:disable Metrics/MethodLength
|
260
337
|
def lock!(
|
261
338
|
lock_name,
|
@@ -268,9 +345,11 @@ class RedisQueuedLocks::Client
|
|
268
345
|
retry_jitter: config[:retry_jitter],
|
269
346
|
fail_fast: false,
|
270
347
|
conflict_strategy: config[:default_conflict_strategy],
|
348
|
+
access_strategy: config[:default_access_strategy],
|
271
349
|
identity: uniq_identity,
|
272
350
|
instrumenter: config[:instrumenter],
|
273
351
|
meta: nil,
|
352
|
+
detailed_acq_timeout_error: config[:detailed_acq_timeout_error],
|
274
353
|
logger: config[:logger],
|
275
354
|
log_lock_try: config[:log_lock_try],
|
276
355
|
instrument: nil,
|
@@ -297,9 +376,11 @@ class RedisQueuedLocks::Client
|
|
297
376
|
logger:,
|
298
377
|
log_lock_try:,
|
299
378
|
meta:,
|
379
|
+
detailed_acq_timeout_error:,
|
300
380
|
instrument:,
|
301
381
|
instrumenter:,
|
302
382
|
conflict_strategy:,
|
383
|
+
access_strategy:,
|
303
384
|
log_sampling_enabled:,
|
304
385
|
log_sampling_percent:,
|
305
386
|
log_sampler:,
|
@@ -0,0 +1 @@
|
|
1
|
+
# frozen_string_literal: true
|
data/redis_queued_locks.gemspec
CHANGED
@@ -10,9 +10,25 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.authors = ['Rustam Ibragimov']
|
11
11
|
spec.email = ['iamdaiver@gmail.com']
|
12
12
|
|
13
|
-
spec.summary
|
14
|
-
|
15
|
-
|
13
|
+
spec.summary =
|
14
|
+
'Distributed locks with "prioritized lock acquisition queue" ' \
|
15
|
+
'capabilities based on the Redis Database.'
|
16
|
+
|
17
|
+
spec.description =
|
18
|
+
'|> Distributed locks with "prioritized lock acquisition queue" capabilities ' \
|
19
|
+
'based on the Redis Database. ' \
|
20
|
+
'|> Each lock request is put into the request queue ' \
|
21
|
+
'(each lock is hosted by it\'s own queue separately from other queues) and processed ' \
|
22
|
+
'in order of their priority (FIFO). ' \
|
23
|
+
'|> Each lock request lives some period of time (RTTL) ' \
|
24
|
+
'(with requeue capabilities) which guarantees the request queue will never be stacked. ' \
|
25
|
+
'|> In addition to the classic `queued` (FIFO) strategy RQL supports ' \
|
26
|
+
'`random` (RANDOM) lock obtaining strategy when any acquirer from the lock queue ' \
|
27
|
+
'can obtain the lock regardless the position in the queue. ' \
|
28
|
+
'|> Provides flexible invocation flow, parametrized limits ' \
|
29
|
+
'(lock request ttl, lock ttl, queue ttl, lock attempts limit, fast failing, etc), ' \
|
30
|
+
'logging and instrumentation.'
|
31
|
+
|
16
32
|
spec.homepage = 'https://github.com/0exp/redis_queued_locks'
|
17
33
|
spec.license = 'MIT'
|
18
34
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis_queued_locks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rustam Ibragimov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-06-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis-client
|
@@ -38,8 +38,16 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0.28'
|
41
|
-
description: Distributed locks with "lock acquisition queue" capabilities
|
42
|
-
the Redis Database.
|
41
|
+
description: '|> Distributed locks with "prioritized lock acquisition queue" capabilities
|
42
|
+
based on the Redis Database. |> Each lock request is put into the request queue
|
43
|
+
(each lock is hosted by it''s own queue separately from other queues) and processed
|
44
|
+
in order of their priority (FIFO). |> Each lock request lives some period of time
|
45
|
+
(RTTL) (with requeue capabilities) which guarantees the request queue will never
|
46
|
+
be stacked. |> In addition to the classic `queued` (FIFO) strategy RQL supports
|
47
|
+
`random` (RANDOM) lock obtaining strategy when any acquirer from the lock queue
|
48
|
+
can obtain the lock regardless the position in the queue. |> Provides flexible invocation
|
49
|
+
flow, parametrized limits (lock request ttl, lock ttl, queue ttl, lock attempts
|
50
|
+
limit, fast failing, etc), logging and instrumentation.'
|
43
51
|
email:
|
44
52
|
- iamdaiver@gmail.com
|
45
53
|
executables: []
|
@@ -58,9 +66,15 @@ files:
|
|
58
66
|
- lib/redis_queued_locks/acquier.rb
|
59
67
|
- lib/redis_queued_locks/acquier/acquire_lock.rb
|
60
68
|
- lib/redis_queued_locks/acquier/acquire_lock/delay_execution.rb
|
69
|
+
- lib/redis_queued_locks/acquier/acquire_lock/dequeue_from_lock_queue.rb
|
70
|
+
- lib/redis_queued_locks/acquier/acquire_lock/dequeue_from_lock_queue/log_visitor.rb
|
71
|
+
- lib/redis_queued_locks/acquier/acquire_lock/instr_visitor.rb
|
72
|
+
- lib/redis_queued_locks/acquier/acquire_lock/log_visitor.rb
|
61
73
|
- lib/redis_queued_locks/acquier/acquire_lock/try_to_lock.rb
|
74
|
+
- lib/redis_queued_locks/acquier/acquire_lock/try_to_lock/log_visitor.rb
|
62
75
|
- lib/redis_queued_locks/acquier/acquire_lock/with_acq_timeout.rb
|
63
76
|
- lib/redis_queued_locks/acquier/acquire_lock/yield_expire.rb
|
77
|
+
- lib/redis_queued_locks/acquier/acquire_lock/yield_expire/log_visitor.rb
|
64
78
|
- lib/redis_queued_locks/acquier/clear_dead_requests.rb
|
65
79
|
- lib/redis_queued_locks/acquier/extend_lock_ttl.rb
|
66
80
|
- lib/redis_queued_locks/acquier/is_locked.rb
|
@@ -87,6 +101,7 @@ files:
|
|
87
101
|
- lib/redis_queued_locks/resource.rb
|
88
102
|
- lib/redis_queued_locks/utilities.rb
|
89
103
|
- lib/redis_queued_locks/version.rb
|
104
|
+
- lib/redis_queued_locks/watcher.rb
|
90
105
|
- redis_queued_locks.gemspec
|
91
106
|
homepage: https://github.com/0exp/redis_queued_locks
|
92
107
|
licenses:
|
@@ -110,8 +125,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
110
125
|
- !ruby/object:Gem::Version
|
111
126
|
version: '0'
|
112
127
|
requirements: []
|
113
|
-
rubygems_version: 3.
|
128
|
+
rubygems_version: 3.5.1
|
114
129
|
signing_key:
|
115
130
|
specification_version: 4
|
116
|
-
summary:
|
131
|
+
summary: Distributed locks with "prioritized lock acquisition queue" capabilities
|
132
|
+
based on the Redis Database.
|
117
133
|
test_files: []
|