redis_queued_locks 1.6.0 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +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: []
|