redis_queued_locks 1.5.0 → 1.7.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.
@@ -2,16 +2,20 @@
2
2
 
3
3
  # @api private
4
4
  # @since 1.0.0
5
+ # @version 1.7.0
5
6
  # rubocop:disable Metrics/ModuleLength
6
7
  # rubocop:disable Metrics/MethodLength
7
8
  # rubocop:disable Metrics/ClassLength
8
9
  # rubocop:disable Metrics/BlockNesting
9
10
  # rubocop:disable Style/IfInsideElse
10
11
  module RedisQueuedLocks::Acquier::AcquireLock
12
+ require_relative 'acquire_lock/log_visitor'
13
+ require_relative 'acquire_lock/instr_visitor'
11
14
  require_relative 'acquire_lock/delay_execution'
12
15
  require_relative 'acquire_lock/with_acq_timeout'
13
16
  require_relative 'acquire_lock/yield_expire'
14
17
  require_relative 'acquire_lock/try_to_lock'
18
+ require_relative 'acquire_lock/dequeue_from_lock_queue'
15
19
 
16
20
  # @since 1.0.0
17
21
  extend TryToLock
@@ -21,8 +25,8 @@ module RedisQueuedLocks::Acquier::AcquireLock
21
25
  extend YieldExpire
22
26
  # @since 1.0.0
23
27
  extend WithAcqTimeout
24
- # @since 1.0.0
25
- extend RedisQueuedLocks::Utilities
28
+ # @since 1.7.0
29
+ extend DequeueFromLockQueue
26
30
 
27
31
  class << self
28
32
  # @param redis [RedisClient]
@@ -86,20 +90,51 @@ module RedisQueuedLocks::Acquier::AcquireLock
86
90
  # - `:extendable_work_through`;
87
91
  # - `:wait_for_lock`;
88
92
  # - `:dead_locking`;
93
+ # @option access_strategy [Symbol]
94
+ # - The way in which the lock will be obtained;
95
+ # - By default it uses `:queued` strategy;
96
+ # - Supports following strategies:
97
+ # - `:queued` (FIFO): the classic queued behavior (default), your lock will be
98
+ # obitaned if you are first in queue and the required lock is free;
99
+ # - `:random` (RANDOM): obtain a lock without checking the positions in the queue
100
+ # (but with checking the limist, retries, timeouts and so on). if lock is
101
+ # free to obtain - it will be obtained;
102
+ # - pre-configured in `config[:default_access_strategy]`;
89
103
  # @option log_sampling_enabled [Boolean]
90
- # - The percent of cases that should be logged;
91
- # - Sampling algorithm is super simple and works via SecureRandom.rand method
92
- # on the base of "weight" algorithm;
93
- # - You can provide your own sampler via config[:log_sampler] config and :sampler option
94
- # (see `RedisQueuedLocks::Logging::Sampler` for examples);
95
- # - The spread of guaranteed percent is approximately +13% (rand method spread);
96
- # - Take an effect when <log_sampling_enabled> parameter has <true> value
97
- # (when log sampling is enabled);
104
+ # - enables <log sampling>: only the configured percent of RQL cases will be logged;
105
+ # - disabled by default;
106
+ # - works in tandem with <config.log_sampling_percent and <log.sampler>;
98
107
  # @option log_sampling_percent [Integer]
99
- # - The percent of cases that should be logged;
100
- # - Take an effect when <log_sampling_enabled> parameter has <true> value
101
- # (when log sampling is enabled);
108
+ # - the percent of cases that should be logged;
109
+ # - take an effect when <config.log_sampling_enalbed> is true;
110
+ # - works in tandem with <config.log_sampling_enabled> and <config.log_sampler> configs;
102
111
  # @option log_sampler [#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]
