redis_queued_locks 1.8.0 → 1.10.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/CHANGELOG.md +66 -0
  4. data/README.md +528 -51
  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/yield_expire/log_visitor.rb +8 -0
  12. data/lib/redis_queued_locks/acquier/acquire_lock/yield_expire.rb +24 -12
  13. data/lib/redis_queued_locks/acquier/acquire_lock.rb +44 -20
  14. data/lib/redis_queued_locks/acquier/clear_dead_requests.rb +5 -1
  15. data/lib/redis_queued_locks/acquier/extend_lock_ttl.rb +5 -1
  16. data/lib/redis_queued_locks/acquier/lock_info.rb +4 -3
  17. data/lib/redis_queued_locks/acquier/locks.rb +2 -2
  18. data/lib/redis_queued_locks/acquier/queue_info.rb +2 -2
  19. data/lib/redis_queued_locks/acquier/release_all_locks.rb +12 -2
  20. data/lib/redis_queued_locks/acquier/release_lock.rb +12 -2
  21. data/lib/redis_queued_locks/client.rb +284 -34
  22. data/lib/redis_queued_locks/errors.rb +8 -0
  23. data/lib/redis_queued_locks/instrument.rb +8 -1
  24. data/lib/redis_queued_locks/logging.rb +8 -1
  25. data/lib/redis_queued_locks/resource.rb +59 -1
  26. data/lib/redis_queued_locks/swarm/acquirers.rb +44 -0
  27. data/lib/redis_queued_locks/swarm/flush_zombies.rb +133 -0
  28. data/lib/redis_queued_locks/swarm/probe_hosts.rb +69 -0
  29. data/lib/redis_queued_locks/swarm/redis_client_builder.rb +67 -0
  30. data/lib/redis_queued_locks/swarm/supervisor.rb +83 -0
  31. data/lib/redis_queued_locks/swarm/swarm_element/isolated.rb +287 -0
  32. data/lib/redis_queued_locks/swarm/swarm_element/threaded.rb +351 -0
  33. data/lib/redis_queued_locks/swarm/swarm_element.rb +8 -0
  34. data/lib/redis_queued_locks/swarm/zombie_info.rb +145 -0
  35. data/lib/redis_queued_locks/swarm.rb +246 -0
  36. data/lib/redis_queued_locks/utilities/lock.rb +22 -0
  37. data/lib/redis_queued_locks/utilities.rb +75 -0
  38. data/lib/redis_queued_locks/version.rb +2 -2
  39. data/lib/redis_queued_locks.rb +2 -0
  40. metadata +14 -4
  41. data/lib/redis_queued_locks/watcher.rb +0 -1
@@ -32,6 +32,56 @@ class RedisQueuedLocks::Client
32
32
  setting :instr_sampling_percent, 15
33
33
  setting :instr_sampler, RedisQueuedLocks::Instrument::Sampler
34
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
+
35
85
  validate('retry_count') { |val| val == nil || (val.is_a?(::Integer) && val >= 0) }
36
86
  validate('retry_delay') { |val| val.is_a?(::Integer) && val >= 0 }
37
87
  validate('retry_jitter') { |val| val.is_a?(::Integer) && val >= 0 }
@@ -82,6 +132,12 @@ class RedisQueuedLocks::Client
82
132
  # @since 1.0.0
83
133
  attr_accessor :uniq_identity
84
134
 
135
+ # @return [RedisQueuedLocks::Swarm]
136
+ #
137
+ # @api private
138
+ # @since 1.9.0
139
+ attr_reader :swarm
140
+
85
141
  # @param redis_client [RedisClient]
86
142
  # Redis connection manager, which will be used for the lock acquierment and distribution.
87
143
  # It should be an instance of RedisClient.
@@ -95,41 +151,118 @@ class RedisQueuedLocks::Client
95
151
  configure(&configs)
96
152
  @uniq_identity = config[:uniq_identifier].call
97
153
  @redis_client = redis_client
