redis_queued_locks 1.7.0 → 1.9.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/CHANGELOG.md +60 -1
  4. data/README.md +485 -46
  5. data/lib/redis_queued_locks/acquier/acquire_lock/dequeue_from_lock_queue/log_visitor.rb +4 -0
  6. data/lib/redis_queued_locks/acquier/acquire_lock/dequeue_from_lock_queue.rb +4 -1
  7. data/lib/redis_queued_locks/acquier/acquire_lock/instr_visitor.rb +20 -5
  8. data/lib/redis_queued_locks/acquier/acquire_lock/log_visitor.rb +24 -0
  9. data/lib/redis_queued_locks/acquier/acquire_lock/try_to_lock/log_visitor.rb +56 -0
  10. data/lib/redis_queued_locks/acquier/acquire_lock/try_to_lock.rb +37 -30
  11. data/lib/redis_queued_locks/acquier/acquire_lock/with_acq_timeout.rb +41 -7
  12. data/lib/redis_queued_locks/acquier/acquire_lock/yield_expire/log_visitor.rb +8 -0
  13. data/lib/redis_queued_locks/acquier/acquire_lock/yield_expire.rb +21 -9
  14. data/lib/redis_queued_locks/acquier/acquire_lock.rb +61 -22
  15. data/lib/redis_queued_locks/acquier/clear_dead_requests.rb +5 -1
  16. data/lib/redis_queued_locks/acquier/extend_lock_ttl.rb +5 -1
  17. data/lib/redis_queued_locks/acquier/lock_info.rb +4 -3
  18. data/lib/redis_queued_locks/acquier/locks.rb +2 -2
  19. data/lib/redis_queued_locks/acquier/queue_info.rb +2 -2
  20. data/lib/redis_queued_locks/acquier/release_all_locks.rb +12 -2
  21. data/lib/redis_queued_locks/acquier/release_lock.rb +12 -2
  22. data/lib/redis_queued_locks/client.rb +320 -10
  23. data/lib/redis_queued_locks/errors.rb +8 -0
  24. data/lib/redis_queued_locks/instrument.rb +8 -1
  25. data/lib/redis_queued_locks/logging.rb +8 -1
  26. data/lib/redis_queued_locks/resource.rb +59 -1
  27. data/lib/redis_queued_locks/swarm/acquirers.rb +44 -0
  28. data/lib/redis_queued_locks/swarm/flush_zombies.rb +133 -0
  29. data/lib/redis_queued_locks/swarm/probe_hosts.rb +69 -0
  30. data/lib/redis_queued_locks/swarm/redis_client_builder.rb +67 -0
  31. data/lib/redis_queued_locks/swarm/supervisor.rb +83 -0
  32. data/lib/redis_queued_locks/swarm/swarm_element/isolated.rb +287 -0
  33. data/lib/redis_queued_locks/swarm/swarm_element/threaded.rb +351 -0
  34. data/lib/redis_queued_locks/swarm/swarm_element.rb +8 -0
  35. data/lib/redis_queued_locks/swarm/zombie_info.rb +145 -0
  36. data/lib/redis_queued_locks/swarm.rb +241 -0
  37. data/lib/redis_queued_locks/utilities/lock.rb +22 -0
  38. data/lib/redis_queued_locks/utilities.rb +75 -0
  39. data/lib/redis_queued_locks/version.rb +2 -2
  40. data/lib/redis_queued_locks.rb +2 -0
  41. data/redis_queued_locks.gemspec +6 -10
  42. metadata +24 -6
  43. data/lib/redis_queued_locks/watcher.rb +0 -1
@@ -40,6 +40,9 @@ module RedisQueuedLocks::Acquier::ReleaseLock
40
40
  # - you can provide your own log sampler with bettter algorithm that should realize
41
41
  # `sampling_happened?(percent) => boolean` interface
42
42
  # (see `RedisQueuedLocks::Logging::Sampler` for example);
43
+ # @param log_sample_this [Boolean]
44
+ # - marks the method that everything should be logged despite the enabled log sampling;
45
+ # - makes sense when log sampling is enabled;
43
46
  # @param instr_sampling_enabled [Boolean]
44
47
  # - enables <instrumentaion sampling>: only the configured percent
45
48
  # of RQL cases will be instrumented;
@@ -58,6 +61,10 @@ module RedisQueuedLocks::Acquier::ReleaseLock
58
61
  # - you can provide your own log sampler with bettter algorithm that should realize
59
62
  # `sampling_happened?(percent) => boolean` interface
60
63
  # (see `RedisQueuedLocks::Instrument::Sampler` for example);