112
+ # - percent-based log sampler that decides should be RQL case logged or not;
113
+ # - works in tandem with <config.log_sampling_enabled> and
114
+ # <config.log_sampling_percent> configs;
115
+ # - based on the ultra simple percent-based (weight-based) algorithm that uses
116
+ # SecureRandom.rand method so the algorithm error is ~(0%..13%);
117
+ # - you can provide your own log sampler with bettter algorithm that should realize
118
+ # `sampling_happened?(percent) => boolean` interface
119
+ # (see `RedisQueuedLocks::Logging::Sampler` for example);
120
+ # @option instr_sampling_enabled [Boolean]
121
+ # - enables <instrumentaion sampling>: only the configured percent
122
+ # of RQL cases will be instrumented;
123
+ # - disabled by default;
124
+ # - works in tandem with <config.instr_sampling_percent and <log.instr_sampler>;
125
+ # @option instr_sampling_percent [Integer]
126
+ # - the percent of cases that should be instrumented;
127
+ # - take an effect when <config.instr_sampling_enalbed> is true;
128
+ # - works in tandem with <config.instr_sampling_enabled> and <config.instr_sampler> configs;
129
+ # @option instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
130
+ # - percent-based log sampler that decides should be RQL case instrumented or not;
131
+ # - works in tandem with <config.instr_sampling_enabled> and
132
+ # <config.instr_sampling_percent> configs;
133
+ # - based on the ultra simple percent-based (weight-based) algorithm that uses
134
+ # SecureRandom.rand method so the algorithm error is ~(0%..13%);
135
+ # - you can provide your own log sampler with bettter algorithm that should realize
136
+ # `sampling_happened?(percent) => boolean` interface
137
+ # (see `RedisQueuedLocks::Instrument::Sampler` for example);
103
138
  # @param [Block]
104
139
  # A block of code that should be executed after the successfully acquired lock.
105
140
  # @return [RedisQueuedLocks::Data,Hash<Symbol,Any>,yield]
@@ -108,7 +143,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
108
143
  #
109
144
  # @api private
110
145
  # @since 1.0.0
111
- # @version 1.4.0
146
+ # @version 1.7.0
112
147
  def acquire_lock(
113
148
  redis,
114
149
  lock_name,
@@ -132,9 +167,13 @@ module RedisQueuedLocks::Acquier::AcquireLock
132
167
  logger:,
133
168
  log_lock_try:,
134
169
  conflict_strategy:,
170
+ access_strategy:,
135
171
  log_sampling_enabled:,
136
172
  log_sampling_percent:,
137
173
  log_sampler:,
174
+ instr_sampling_enabled:,
175
+ instr_sampling_percent:,
176
+ instr_sampler:,
138
177
  &block
139
178
  )
140
179
  # Step 0: Prevent argument type incompatabilities
@@ -187,6 +226,11 @@ module RedisQueuedLocks::Acquier::AcquireLock
187
226
  log_sampling_percent,
188
227
  log_sampler
189
228
  )
229
+ instr_sampled = RedisQueuedLocks::Instrument.should_instrument?(
230
+ instr_sampling_enabled,
231
+ instr_sampling_percent,
232
+ instr_sampler
233
+ )
190
234
 
191
235
  # Step X: intermediate result observer
192
236
  acq_process = {
@@ -207,18 +251,16 @@ module RedisQueuedLocks::Acquier::AcquireLock
207
251
  lock_key_queue,
208
252
  queue_ttl,
209
253
  acquier_id,
210
- log_sampled
254
+ access_strategy,
255
+ log_sampled,
256
+ instr_sampled
211
257
  )
212
258
  end
213
259
 
214
- run_non_critical do
215
- logger.debug do
216
- "[redis_queued_locks.start_lock_obtaining] " \
217
- "lock_key => '#{lock_key}' " \
218
- "queue_ttl => #{queue_ttl} " \
219
- "acq_id => '#{acquier_id}'"
220
- end
221
- end if log_sampled
260
+ LogVisitor.start_lock_obtaining(
261
+ logger, log_sampled,
262
+ lock_key, queue_ttl, acquier_id, access_strategy
263
+ )
222
264
 
223
265
  # Step 2: try to lock with timeout
224
266
  with_acq_timeout(timeout, lock_key, raise_errors, on_timeout: acq_dequeue) do
@@ -226,28 +268,21 @@ module RedisQueuedLocks::Acquier::AcquireLock
226
268
 
227
269
  # Step 2.1: cyclically try to obtain the lock
228
270
  while acq_process[:should_try]
229
- run_non_critical do
230
- logger.debug do
231
- "[redis_queued_locks.start_try_to_lock_cycle] " \
232
- "lock_key => '#{lock_key}' " \
233
- "queue_ttl => #{queue_ttl} " \
234
- "acq_id => '{#{acquier_id}'"
235
- end
236
- end if log_sampled
271
+
272
+ LogVisitor.start_try_to_lock_cycle(
273
+ logger, log_sampled,
274
+ lock_key, queue_ttl, acquier_id, access_strategy
275
+ )
237
276
 
