redis_queued_locks 1.8.0 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/CHANGELOG.md +51 -0
  4. data/README.md +402 -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/yield_expire/log_visitor.rb +8 -0
  12. data/lib/redis_queued_locks/acquier/acquire_lock/yield_expire.rb +13 -9
  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 +241 -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