64
+ # @param instr_sample_this [Boolean]
65
+ # - marks the method that everything should be instrumneted
66
+ # despite the enabled instrumentation sampling;
67
+ # - makes sense when instrumentation sampling is enabled;
61
68
  # @return [RedisQueuedLocks::Data,Hash<Symbol,Boolean<Hash<Symbol,Numeric|String|Symbol>>]
62
69
  # Format: { ok: true/false, result: Hash<Symbol,Numeric|String|Symbol> }
63
70
  #
@@ -73,9 +80,11 @@ module RedisQueuedLocks::Acquier::ReleaseLock
73
80
  log_sampling_enabled,
74
81
  log_sampling_percent,
75
82
  log_sampler,
83
+ log_sample_this,
76
84
  instr_sampling_enabled,
77
85
  instr_sampling_percent,
78
- instr_sampler
86
+ instr_sampler,
87
+ instr_sample_this
79
88
  )
80
89
  lock_key = RedisQueuedLocks::Resource.prepare_lock_key(lock_name)
81
90
  lock_key_queue = RedisQueuedLocks::Resource.prepare_lock_queue(lock_name)
@@ -84,10 +93,11 @@ module RedisQueuedLocks::Acquier::ReleaseLock
84
93
  fully_release_lock(redis, lock_key, lock_key_queue) => { ok:, result: }
85
94
  time_at = Time.now.to_f
86
95
  rel_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
87
- rel_time = ((rel_end_time - rel_start_time) / 1_000).ceil(2)
96
+ rel_time = ((rel_end_time - rel_start_time) / 1_000.0).ceil(2)
88
97
 
89
98
  instr_sampled = RedisQueuedLocks::Instrument.should_instrument?(
90
99
  instr_sampling_enabled,
100
+ instr_sample_this,
91
101
  instr_sampling_percent,
92
102
  instr_sampler
93
103
  )
@@ -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
@@ -31,12 +32,63 @@ class RedisQueuedLocks::Client
31
32
  setting :instr_sampling_percent, 15
32
33
  setting :instr_sampler, RedisQueuedLocks::Instrument::Sampler
33
34
 
35
+ setting :swarm do
36
+ setting :auto_swarm, false
37
+ setting :supervisor do
38
+ setting :liveness_probing_period, 2 # NOTE: in seconds
39
+ end
40
+ setting :probe_hosts do
41
+ setting :enabled_for_swarm, true
42
+ setting :redis_config do
43
+ setting :sentinel, false
44
+ setting :pooled, false
45
+ setting :config, {}
46
+ setting :pool_config, {}
47
+ end
48
+ setting :probe_period, 2 # NOTE: in seconds
49
+ end
50
+ setting :flush_zombies do
51
+ setting :enabled_for_swarm, true
52
+ setting :redis_config do
53
+ setting :sentinel, false
54
+ setting :pooled, false
55
+ setting :config, {}
56
+ setting :pool_config, {}
57
+ end
58
+ setting :zombie_ttl, 15_000 # NOTE: in milliseconds
59
+ setting :zombie_lock_scan_size, 500
60
+ setting :zombie_queue_scan_size, 500
61
+ setting :zombie_flush_period, 10 # NOTE: in seconds
62
+ end
63
+ end
64
+
65
+ validate('swarm.auto_swarm', :boolean)
66
+ validate('swarm.visor.liveness_probing_period', :integer)
67
+
68
+ validate('swarm.probe_hosts.enabled_for_swarm', :boolean)
69
+ validate('swarm.probe_hosts.redis_config.sentinel', :boolean)
70
+ validate('swarm.probe_hosts.redis_config.pooled', :boolean)
71
+ validate('swarm.probe_hosts.redis_config.config', :hash)
72
+ validate('swarm.probe_hosts.redis_config.pool_config', :hash)
73
+ validate('swarm.probe_hosts.probe_period', :integer)
74
+
75
+ validate('swarm.flush_zombies.enabled_for_swarm', :boolean)
76
+ validate('swarm.flush_zombies.redis_config.sentinel', :boolean)
77
+ validate('swarm.flush_zombies.redis_config.pooled', :boolean)
78
+ validate('swarm.flush_zombies.redis_config.config', :hash)
79
+ validate('swarm.flush_zombies.redis_config.pool_config', :hash)
80
+ validate('swarm.flush_zombies.zombie_ttl', :integer)
81
+ validate('swarm.flush_zombies.zombie_lock_scan_size', :integer)
82
+ validate('swarm.flush_zombies.zombie_queue_scan_size', :integer)
83
+ validate('swarm.flush_zombies.zombie_flush_period', :integer)
84
+
34
85
  validate('retry_count') { |val| val == nil || (val.is_a?(::Integer) && val >= 0) }