238
277
  # Step 2.X: check the actual score: is it in queue ttl limit or not?
239
278
  if RedisQueuedLocks::Resource.dead_score_reached?(acquier_position, queue_ttl)
240
279
  # Step 2.X.X: dead score reached => re-queue the lock request with the new score;
241
280
  acquier_position = RedisQueuedLocks::Resource.calc_initial_acquier_position
242
281
 
243
- run_non_critical do
244
- logger.debug do
245
- "[redis_queued_locks.dead_score_reached__reset_acquier_position] " \
246
- "lock_key => '#{lock_key} " \
247
- "queue_ttl => #{queue_ttl} " \
248
- "acq_id => '#{acquier_id}'"
249
- end
250
- end if log_sampled
282
+ LogVisitor.dead_score_reached__reset_acquier_position(
283
+ logger, log_sampled,
284
+ lock_key, queue_ttl, acquier_id, access_strategy
285
+ )
251
286
  end
252
287
 
253
288
  try_to_lock(
@@ -262,8 +297,10 @@ module RedisQueuedLocks::Acquier::AcquireLock
262
297
  queue_ttl,
263
298
  fail_fast,
264
299
  conflict_strategy,
300
+ access_strategy,
265
301
  meta,
266
- log_sampled
302
+ log_sampled,
303
+ instr_sampled
267
304
  ) => { ok:, result: }
268
305
 
269
306
  acq_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
@@ -278,72 +315,38 @@ module RedisQueuedLocks::Acquier::AcquireLock
278
315
  # Step X: (instrumentation)
279
316
  if acq_process[:result][:process] == :extendable_conflict_work_through
280
317
  # instrumetnation: (reentrant lock with ttl extension)
281
- run_non_critical do
282
- logger.debug do
283
- "[redis_queued_locks.extendable_reentrant_lock_obtained] " \
284
- "lock_key => '#{result[:lock_key]}' " \
285
- "queue_ttl => #{queue_ttl} " \
286
- "acq_id => '#{acquier_id}' " \
287
- "acq_time => #{acq_time} (ms)"
288
- end
289
- end if log_sampled
290
-
291
- run_non_critical do
292
- instrumenter.notify('redis_queued_locks.extendable_reentrant_lock_obtained', {
293
- lock_key: result[:lock_key],
294
- ttl: result[:ttl],
295
- acq_id: result[:acq_id],
296
- ts: result[:ts],
297
- acq_time: acq_time,
298
- instrument:
299
- })
300
- end
318
+ LogVisitor.extendable_reentrant_lock_obtained(
319
+ logger, log_sampled,
320
+ result[:lock_key], queue_ttl, acquier_id, acq_time, access_strategy
321
+ )
322
+ InstrVisitor.extendable_reentrant_lock_obtained(
323
+ instrumenter, instr_sampled,
324
+ result[:lock_key], result[:ttl], result[:acq_id], result[:ts], acq_time,
325
+ instrument
326
+ )
301
327
  elsif acq_process[:result][:process] == :conflict_work_through
302
328
  # instrumetnation: (reentrant lock without ttl extension)
303
- run_non_critical do
304
- logger.debug do
305
- "[redis_queued_locks.reentrant_lock_obtained] " \
306
- "lock_key => '#{result[:lock_key]}' " \
307
- "queue_ttl => #{queue_ttl} " \
308
- "acq_id => '#{acquier_id}' " \
309
- "acq_time => #{acq_time} (ms)"
310
- end
311
- end if log_sampled
312
-
313
- run_non_critical do
314
- instrumenter.notify('redis_queued_locks.reentrant_lock_obtained', {
315
- lock_key: result[:lock_key],
316
- ttl: result[:ttl],
317
- acq_id: result[:acq_id],
318
- ts: result[:ts],
319
- acq_time: acq_time,
320
- instrument:
321
- })
322
- end
329
+ LogVisitor.reentrant_lock_obtained(
330
+ logger, log_sampled,
331
+ result[:lock_key], queue_ttl, acquier_id, acq_time, access_strategy
332
+ )
333
+ InstrVisitor.reentrant_lock_obtained(
334
+ instrumenter, instr_sampled,
335
+ result[:lock_key], result[:ttl], result[:acq_id], result[:ts], acq_time,
336
+ instrument
337
+ )
323
338
  else