154
+ @swarm = RedisQueuedLocks::Swarm.new(self).tap { |s| s.swarm! if config[:swarm][:auto_swarm] }
98
155
  end
99
156
 
100
- # Retrun the current acquirer identifier.
157
+ # @return [Hash<Symbol,Boolean|Symbol>]
101
158
  #
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]
159
+ # @api public
160
+ # @since 1.9.0
161
+ def swarmize!
162
+ swarm.swarm!
163
+ end
164
+
165
+ # @return [Hash<Symbol,Boolean|Symbol>]
108
166
  #
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
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>>]
114
175
  #
115
176
  # @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
177
+ # @since 1.9.0
178
+ def swarm_info(zombie_ttl: config[:swarm][:flush_zombies][:zombie_ttl])
179
+ swarm.swarm_info(zombie_ttl:)
180
+ end
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]
123
210
  )
124
- RedisQueuedLocks::Resource.acquier_identifier(
125
- process_id,
126
- thread_id,
127
- fiber_id,
128
- ractor_id,
129
- identity
130
- )
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:)
131
238
  end
132
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
+
133
266
  # @param lock_name [String]
134
267
  # Lock name to be obtained.
135
268
  # @option ttl [Integer]
@@ -236,6 +369,9 @@ class RedisQueuedLocks::Client
236
369
  # - you can provide your own log sampler with bettter algorithm that should realize
237
370
  # `sampling_happened?(percent) => boolean` interface
238
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;
239
375
  # @option instr_sampling_enabled [Boolean]
240
376
  # - enables <instrumentaion sampling>: only the configured percent
241
377
  # of RQL cases will be instrumented;
@@ -254,6 +390,10 @@ class RedisQueuedLocks::Client
254
390
  # - you can provide your own log sampler with bettter algorithm that should realize
255
391
  # `sampling_happened?(percent) => boolean` interface
256
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;
257
397
  # @param block [Block]
258
398
  # A block of code that should be executed after the successfully acquired lock.
259
399
  # @return [RedisQueuedLocks::Data,Hash<Symbol,Any>,yield]
@@ -287,9 +427,11 @@ class RedisQueuedLocks::Client
287
427
  log_sampling_enabled: config[:log_sampling_enabled],
288
428
  log_sampling_percent: config[:log_sampling_percent],
289
429
  log_sampler: config[:log_sampler],
430
+ log_sample_this: false,
290
431
  instr_sampling_enabled: config[:instr_sampling_enabled],
291
432
  instr_sampling_percent: config[:instr_sampling_percent],
292
433
  instr_sampler: config[:instr_sampler],
434
+ instr_sample_this: false,
293
435
  &block
294
436
  )
295
437
  RedisQueuedLocks::Acquier::AcquireLock.acquire_lock(
@@ -320,9 +462,11 @@ class RedisQueuedLocks::Client
320
462
  log_sampling_enabled:,
321
463
  log_sampling_percent:,
322
464
  log_sampler:,
465
+ log_sample_this:,
323
466
  instr_sampling_enabled:,
324
467
  instr_sampling_percent:,
325
468
  instr_sampler:,
469
+ instr_sample_this:,
326
470
  &block
327
471
  )
328
472
  end
@@ -356,9 +500,11 @@ class RedisQueuedLocks::Client
356
500
  log_sampling_enabled: config[:log_sampling_enabled],
357
501
  log_sampling_percent: config[:log_sampling_percent],
358
502
  log_sampler: config[:log_sampler],
503
+ log_sample_this: false,
359
504
  instr_sampling_enabled: config[:instr_sampling_enabled],
360
505
  instr_sampling_percent: config[:instr_sampling_percent],
361
506
  instr_sampler: config[:instr_sampler],
507
+ instr_sample_this: false,
362
508
  &block
363
509
  )
364
510
  lock(
@@ -384,9 +530,11 @@ class RedisQueuedLocks::Client
384
530
  log_sampling_enabled:,
385
531
  log_sampling_percent:,
386
532
  log_sampler:,
533
+ log_sample_this:,
387
534
  instr_sampling_enabled:,
388
535
  instr_sampling_percent:,
389
536
  instr_sampler:,
537
+ instr_sample_this:,
390
538
  &block
391
539
  )