35
86
  validate('retry_delay') { |val| val.is_a?(::Integer) && val >= 0 }
36
87
  validate('retry_jitter') { |val| val.is_a?(::Integer) && val >= 0 }
37
88
  validate('try_to_lock_timeout') { |val| val == nil || (val.is_a?(::Integer) && val >= 0) }
38
89
  validate('default_lock_tt', :integer)
39
90
  validate('default_queue_ttl', :integer)
91
+ validate('detailed_acq_timeout_error', :boolean)
40
92
  validate('lock_release_batch_size', :integer)
41
93
  validate('instrumenter') { |val| RedisQueuedLocks::Instrument.valid_interface?(val) }
42
94
  validate('uniq_identifier', :proc)
@@ -80,6 +132,12 @@ class RedisQueuedLocks::Client
80
132
  # @since 1.0.0
81
133
  attr_accessor :uniq_identity
82
134
 
135
+ # @return [RedisQueuedLocks::Swarm]
136
+ #
137
+ # @api private
138
+ # @since 1.9.0
139
+ attr_reader :swarm
140
+
83
141
  # @param redis_client [RedisClient]
84
142
  # Redis connection manager, which will be used for the lock acquierment and distribution.
85
143
  # It should be an instance of RedisClient.
@@ -93,8 +151,118 @@ class RedisQueuedLocks::Client
93
151
  configure(&configs)
94
152
  @uniq_identity = config[:uniq_identifier].call
95
153
  @redis_client = redis_client
154
+ @swarm = RedisQueuedLocks::Swarm.new(self).tap { |s| s.swarm! if config[:swarm][:auto_swarm] }
155
+ end
156
+
157
+ # @return [Hash<Symbol,Boolean|Symbol>]
158
+ #
159
+ # @api public
160
+ # @since 1.9.0
161
+ def swarmize!
162
+ swarm.swarm!
163
+ end
164
+
165
+ # @return [Hash<Symbol,Boolean|Symbol>]
166
+ #
167
+ # @api public
168
+ # @since 1.9.0
169
+ def deswarmize!
170
+ swarm.deswarm!
171
+ end
172
+
173
+ # @option zombie_ttl [Integer]
174
+ # @return [Hash<String,Hash<Symbol,Float|Time>>]
175
+ #
176
+ # @api public
177
+ # @since 1.9.0
178
+ def swarm_info(zombie_ttl: config[:swarm][:flush_zombies][:zombie_ttl])
179
+ swarm.swarm_info(zombie_ttl:)
96
180
  end
97
181
 
182
+ # @return [Hash<Symbol,Boolean|<Hash<Symbol,Boolean>>]
183
+ #
184
+ # @api public
185
+ # @since 1.9.0
186
+ def swarm_status
187
+ swarm.swarm_status
188
+ end
189
+ alias_method :swarm_state, :swarm_status
190
+
191
+ # @return [Hash<Symbol,Boolean|String|Float>]
192
+ #
193
+ # @api public
194
+ # @since 1.9.0
195
+ def probe_hosts
196
+ swarm.probe_hosts
197
+ end
198
+
199
+ # @option zombie_ttl [Integer]
200
+ # @option lock_scan_size [Integer]
201
+ # @option queue_scan_size [Integer]
202
+ # @return [Hash<Symbol,Boolean|Set<String>>]
203
+ #
204
+ # @api public
205
+ # @since 1.9.0
206
+ def flush_zombies(
207
+ zombie_ttl: config[:swarm][:flush_zombies][:zombie_ttl],
208
+ lock_scan_size: config[:swarm][:flush_zombies][:zombie_lock_scan_size],
209
+ queue_scan_size: config[:swarm][:flush_zombies][:zombie_queue_scan_size]
210
+ )
211
+ swarm.flush_zombies(zombie_ttl:, lock_scan_size:, queue_scan_size:)
212
+ end
213
+
214
+ # @option zombie_ttl [Integer]
215
+ # @option lock_scan_size [Integer]
216
+ # @return [Set<String>]
217
+ #
218
+ # @api public
219
+ # @since 1.9.0
220
+ def zombie_locks(
221
+ zombie_ttl: config[:swarm][:flush_zombies][:zombie_ttl],
222
+ lock_scan_size: config[:swarm][:flush_zombies][:zombie_lock_scan_size]
223
+ )
224
+ swarm.zombie_locks(zombie_ttl:, lock_scan_size:)
225
+ end
226
+
227
+ # @option zombie_ttl [Integer]
228
+ # @option lock_scan_size [Integer]
229
+ # @return [Set<String>]
230
+ #
231
+ # @api ppublic
232
+ # @since 1.9.0
233
+ def zombie_acquiers(
234
+ zombie_ttl: config[:swarm][:flush_zombies][:zombie_ttl],
235
+ lock_scan_size: config[:swarm][:flush_zombies][:zombie_lock_scan_size]
236
+ )
237
+ swarm.zombie_acquiers(zombie_ttl:, lock_scan_size:)
238
+ end
239
+
240
+ # @option zombie_ttl [Integer]
241
+ # @return [Set<String>]
242
+ #
243
+ # @api public
244
+ # @since 1.9.0
245
+ def zombie_hosts(zombie_ttl: config[:swarm][:flush_zombies][:zombie_ttl])
246
+ swarm.zombie_hosts(zombie_ttl:)
247
+ end
248
+
249
+ # @return [Hash<Symbol,Set<String>>]
250
+ # Format: {
251
+ # zombie_hosts: <Set<String>>,
252
+ # zombie_acquiers: <Set<String>>,
253
+ # zombie_locks: <Set<String>>
254
+ # }
255
+ #
256
+ # @api public
257
+ # @since 1.9.0
258
+ def zombies_info(
259
+ zombie_ttl: config[:swarm][:flush_zombies][:zombie_ttl],
260
+ lock_scan_size: config[:swarm][:flush_zombies][:zombie_ttl]
261
+ )
262
+ swarm.zombies_info(zombie_ttl:, lock_scan_size:)
263
+ end
264
+ alias_method :zombies, :zombies_info
265
+
98
266
  # @param lock_name [String]