324
339
  # instrumentation: (classic lock obtain)
325
340
  # NOTE: classic is: acq_process[:result][:process] == :lock_obtaining
326
- run_non_critical do
327
- logger.debug do
328
- "[redis_queued_locks.lock_obtained] " \
329
- "lock_key => '#{result[:lock_key]}' " \
330
- "queue_ttl => #{queue_ttl} " \
331
- "acq_id => '#{acquier_id}' " \
332
- "acq_time => #{acq_time} (ms)"
333
- end
334
- end if log_sampled
335
-
336
- # Step X (instrumentation): lock obtained
337
- run_non_critical do
338
- instrumenter.notify('redis_queued_locks.lock_obtained', {
339
- lock_key: result[:lock_key],
340
- ttl: result[:ttl],
341
- acq_id: result[:acq_id],
342
- ts: result[:ts],
343
- acq_time: acq_time,
344
- instrument:
345
- })
346
- end
341
+ LogVisitor.lock_obtained(
342
+ logger, log_sampled,
343
+ result[:lock_key], queue_ttl, acquier_id, acq_time, access_strategy
344
+ )
345
+ InstrVisitor.lock_obtained(
346
+ instrumenter, instr_sampled,
347
+ result[:lock_key], result[:ttl], result[:acq_id], result[:ts], acq_time,
348
+ instrument
349
+ )
347
350
  end
348
351
 
349
352
  # Step 2.1.a: successfully acquired => build the result
@@ -447,11 +450,13 @@ module RedisQueuedLocks::Acquier::AcquireLock
447
450
  logger,
448
451
  lock_key,
449
452
  acquier_id,
453
+ access_strategy,
450
454
  timed,
451
455
  ttl_shift,
452
456
  ttl,
453
457
  queue_ttl,
454
458
  log_sampled,
459
+ instr_sampled,
455
460
  should_expire, # NOTE: should expire the lock after the block execution
456
461
  should_decrease, # NOTE: should decrease the lock ttl in reentrant locks?
457
462
  &block
@@ -467,30 +472,30 @@ module RedisQueuedLocks::Acquier::AcquireLock
467
472
  if acq_process[:result][:process] == :extendable_conflict_work_through ||
468
473
  acq_process[:result][:process] == :conflict_work_through
469
474
  # Step X (instrumentation): reentrant_lock_hold_completes
470
- run_non_critical do
471
- instrumenter.notify('redis_queued_locks.reentrant_lock_hold_completes', {
472
- hold_time: acq_process[:hold_time],
473
- ttl: acq_process[:lock_info][:ttl],
474
- acq_id: acq_process[:lock_info][:acq_id],
475
- ts: acq_process[:lock_info][:ts],
476
- lock_key: acq_process[:lock_info][:lock_key],
477
- acq_time: acq_process[:acq_time],
478
- instrument:
479
- })
480
- end
475
+ InstrVisitor.reentrant_lock_hold_completes(
476
+ instrumenter,
477
+ instr_sampled,
478
+ acq_process[:lock_info][:lock_key],
479
+ acq_process[:lock_info][:ttl],
480
+ acq_process[:lock_info][:acq_id],
481
+ acq_process[:lock_info][:ts],
482
+ acq_process[:acq_time],
483
+ acq_process[:hold_time],
484
+ instrument
485
+ )
481
486
  else
482
487
  # Step X (instrumentation): lock_hold_and_release
483
- run_non_critical do
484
- instrumenter.notify('redis_queued_locks.lock_hold_and_release', {
485
- hold_time: acq_process[:hold_time],
486
- ttl: acq_process[:lock_info][:ttl],
487
- acq_id: acq_process[:lock_info][:acq_id],
488
- ts: acq_process[:lock_info][:ts],
489
- lock_key: acq_process[:lock_info][:lock_key],
490
- acq_time: acq_process[:acq_time],
491
- instrument:
492
- })
493
- end
488
+ InstrVisitor.lock_hold_and_release(
489
+ instrumenter,
490
+ instr_sampled,
491
+ acq_process[:lock_info][:lock_key],
492
+ acq_process[:lock_info][:ttl],
493
+ acq_process[:lock_info][:acq_id],
494
+ acq_process[:lock_info][:ts],
495
+ acq_process[:lock_info][:lock_key],
496
+ acq_process[:acq_time],
497
+ instrument
498
+ )
494
499
  end
