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.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/CHANGELOG.md +66 -0
- data/README.md +528 -51
- data/lib/redis_queued_locks/acquier/acquire_lock/dequeue_from_lock_queue/log_visitor.rb +4 -0
- data/lib/redis_queued_locks/acquier/acquire_lock/dequeue_from_lock_queue.rb +4 -1
- data/lib/redis_queued_locks/acquier/acquire_lock/instr_visitor.rb +20 -5
- data/lib/redis_queued_locks/acquier/acquire_lock/log_visitor.rb +24 -0
- data/lib/redis_queued_locks/acquier/acquire_lock/try_to_lock/log_visitor.rb +56 -0
- data/lib/redis_queued_locks/acquier/acquire_lock/try_to_lock.rb +37 -30
- data/lib/redis_queued_locks/acquier/acquire_lock/yield_expire/log_visitor.rb +8 -0
- data/lib/redis_queued_locks/acquier/acquire_lock/yield_expire.rb +24 -12
- data/lib/redis_queued_locks/acquier/acquire_lock.rb +44 -20
- data/lib/redis_queued_locks/acquier/clear_dead_requests.rb +5 -1
- data/lib/redis_queued_locks/acquier/extend_lock_ttl.rb +5 -1
- data/lib/redis_queued_locks/acquier/lock_info.rb +4 -3
- data/lib/redis_queued_locks/acquier/locks.rb +2 -2
- data/lib/redis_queued_locks/acquier/queue_info.rb +2 -2
- data/lib/redis_queued_locks/acquier/release_all_locks.rb +12 -2
- data/lib/redis_queued_locks/acquier/release_lock.rb +12 -2
- data/lib/redis_queued_locks/client.rb +284 -34
- data/lib/redis_queued_locks/errors.rb +8 -0
- data/lib/redis_queued_locks/instrument.rb +8 -1
- data/lib/redis_queued_locks/logging.rb +8 -1
- data/lib/redis_queued_locks/resource.rb +59 -1
- data/lib/redis_queued_locks/swarm/acquirers.rb +44 -0
- data/lib/redis_queued_locks/swarm/flush_zombies.rb +133 -0
- data/lib/redis_queued_locks/swarm/probe_hosts.rb +69 -0
- data/lib/redis_queued_locks/swarm/redis_client_builder.rb +67 -0
- data/lib/redis_queued_locks/swarm/supervisor.rb +83 -0
- data/lib/redis_queued_locks/swarm/swarm_element/isolated.rb +287 -0
- data/lib/redis_queued_locks/swarm/swarm_element/threaded.rb +351 -0
- data/lib/redis_queued_locks/swarm/swarm_element.rb +8 -0
- data/lib/redis_queued_locks/swarm/zombie_info.rb +145 -0
- data/lib/redis_queued_locks/swarm.rb +246 -0
- data/lib/redis_queued_locks/utilities/lock.rb +22 -0
- data/lib/redis_queued_locks/utilities.rb +75 -0
- data/lib/redis_queued_locks/version.rb +2 -2
- data/lib/redis_queued_locks.rb +2 -0
- metadata +14 -4
- 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
|
-
#
|
157
|
+
# @return [Hash<Symbol,Boolean|Symbol>]
|
101
158
|
#
|
102
|
-
# @
|
103
|
-
# @
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
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
|
-
# @
|
110
|
-
# @
|
111
|
-
|
112
|
-
|
113
|
-
|
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.
|
117
|
-
def
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
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?(
|
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?(
|
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.
|
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
|