99
267
  # Lock name to be obtained.
100
268
  # @option ttl [Integer]
@@ -146,6 +314,27 @@ class RedisQueuedLocks::Client
146
314
  # @option meta [NilClass,Hash<String|Symbol,Any>]
147
315
  # - A custom metadata wich will be passed to the lock data in addition to the existing data;
148
316
  # - Metadata can not contain reserved lock data keys;
317
+ # @option detailed_acq_timeout_error [Boolean]
318
+ # - When the lock acquirement try reached the acquirement time limit (:timeout option) the
319
+ # `RedisQueuedLocks::LockAcquirementTimeoutError` is raised (when `raise_errors` option
320
+ # set to `true`). The error message contains the lock key name and the timeout value).
321
+ # - <true> option adds the additional details to the error message:
322
+ # - current lock queue state (you can see which acquirer blocks your request and
323
+ # how much acquirers are in queue);
324
+ # - current lock data stored inside (for example: you can check the current acquirer and
325
+ # the lock meta state if you store some additional data there);
326
+ # - Realized as an option because of the additional lock data requires two additional Redis
327
+ # queries: (1) get the current lock from redis and (2) fetch the lock queue state;
328
+ # - These two additional Redis queries has async nature so you can receive
329
+ # inconsistent data of the lock and of the lock queue in your error emssage because:
330
+ # - required lock can be released after the error moment and before the error message build;
331
+ # - required lock can be obtained by other process after the error moment and
332
+ # before the error message build;
333
+ # - required lock queue can reach a state when the blocking acquirer start to obtain the lock
334
+ # and moved from the lock queue after the error moment and before the error message build;
335
+ # - You should consider the async nature of this error message and should use received data
336
+ # from error message correspondingly;
337
+ # - pre-configred in `config[:detailed_acq_timeout_error]`;
149
338
  # @option logger [::Logger,#debug]
150
339
  # - Logger object used from the configuration layer (see config[:logger]);
151
340
  # - See `RedisQueuedLocks::Logging::VoidLogger` for example;
@@ -180,6 +369,9 @@ class RedisQueuedLocks::Client
180
369
  # - you can provide your own log sampler with bettter algorithm that should realize
181
370
  # `sampling_happened?(percent) => boolean` interface
182
371
  # (see `RedisQueuedLocks::Logging::Sampler` for example);
372
+ # @option log_sample_this [Boolean]
373
+ # - marks the method that everything should be logged despite the enabled log sampling;
374
+ # - makes sense when log sampling is enabled;
183
375
  # @option instr_sampling_enabled [Boolean]
184
376
  # - enables <instrumentaion sampling>: only the configured percent
185
377
  # of RQL cases will be instrumented;
@@ -198,6 +390,10 @@ class RedisQueuedLocks::Client
198
390
  # - you can provide your own log sampler with bettter algorithm that should realize
199
391
  # `sampling_happened?(percent) => boolean` interface
200
392
  # (see `RedisQueuedLocks::Instrument::Sampler` for example);