495
500
  end
496
501
  else
@@ -13,10 +13,14 @@ module RedisQueuedLocks::Acquier::ClearDeadRequests
13
13
  # @param log_sampling_enabled [Boolean]
14
14
  # @param log_sampling_percent [Integer]
15
15
  # @param log_sampler [#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]
16
+ # @param instr_sampling_enabled [Boolean]
17
+ # @param instr_sampling_percent [Integer]
18
+ # @param instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
16
19
  # @return [Hash<Symbol,Boolean|Hash<Symbol,Set<String>>>]
17
20
  #
18
21
  # @api private
19
22
  # @since 1.0.0
23
+ # @version 1.6.0
20
24
  def clear_dead_requests(
21
25
  redis_client,
22
26
  scan_size,
@@ -26,7 +30,10 @@ module RedisQueuedLocks::Acquier::ClearDeadRequests
26
30
  instrument,
27
31
  log_sampling_enabled,
28
32
  log_sampling_percent,
29
- log_sampler
33
+ log_sampler,
34
+ instr_sampling_enabled,
35
+ instr_sampling_percent,
36
+ instr_sampler
30
37
  )
31
38
  dead_score = RedisQueuedLocks::Resource.acquier_dead_score(dead_ttl / 1_000.0)
32
39
 
@@ -17,22 +17,32 @@ module RedisQueuedLocks::Acquier::ExtendLockTTL
17
17
  # @param lock_name [String]
18
18
  # @param milliseconds [Integer]
19
19
  # @param logger [::Logger,#debug]
20
+ # @param instrumenter [#notify]
21
+ # @param instrument [NilClass,Any]
20
22
  # @param log_sampling_enabled [Boolean]
21
23
  # @param log_sampling_percent [Integer]
22
24
  # @param log_sampler [#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]
25
+ # @param instr_sampling_enabled [Boolean]
26
+ # @param instr_sampling_percent [Integer]
27
+ # @param instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
23
28
  # @return [Hash<Symbol,Boolean|Symbol>]
24
29
  #
25
30
  # @api private
26
31
  # @since 1.0.0
27
- # @version 1.5.0
32
+ # @version 1.6.0
28
33
  def extend_lock_ttl(
29
34
  redis_client,
30
35
  lock_name,
31
36
  milliseconds,
32
37
  logger,
38
+ instrumenter,
39
+ instrument,
33
40
  log_sampling_enabled,
34
41
  log_sampling_percent,
35
- log_sampler
42
+ log_sampler,
43
+ instr_sampling_enabled,
44
+ instr_sampling_percent,
45
+ instr_sampler
36
46
  )
37
47
  lock_key = RedisQueuedLocks::Resource.prepare_lock_key(lock_name)
38
48
 
@@ -24,19 +24,46 @@ module RedisQueuedLocks::Acquier::ReleaseAllLocks
24
24
  # - Custom instrumentation data wich will be passed to the instrumenter's payload
25
25
  # with :instrument key;
26
26
  # @param log_sampling_enabled [Boolean]
27
- # - Enables <log sampling>: only the configured percent of cases will be logged;
28
- # - Works in tandem with <log_samplng_percent> option;
27
+ # - enables <log sampling>: only the configured percent of RQL cases will be logged;
28
+ # - disabled by default;
29
+ # - works in tandem with <config.log_sampling_percent and <log.sampler>;
29
30
  # @param log_sampling_percent [Integer]
30
- # - The percent of cases that should be logged;
31
- # - Take an effect when <log_sampling_enabled> parameter has <true> value
32
- # (when log sampling is enabled);
31
+ # - the percent of cases that should be logged;
32
+ # - take an effect when <config.log_sampling_enalbed> is true;
33
+ # - works in tandem with <config.log_sampling_enabled> and <config.log_sampler> configs;
33
34
  # @param log_sampler [#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]