392
540
  end
@@ -399,9 +547,11 @@ class RedisQueuedLocks::Client
399
547
  # @option log_sampling_enabled [Boolean]
400
548
  # @option log_sampling_percent [Integer]
401
549
  # @option log_sampler [#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]
550
+ # @option log_sample_this [Boolean]
402
551
  # @option instr_sampling_enabled [Boolean]
403
552
  # @option instr_sampling_percent [Integer]
404
553
  # @option instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
554
+ # @option instr_sample_this [Boolean]
405
555
  # @return [RedisQueuedLocks::Data, Hash<Symbol,Any>]
406
556
  # Format: {
407
557
  # ok: true/false,
@@ -425,9 +575,11 @@ class RedisQueuedLocks::Client
425
575
  log_sampling_enabled: config[:log_sampling_enabled],
426
576
  log_sampling_percent: config[:log_sampling_percent],
427
577
  log_sampler: config[:log_sampler],
578
+ log_sample_this: false,
428
579
  instr_sampling_enabled: config[:instr_sampling_enabled],
429
580
  instr_sampling_percent: config[:instr_sampling_percent],
430
- instr_sampler: config[:instr_sampler]
581
+ instr_sampler: config[:instr_sampler],
582
+ instr_sample_this: false
431
583
  )
432
584
  RedisQueuedLocks::Acquier::ReleaseLock.release_lock(
433
585
  redis_client,
@@ -437,11 +589,14 @@ class RedisQueuedLocks::Client
437
589
  log_sampling_enabled,
438
590
  log_sampling_percent,
439
591
  log_sampler,
592
+ log_sample_this,
440
593
  instr_sampling_enabled,
441
594
  instr_sampling_percent,
442
- instr_sampler
595
+ instr_sampler,
596
+ instr_sample_this
443
597
  )
444
598
  end
599
+ alias_method :release_lock, :unlock
445
600
 
446
601
  # @param lock_name [String]
447
602
  # @return [Boolean]
@@ -479,6 +634,82 @@ class RedisQueuedLocks::Client
479
634
  RedisQueuedLocks::Acquier::QueueInfo.queue_info(redis_client, lock_name)
480
635
  end
481
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
+
482
713
  # This method is non-atomic cuz redis does not provide an atomic function for TTL/PTTL extension.
483
714
  # So the methid is spliited into the two commands:
484
715
  # (1) read current pttl
@@ -498,9 +729,11 @@ class RedisQueuedLocks::Client
498
729
  # @option log_sampling_enabled [Boolean]
499
730
  # @option log_sampling_percent [Integer]
500
731
  # @option log_sampler [#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]
732
+ # @option log_sample_this [Boolean]
501
733
  # @option instr_sampling_enabled [Boolean]
502
734
  # @option instr_sampling_percent [Integer]
503
735
  # @option instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
736
+ # @option instr_sample_this [Boolean]
504
737
  # @return [Hash<Symbol,Boolean|Symbol>]
505
738
  # - { ok: true, result: :ttl_extended }
506
739
  # - { ok: false, result: :async_expire_or_no_lock }
@@ -517,9 +750,11 @@ class RedisQueuedLocks::Client
517
750
  log_sampling_enabled: config[:log_sampling_enabled],
518
751
  log_sampling_percent: config[:log_sampling_percent],
519
752
  log_sampler: config[:log_sampler],
753
+ log_sample_this: false,
520
754
  instr_sampling_enabled: config[:instr_sampling_enabled],
521
755
  instr_sampling_percent: config[:instr_sampling_percent],
522
- instr_sampler: config[:instr_sampler]
756
+ instr_sampler: config[:instr_sampler],
757
+ instr_sample_this: false
523
758
  )