393
+ # @option instr_sample_this [Boolean]
394
+ # - marks the method that everything should be instrumneted
395
+ # despite the enabled instrumentation sampling;
396
+ # - makes sense when instrumentation sampling is enabled;
201
397
  # @param block [Block]
202
398
  # A block of code that should be executed after the successfully acquired lock.
203
399
  # @return [RedisQueuedLocks::Data,Hash<Symbol,Any>,yield]
@@ -206,7 +402,7 @@ class RedisQueuedLocks::Client
206
402
  #
207
403
  # @api public
208
404
  # @since 1.0.0
209
- # @version 1.7.0
405
+ # @version 1.8.0
210
406
  # rubocop:disable Metrics/MethodLength
211
407
  def lock(
212
408
  lock_name,
@@ -223,6 +419,7 @@ class RedisQueuedLocks::Client
223
419
  access_strategy: config[:default_access_strategy],
224
420
  identity: uniq_identity,
225
421
  meta: nil,
422
+ detailed_acq_timeout_error: config[:detailed_acq_timeout_error],
226
423
  logger: config[:logger],
227
424
  log_lock_try: config[:log_lock_try],
228
425
  instrumenter: config[:instrumenter],
@@ -230,9 +427,11 @@ class RedisQueuedLocks::Client
230
427
  log_sampling_enabled: config[:log_sampling_enabled],
231
428
  log_sampling_percent: config[:log_sampling_percent],
232
429
  log_sampler: config[:log_sampler],
430
+ log_sample_this: false,
233
431
  instr_sampling_enabled: config[:instr_sampling_enabled],
234
432
  instr_sampling_percent: config[:instr_sampling_percent],
235
433
  instr_sampler: config[:instr_sampler],
434
+ instr_sample_this: false,
236
435
  &block
237
436
  )
238
437
  RedisQueuedLocks::Acquier::AcquireLock.acquire_lock(
@@ -256,15 +455,18 @@ class RedisQueuedLocks::Client
256
455
  conflict_strategy:,
257
456
  access_strategy:,
258
457
  meta:,
458
+ detailed_acq_timeout_error:,
259
459
  logger:,
260
460
  log_lock_try:,
261
461
  instrument:,
262
462
  log_sampling_enabled:,
263
463
  log_sampling_percent:,
264
464
  log_sampler:,
465
+ log_sample_this:,
265
466
  instr_sampling_enabled:,
266
467
  instr_sampling_percent:,
267
468
  instr_sampler:,
469
+ instr_sample_this:,
268
470
  &block
269
471
  )
270
472
  end
@@ -274,7 +476,7 @@ class RedisQueuedLocks::Client
274
476
  #
275
477
  # @api public
276
478
  # @since 1.0.0
277
- # @version 1.7.0
479
+ # @version 1.8.0
278
480
  # rubocop:disable Metrics/MethodLength
279
481
  def lock!(
280
482
  lock_name,
@@ -291,15 +493,18 @@ class RedisQueuedLocks::Client
291
493
  identity: uniq_identity,
292
494
  instrumenter: config[:instrumenter],
293
495
  meta: nil,
496
+ detailed_acq_timeout_error: config[:detailed_acq_timeout_error],
294
497
  logger: config[:logger],
295
498
  log_lock_try: config[:log_lock_try],
296
499
  instrument: nil,
297
500
  log_sampling_enabled: config[:log_sampling_enabled],
298
501
  log_sampling_percent: config[:log_sampling_percent],
299
502
  log_sampler: config[:log_sampler],
503
+ log_sample_this: false,
300
504
  instr_sampling_enabled: config[:instr_sampling_enabled],
301
505
  instr_sampling_percent: config[:instr_sampling_percent],
302
506
  instr_sampler: config[:instr_sampler],
507
+ instr_sample_this: false,
303
508
  &block
304
509
  )
305
510
  lock(
@@ -317,6 +522,7 @@ class RedisQueuedLocks::Client
317
522
  logger:,
318
523
  log_lock_try:,
319
524
  meta:,
525
+ detailed_acq_timeout_error:,
320
526
  instrument:,
321
527
  instrumenter:,
322
528
  conflict_strategy:,
@@ -324,9 +530,11 @@ class RedisQueuedLocks::Client
324
530
  log_sampling_enabled:,
325
531
  log_sampling_percent:,
326
532
  log_sampler:,
533
+ log_sample_this:,
327
534
  instr_sampling_enabled:,
328
535
  instr_sampling_percent:,
329
536
  instr_sampler:,
537
+ instr_sample_this:,
330
538
  &block
331
539
  )
332
540
  end
@@ -339,9 +547,11 @@ class RedisQueuedLocks::Client
339
547
  # @option log_sampling_enabled [Boolean]