35
+ # - percent-based log sampler that decides should be RQL case logged or not;
36
+ # - works in tandem with <config.log_sampling_enabled> and
37
+ # <config.log_sampling_percent> configs;
38
+ # - based on the ultra simple percent-based (weight-based) algorithm that uses
39
+ # SecureRandom.rand method so the algorithm error is ~(0%..13%);
40
+ # - you can provide your own log sampler with bettter algorithm that should realize
41
+ # `sampling_happened?(percent) => boolean` interface
42
+ # (see `RedisQueuedLocks::Logging::Sampler` for example);
43
+ # @param instr_sampling_enabled [Boolean]
44
+ # - enables <instrumentaion sampling>: only the configured percent
45
+ # of RQL cases will be instrumented;
46
+ # - disabled by default;
47
+ # - works in tandem with <config.instr_sampling_percent and <log.instr_sampler>;
48
+ # @param instr_sampling_percent [Integer]
49
+ # - the percent of cases that should be instrumented;
50
+ # - take an effect when <config.instr_sampling_enalbed> is true;
51
+ # - works in tandem with <config.instr_sampling_enabled> and <config.instr_sampler> configs;
52
+ # @param instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
53
+ # - percent-based log sampler that decides should be RQL case instrumented or not;
54
+ # - works in tandem with <config.instr_sampling_enabled> and
55
+ # <config.instr_sampling_percent> configs;
56
+ # - based on the ultra simple percent-based (weight-based) algorithm that uses
57
+ # SecureRandom.rand method so the algorithm error is ~(0%..13%);
58
+ # - you can provide your own log sampler with bettter algorithm that should realize
59
+ # `sampling_happened?(percent) => boolean` interface
60
+ # (see `RedisQueuedLocks::Instrument::Sampler` for example);
34
61
  # @return [RedisQueuedLocks::Data,Hash<Symbol,Any>]
35
62
  # Format: { ok: true, result: Hash<Symbol,Numeric> }
36
63
  #
37
64
  # @api private
38
65
  # @since 1.0.0
39
- # @version 1.5.0
66
+ # @version 1.6.0
40
67
  def release_all_locks(
41
68
  redis,
42
69
  batch_size,
@@ -45,7 +72,10 @@ module RedisQueuedLocks::Acquier::ReleaseAllLocks
45
72
  instrument,
46
73
  log_sampling_enabled,
47
74
  log_sampling_percent,
48
- log_sampler
75
+ log_sampler,
76
+ instr_sampling_enabled,
77
+ instr_sampling_percent,
78
+ instr_sampler
49
79
  )
50
80
  rel_start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
51
81
  fully_release_all_locks(redis, batch_size) => { ok:, result: }
@@ -53,13 +83,19 @@ module RedisQueuedLocks::Acquier::ReleaseAllLocks
53
83
  rel_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
54
84
  rel_time = ((rel_end_time - rel_start_time) / 1_000).ceil(2)
55
85
 
86
+ instr_sampled = RedisQueuedLocks::Instrument.should_instrument?(
87
+ instr_sampling_enabled,
88
+ instr_sampling_percent,
89
+ instr_sampler
90
+ )
91
+
56
92
  run_non_critical do
57
93
  instrumenter.notify('redis_queued_locks.explicit_all_locks_release', {
58
94
  at: time_at,
59
95
  rel_time: rel_time,
60
96
  rel_key_cnt: result[:rel_key_cnt]
61
97
  })
62
- end
98
+ end if instr_sampled
63
99
 