524
759
  RedisQueuedLocks::Acquier::ExtendLockTTL.extend_lock_ttl(
525
760
  redis_client,
@@ -531,9 +766,11 @@ class RedisQueuedLocks::Client
531
766
  log_sampling_enabled,
532
767
  log_sampling_percent,
533
768
  log_sampler,
769
+ log_sample_this,
534
770
  instr_sampling_enabled,
535
771
  instr_sampling_percent,
536
- instr_sampler
772
+ instr_sampler,
773
+ instr_sample_this
537
774
  )
538
775
  end
539
776
 
@@ -549,9 +786,11 @@ class RedisQueuedLocks::Client
549
786
  # @option log_sampling_enabled [Boolean]
550
787
  # @option log_sampling_percent [Integer]
551
788
  # @option log_sampler [#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]
789
+ # @option log_sample_this [Boolean]
552
790
  # @option instr_sampling_enabled [Boolean]
553
791
  # @option instr_sampling_percent [Integer]
554
792
  # @option instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
793
+ # @option instr_sample_this [Boolean]
555
794
  # @return [RedisQueuedLocks::Data,Hash<Symbol,Boolean|Hash<Symbol,Numeric>>]
556
795
  # Example: { ok: true, result { rel_key_cnt: 100, rel_time: 0.01 } }
557
796
  #
@@ -566,9 +805,11 @@ class RedisQueuedLocks::Client
566
805
  log_sampling_enabled: config[:log_sampling_enabled],
567
806
  log_sampling_percent: config[:log_sampling_percent],
568
807
  log_sampler: config[:log_sampler],
808
+ log_sample_this: false,
569
809
  instr_sampling_enabled: config[:instr_sampling_enabled],
570
810
  instr_sampling_percent: config[:instr_sampling_percent],
571
- instr_sampler: config[:instr_sampler]
811
+ instr_sampler: config[:instr_sampler],
812
+ instr_sample_this: false
572
813
  )
573
814
  RedisQueuedLocks::Acquier::ReleaseAllLocks.release_all_locks(
574
815
  redis_client,
@@ -579,11 +820,14 @@ class RedisQueuedLocks::Client
579
820
  log_sampling_enabled,
580
821
  log_sampling_percent,
581
822
  log_sampler,
823
+ log_sample_this,
582
824
  instr_sampling_enabled,
583
825
  instr_sampling_percent,
584
- instr_sampler
826
+ instr_sampler,
827
+ instr_sample_this
585
828
  )
586
829
  end
830
+ alias_method :release_locks, :clear_locks
587
831
 
588
832
  # @option scan_size [Integer]
589
833
  # The batch of scanned keys for Redis'es SCAN command.
@@ -672,9 +916,11 @@ class RedisQueuedLocks::Client
672
916
  # @option log_sampling_enabled [Boolean]
673
917
  # @option log_sampling_percent [Integer]
674
918
  # @option log_sampler [#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]
919
+ # @option log_sample_this [Boolean]
675
920
  # @option instr_sampling_enabled [Boolean]
676
921
  # @option instr_sampling_percent [Integer]
677
922
  # @option instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
923
+ # @option instr_sample_this [Boolean]
678
924
  # @return [Hash<Symbol,Boolean|Hash<Symbol,Set<String>>>]
679
925
  # Format: { ok: true, result: { processed_queus: Set<String> } }
680
926
  #
@@ -690,9 +936,11 @@ class RedisQueuedLocks::Client
690
936
  log_sampling_enabled: config[:log_sampling_enabled],
691
937
  log_sampling_percent: config[:log_sampling_percent],
692
938
  log_sampler: config[:log_sampler],
939
+ log_sample_this: false,
693
940
  instr_sampling_enabled: config[:instr_sampling_enabled],
694
941
  instr_sampling_percent: config[:instr_sampling_percent],
695
- instr_sampler: config[:instr_sampler]
942
+ instr_sampler: config[:instr_sampler],
943
+ instr_sample_this: false
696
944
  )