340
548
  # @option log_sampling_percent [Integer]
341
549
  # @option log_sampler [#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]
550
+ # @option log_sample_this [Boolean]
342
551
  # @option instr_sampling_enabled [Boolean]
343
552
  # @option instr_sampling_percent [Integer]
344
553
  # @option instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
554
+ # @option instr_sample_this [Boolean]
345
555
  # @return [RedisQueuedLocks::Data, Hash<Symbol,Any>]
346
556
  # Format: {
347
557
  # ok: true/false,
@@ -365,9 +575,11 @@ class RedisQueuedLocks::Client
365
575
  log_sampling_enabled: config[:log_sampling_enabled],
366
576
  log_sampling_percent: config[:log_sampling_percent],
367
577
  log_sampler: config[:log_sampler],
578
+ log_sample_this: false,
368
579
  instr_sampling_enabled: config[:instr_sampling_enabled],
369
580
  instr_sampling_percent: config[:instr_sampling_percent],
370
- instr_sampler: config[:instr_sampler]
581
+ instr_sampler: config[:instr_sampler],
582
+ instr_sample_this: false
371
583
  )
372
584
  RedisQueuedLocks::Acquier::ReleaseLock.release_lock(
373
585
  redis_client,
@@ -377,11 +589,14 @@ class RedisQueuedLocks::Client
377
589
  log_sampling_enabled,
378
590
  log_sampling_percent,
379
591
  log_sampler,
592
+ log_sample_this,
380
593
  instr_sampling_enabled,
381
594
  instr_sampling_percent,
382
- instr_sampler
595
+ instr_sampler,
596
+ instr_sample_this
383
597
  )
384
598
  end
599
+ alias_method :release_lock, :unlock
385
600
 
386
601
  # @param lock_name [String]
387
602
  # @return [Boolean]
@@ -419,6 +634,82 @@ class RedisQueuedLocks::Client
419
634
  RedisQueuedLocks::Acquier::QueueInfo.queue_info(redis_client, lock_name)
420
635
  end
421
636
 
637
+ # Retrun the current acquirer identifier.
638
+ #
639
+ # @option process_id [Integer,Any] Process identifier.
640
+ # @option thread_id [Integer,Any] Thread identifier.
641
+ # @option fiber_id [Integer,Any] Fiber identifier.
642
+ # @option ractor_id [Integer,Any] Ractor identifier.
643
+ # @option identity [String] Unique per-process string. See `config[:uniq_identifier]`.
644
+ # @return [String]
645
+ #
646
+ # @see RedisQueuedLocks::Resource.acquier_identifier
647
+ # @see RedisQueuedLocks::Resource.get_process_id
648
+ # @see RedisQueuedLocks::Resource.get_thread_id
649
+ # @see RedisQueuedLocks::Resource.get_fiber_id
650
+ # @see RedisQueuedLocks::Resource.get_ractor_id
651
+ # @see RedisQueuedLocks::Client#uniq_identity
652
+ #
653
+ # @api public
654
+ # @since 1.8.0
655
+ def current_acquier_id(
656
+ process_id: RedisQueuedLocks::Resource.get_process_id,
657
+ thread_id: RedisQueuedLocks::Resource.get_thread_id,
658
+ fiber_id: RedisQueuedLocks::Resource.get_fiber_id,
659
+ ractor_id: RedisQueuedLocks::Resource.get_ractor_id,
660
+ identity: uniq_identity
661
+ )
662
+ RedisQueuedLocks::Resource.acquier_identifier(
663
+ process_id,
664
+ thread_id,
665
+ fiber_id,
666
+ ractor_id,
667
+ identity
668
+ )
669
+ end
670
+
671
+ # Retrun the current host identifier.
672
+ #
673
+ # @option process_id [Integer,Any] Process identifier.
674
+ # @option thread_id [Integer,Any] Thread identifier.
675
+ # @option fiber_id [Integer,Any] Fiber identifier.
676
+ # @option ractor_id [Integer,Any] Ractor identifier.
677
+ # @option identity [String] Unique per-process string. See `config[:uniq_identifier]`.
678
+ # @return [String]
679
+ #
680
+ # @see RedisQueuedLocks::Resource.host_identifier
681
+ # @see RedisQueuedLocks::Resource.get_process_id
682
+ # @see RedisQueuedLocks::Resource.get_thread_id
683
+ # @see RedisQueuedLocks::Resource.get_ractor_id
684
+ # @see RedisQueuedLocks::Client#uniq_identity
685
+ #
686
+ # @api public
687
+ # @since 1.9.0
688
+ def current_host_id(
689
+ process_id: RedisQueuedLocks::Resource.get_process_id,
690
+ thread_id: RedisQueuedLocks::Resource.get_thread_id,
691
+ ractor_id: RedisQueuedLocks::Resource.get_ractor_id,
692
+ identity: uniq_identity
693
+ )
694
+ RedisQueuedLocks::Resource.host_identifier(
695
+ process_id,
696
+ thread_id,
697
+ ractor_id,
698
+ identity
699
+ )
700
+ end
701
+
702
+ # Return the list of possible host identifiers that can be reached from the current ractor.
703
+ #
704
+ # @param identity [String] Unique identity (RedisQueuedLocks::Client#uniq_identity by default)
705
+ # @return [Array<String>]
706
+ #
707
+ # @api public
708
+ # @since 1.9.0
709
+ def possible_host_ids(identity = uniq_identity)
710
+ RedisQueuedLocks::Resource.possible_host_identifiers(identity)
711
+ end
712
+
422
713
  # This method is non-atomic cuz redis does not provide an atomic function for TTL/PTTL extension.