64
100
  RedisQueuedLocks::Data[
65
101
  ok: true,
@@ -24,25 +24,47 @@ module RedisQueuedLocks::Acquier::ReleaseLock
24
24
  # - Logger object used from `configuration` layer (see config[:logger]);
25
25
  # - See RedisQueuedLocks::Logging::VoidLogger for example;
26
26
  # @param log_sampling_enabled [Boolean]
27
- # - The percent of cases that should be logged;
28
- # - Sampling algorithm is super simple and works via SecureRandom.rand method
29
- # on the base of "weight" algorithm;
30
- # - You can provide your own sampler via config[:log_sampler] config and :sampler option
31
- # (see `RedisQueuedLocks::Logging::Sampler` for examples);
32
- # - The spread of guaranteed percent is approximately +13% (rand method spread);
33
- # - Take an effect when <log_sampling_enabled> parameter has <true> value
34
- # (when log sampling is enabled);
27
+ # - enables <log sampling>: only the configured percent of RQL cases will be logged;
28
+ # - disabled by default;
29
+ # - works in tandem with <config.log_sampling_percent and <log.sampler>;
35
30
  # @param log_sampling_percent [Integer]
36
- # - The percent of cases that should be logged;
37
- # - Take an effect when <log_sampling_enabled> parameter has <true> value
38
- # (when log sampling is enabled);
31
+ # - the percent of cases that should be logged;
32
+ # - take an effect when <config.log_sampling_enalbed> is true;
33
+ # - works in tandem with <config.log_sampling_enabled> and <config.log_sampler> configs;
39
34
  # @param log_sampler [#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]
35
+ # - percent-based log sampler that decides should be RQL case logged or not;
36
+ # - works in tandem with <config.log_sampling_enabled> and
37
+ # <config.log_sampling_percent> configs;
38
+ # - based on the ultra simple percent-based (weight-based) algorithm that uses
39
+ # SecureRandom.rand method so the algorithm error is ~(0%..13%);
40
+ # - you can provide your own log sampler with bettter algorithm that should realize
41
+ # `sampling_happened?(percent) => boolean` interface
42
+ # (see `RedisQueuedLocks::Logging::Sampler` for example);
43
+ # @param instr_sampling_enabled [Boolean]
44
+ # - enables <instrumentaion sampling>: only the configured percent
45
+ # of RQL cases will be instrumented;
46
+ # - disabled by default;
47
+ # - works in tandem with <config.instr_sampling_percent and <log.instr_sampler>;
48
+ # @param instr_sampling_percent [Integer]
49
+ # - the percent of cases that should be instrumented;
50
+ # - take an effect when <config.instr_sampling_enalbed> is true;
51
+ # - works in tandem with <config.instr_sampling_enabled> and <config.instr_sampler> configs;
52
+ # @param instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
53
+ # - percent-based log sampler that decides should be RQL case instrumented or not;
54
+ # - works in tandem with <config.instr_sampling_enabled> and
55
+ # <config.instr_sampling_percent> configs;
56
+ # - based on the ultra simple percent-based (weight-based) algorithm that uses
57
+ # SecureRandom.rand method so the algorithm error is ~(0%..13%);
58
+ # - you can provide your own log sampler with bettter algorithm that should realize
59
+ # `sampling_happened?(percent) => boolean` interface
60
+ # (see `RedisQueuedLocks::Instrument::Sampler` for example);
40
61
  # @return [RedisQueuedLocks::Data,Hash<Symbol,Boolean<Hash<Symbol,Numeric|String|Symbol>>]
41
62
  # Format: { ok: true/false, result: Hash<Symbol,Numeric|String|Symbol> }
42
63
  #
43
64
  # @api private
44
65
  # @since 1.0.0
45
- # @version 1.5.0
66
+ # @version 1.6.0
67
+ # rubocop:disable Metrics/MethodLength
46
68
  def release_lock(
47
69
  redis,
48
70
  lock_name,
@@ -50,7 +72,10 @@ module RedisQueuedLocks::Acquier::ReleaseLock
50
72
  logger,
51
73
  log_sampling_enabled,
52
74
  log_sampling_percent,
53
- log_sampler
75
+ log_sampler,
76
+ instr_sampling_enabled,
77
+ instr_sampling_percent,
78
+ instr_sampler
54
79
  )
55
80
  lock_key = RedisQueuedLocks::Resource.prepare_lock_key(lock_name)
56
81
  lock_key_queue = RedisQueuedLocks::Resource.prepare_lock_queue(lock_name)
@@ -61,6 +86,12 @@ module RedisQueuedLocks::Acquier::ReleaseLock
61
86
  rel_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
62
87
  rel_time = ((rel_end_time - rel_start_time) / 1_000).ceil(2)
63
88
 
89
+ instr_sampled = RedisQueuedLocks::Instrument.should_instrument?(
90
+ instr_sampling_enabled,
91
+ instr_sampling_percent,
92
+ instr_sampler
93
+ )
94
+
64
95
  run_non_critical do
65
96
  instrumenter.notify('redis_queued_locks.explicit_lock_release', {
66
97
  lock_key: lock_key,
@@ -68,7 +99,7 @@ module RedisQueuedLocks::Acquier::ReleaseLock
68
99
  rel_time: rel_time,
69
100
  at: time_at
70
101
  })
71
- end
102
+ end if instr_sampled
72
103
 
73
104
  RedisQueuedLocks::Data[
74
105
  ok: true,
@@ -81,6 +112,7 @@ module RedisQueuedLocks::Acquier::ReleaseLock
81
112
  }
82
113
  ]
83
114
  end
115
+ # rubocop:enable Metrics/MethodLength
84
116
 
85
117
  private
86
118