697
945
  RedisQueuedLocks::Acquier::ClearDeadRequests.clear_dead_requests(
698
946
  redis_client,
@@ -704,9 +952,11 @@ class RedisQueuedLocks::Client
704
952
  log_sampling_enabled,
705
953
  log_sampling_percent,
706
954
  log_sampler,
955
+ log_sample_this,
707
956
  instr_sampling_enabled,
708
957
  instr_sampling_percent,
709
- instr_sampler
958
+ instr_sampler,
959
+ instr_sample_this
710
960
  )
711
961
  end
712
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
 
@@ -21,6 +21,12 @@ module RedisQueuedLocks::Resource
21
21
  # @since 1.0.0
22
22
  LOCK_QUEUE_PATTERN = 'rql:lock_queue:*'
23
23
 
24
+ # @return [String]
25
+ #
26
+ # @api private
27
+ # @since 1.9.0
28
+ SWARM_KEY = 'rql:swarm:hsts'
29
+
24
30
  # @return [Integer] Redis time error (in milliseconds).
25
31
  #
26
32
  # @api private
@@ -53,6 +59,22 @@ module RedisQueuedLocks::Resource
53
59
  "rql:acq:#{process_id}/#{thread_id}/#{fiber_id}/#{ractor_id}/#{identity}"
54
60
  end
55
61
 
62
+ # @param process_id [Integer,String]
63
+ # @param thread_id [Integer,String]
64
+ # @param ractor_id [Integer,String]
65
+ # @param identity [String]
66
+ # @return [String]
67
+ #
68
+ # @api private
69
+ # @since 1.9.0
70
+ def host_identifier(process_id, thread_id, ractor_id, identity)
71
+ # NOTE:
72
+ # - fiber's object_id is not used cuz we can't analyze fiber objects via ObjectSpace
73
+ # after the any new Ractor object is created in the current process
74
+ # (ObjectSpace no longer sees objects of Thread and Fiber classes after that);
75
+ "rql:hst:#{process_id}/#{thread_id}/#{ractor_id}/#{identity}"
76
+ end
77
+
56
78
  # @param lock_name [String]
57
79
  # @return [String]
58
80
  #
@@ -88,8 +110,17 @@ module RedisQueuedLocks::Resource
88
110
  Time.now.to_f - queue_ttl
89
111
  end
90
112
 
113
+ # @param zombie_ttl [Float] In seconds with milliseconds.
114
+ # @return [Float]
115
+ #
116
+ # @api private
117
+ # @since 1.9.0
118
+ def calc_zombie_score(zombie_ttl)
119
+ Time.now.to_f - zombie_ttl
120
+ end
121
+
91
122
  # @param acquier_position [Float]
92
- # A time (epoch, seconds.microseconds) that represents
123
+ # A time (epoch, seconds.milliseconds) that represents
93
124
  # the acquier position in lock request queue.
94
125
  # @parma queue_ttl [Integer]
95
126
  # In second.
@@ -143,5 +174,32 @@ module RedisQueuedLocks::Resource
143
174
  def get_process_id
144
175
  ::Process.pid
145
176
  end
177
+
178
+ # @return [Array<String>]
179
+ #
180
+ # @api private
181
+ # @since 1.9.0
182
+ def possible_host_identifiers(identity)
183
+ # NOTE №1: we can not use ObjectSpace.each_object for Thread and Fiber cuz after the any
184
+ # ractor creation the ObjectSpace stops seeing ::Thread and ::Fiber objects: each_object
185
+ # for each of them returns `0`;
186
+ # NOTE №2: we have no any approach to count Fiber objects in the current process without
187
+ # object space API (or super memory-expensive) so host identification works without fibers;
188
+ # NOTE №3: we still can extract thread objects via Thread.list API;
189
+ current_process_id = get_process_id
190
+ current_threads = ::Thread.list
191
+ current_ractor_id = get_ractor_id
192
+
193
+ [].tap do |acquiers|
194
+ current_threads.each do |thread|
195
+ acquiers << host_identifier(
196
+ current_process_id,
197
+ thread.object_id,
198
+ current_ractor_id,
199
+ identity
200
+ )
201
+ end
202
+ end
203
+ end
146
204
  end
147
205
  end