423
714
  # So the methid is spliited into the two commands:
424
715
  # (1) read current pttl
@@ -438,9 +729,11 @@ class RedisQueuedLocks::Client
438
729
  # @option log_sampling_enabled [Boolean]
439
730
  # @option log_sampling_percent [Integer]
440
731
  # @option log_sampler [#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]
732
+ # @option log_sample_this [Boolean]
441
733
  # @option instr_sampling_enabled [Boolean]
442
734
  # @option instr_sampling_percent [Integer]
443
735
  # @option instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
736
+ # @option instr_sample_this [Boolean]
444
737
  # @return [Hash<Symbol,Boolean|Symbol>]
445
738
  # - { ok: true, result: :ttl_extended }
446
739
  # - { ok: false, result: :async_expire_or_no_lock }
@@ -457,9 +750,11 @@ class RedisQueuedLocks::Client
457
750
  log_sampling_enabled: config[:log_sampling_enabled],
458
751
  log_sampling_percent: config[:log_sampling_percent],
459
752
  log_sampler: config[:log_sampler],
753
+ log_sample_this: false,
460
754
  instr_sampling_enabled: config[:instr_sampling_enabled],
461
755
  instr_sampling_percent: config[:instr_sampling_percent],
462
- instr_sampler: config[:instr_sampler]
756
+ instr_sampler: config[:instr_sampler],
757
+ instr_sample_this: false
463
758
  )
464
759
  RedisQueuedLocks::Acquier::ExtendLockTTL.extend_lock_ttl(
465
760
  redis_client,
@@ -471,9 +766,11 @@ class RedisQueuedLocks::Client
471
766
  log_sampling_enabled,
472
767
  log_sampling_percent,
473
768
  log_sampler,
769
+ log_sample_this,
474
770
  instr_sampling_enabled,
475
771
  instr_sampling_percent,
476
- instr_sampler
772
+ instr_sampler,
773
+ instr_sample_this
477
774
  )
478
775
  end
479
776
 
@@ -489,9 +786,11 @@ class RedisQueuedLocks::Client
489
786
  # @option log_sampling_enabled [Boolean]
490
787
  # @option log_sampling_percent [Integer]
491
788
  # @option log_sampler [#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]
789
+ # @option log_sample_this [Boolean]
492
790
  # @option instr_sampling_enabled [Boolean]
493
791
  # @option instr_sampling_percent [Integer]
494
792
  # @option instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
793
+ # @option instr_sample_this [Boolean]
495
794
  # @return [RedisQueuedLocks::Data,Hash<Symbol,Boolean|Hash<Symbol,Numeric>>]
496
795
  # Example: { ok: true, result { rel_key_cnt: 100, rel_time: 0.01 } }
497
796
  #
@@ -506,9 +805,11 @@ class RedisQueuedLocks::Client
506
805
  log_sampling_enabled: config[:log_sampling_enabled],
507
806
  log_sampling_percent: config[:log_sampling_percent],
508
807
  log_sampler: config[:log_sampler],
808
+ log_sample_this: false,
509
809
  instr_sampling_enabled: config[:instr_sampling_enabled],
510
810
  instr_sampling_percent: config[:instr_sampling_percent],
511
- instr_sampler: config[:instr_sampler]
811
+ instr_sampler: config[:instr_sampler],
812
+ instr_sample_this: false
512
813
  )
