redis_queued_locks 1.5.0 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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