513
814
  RedisQueuedLocks::Acquier::ReleaseAllLocks.release_all_locks(
514
815
  redis_client,
@@ -519,11 +820,14 @@ class RedisQueuedLocks::Client
519
820
  log_sampling_enabled,
520
821
  log_sampling_percent,
521
822
  log_sampler,
823
+ log_sample_this,
522
824
  instr_sampling_enabled,
523
825
  instr_sampling_percent,
524
- instr_sampler
826
+ instr_sampler,
827
+ instr_sample_this
525
828
  )
526
829
  end
830
+ alias_method :release_locks, :clear_locks
527
831
 
528
832
  # @option scan_size [Integer]
529
833
  # The batch of scanned keys for Redis'es SCAN command.
@@ -612,9 +916,11 @@ class RedisQueuedLocks::Client
612
916
  # @option log_sampling_enabled [Boolean]
613
917
  # @option log_sampling_percent [Integer]
614
918
  # @option log_sampler [#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]
919
+ # @option log_sample_this [Boolean]
615
920
  # @option instr_sampling_enabled [Boolean]
616
921
  # @option instr_sampling_percent [Integer]
617
922
  # @option instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
923
+ # @option instr_sample_this [Boolean]
618
924
  # @return [Hash<Symbol,Boolean|Hash<Symbol,Set<String>>>]
619
925
  # Format: { ok: true, result: { processed_queus: Set<String> } }
620
926
  #
@@ -630,9 +936,11 @@ class RedisQueuedLocks::Client
630
936
  log_sampling_enabled: config[:log_sampling_enabled],
631
937
  log_sampling_percent: config[:log_sampling_percent],
632
938
  log_sampler: config[:log_sampler],
939
+ log_sample_this: false,
633
940
  instr_sampling_enabled: config[:instr_sampling_enabled],
634
941
  instr_sampling_percent: config[:instr_sampling_percent],
635
- instr_sampler: config[:instr_sampler]
942
+ instr_sampler: config[:instr_sampler],
943
+ instr_sample_this: false
636
944
  )
637
945
  RedisQueuedLocks::Acquier::ClearDeadRequests.clear_dead_requests(
638
946
  redis_client,
@@ -644,9 +952,11 @@ class RedisQueuedLocks::Client
644
952
  log_sampling_enabled,
645
953
  log_sampling_percent,
646
954
  log_sampler,
955
+ log_sample_this,
647
956
  instr_sampling_enabled,
648
957
  instr_sampling_percent,
649
- instr_sampler
958
+ instr_sampler,
959
+ instr_sample_this
650
960
  )
651
961
  end
652
962
  end
@@ -28,4 +28,12 @@ module RedisQueuedLocks
28
28
  # @api public
29
29
  # @since 1.3.0
30
30
  ConflictLockObtainError = Class.new(Error)
31
+
32
+ # @api public
33
+ # @since 1.9.0
34
+ SwarmError = Class.new(Error)
35
+
36
+ # @api public
37
+ # @since 1.9.0
38
+ SwarmArgumentError = Class.new(ArgumentError)
31
39
  end
@@ -10,14 +10,21 @@ module RedisQueuedLocks::Instrument
10
10
 
11
11
  class << self
12
12
  # @param instr_sampling_enabled [Boolean]
13
+ # @param instr_sample_this [Boolean]
13
14
  # @param instr_sampling_percent [Integer]
14
15
  # @param instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
15
16
  # @return [Boolean]
16
17
  #
17
18
  # @api private
18
19
  # @since 1.6.0
19
- def should_instrument?(instr_sampling_enabled, instr_sampling_percent, instr_sampler)
20
+ def should_instrument?(
21
+ instr_sampling_enabled,
22
+ instr_sample_this,
23
+ instr_sampling_percent,
24
+ instr_sampler
25
+ )
20
26
  return true unless instr_sampling_enabled
27
+ return true if instr_sample_this
21
28
  instr_sampler.sampling_happened?(instr_sampling_percent)
22
29
  end
23
30
 
@@ -9,14 +9,21 @@ module RedisQueuedLocks::Logging
9
9
 
10
10
  class << self
11
11
  # @param log_sampling_enabled [Boolean]
12
+ # @param log_sample_this [Boolean]
12
13
  # @param log_sampling_percent [Integer]
13
14
  # @param log_sampler [#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]
14
15
  # @return [Boolean]
15
16
  #
16
17
  # @api private
17
18
  # @since 1.5.0
18
- def should_log?(log_sampling_enabled, log_sampling_percent, log_sampler)
19
+ def should_log?(
20
+ log_sampling_enabled,
21
+ log_sample_this,
22
+ log_sampling_percent,
23
+ log_sampler
24
+ )
19
25
  return true unless log_sampling_enabled
26
+ return true if log_sample_this
20
27
  log_sampler.sampling_happened?(log_sampling_percent)
21
28
  end
22
29