redis_queued_locks 1.4.0 → 1.6.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.
@@ -86,6 +86,41 @@ module RedisQueuedLocks::Acquier::AcquireLock
86
86
  # - `:extendable_work_through`;
87
87
  # - `:wait_for_lock`;
88
88
  # - `:dead_locking`;
89
+ # @option log_sampling_enabled [Boolean]
90
+ # - enables <log sampling>: only the configured percent of RQL cases will be logged;
91
+ # - disabled by default;
92
+ # - works in tandem with <config.log_sampling_percent and <log.sampler>;
93
+ # @option log_sampling_percent [Integer]
94
+ # - the percent of cases that should be logged;
95
+ # - take an effect when <config.log_sampling_enalbed> is true;
96
+ # - works in tandem with <config.log_sampling_enabled> and <config.log_sampler> configs;
97
+ # @option log_sampler [#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]
98
+ # - percent-based log sampler that decides should be RQL case logged or not;
99
+ # - works in tandem with <config.log_sampling_enabled> and
100
+ # <config.log_sampling_percent> configs;
101
+ # - based on the ultra simple percent-based (weight-based) algorithm that uses
102
+ # SecureRandom.rand method so the algorithm error is ~(0%..13%);
103
+ # - you can provide your own log sampler with bettter algorithm that should realize
104
+ # `sampling_happened?(percent) => boolean` interface
105
+ # (see `RedisQueuedLocks::Logging::Sampler` for example);
106
+ # @option instr_sampling_enabled [Boolean]
107
+ # - enables <instrumentaion sampling>: only the configured percent
108
+ # of RQL cases will be instrumented;
109
+ # - disabled by default;
110
+ # - works in tandem with <config.instr_sampling_percent and <log.instr_sampler>;
111
+ # @option instr_sampling_percent [Integer]
112
+ # - the percent of cases that should be instrumented;
113
+ # - take an effect when <config.instr_sampling_enalbed> is true;
114
+ # - works in tandem with <config.instr_sampling_enabled> and <config.instr_sampler> configs;
115
+ # @option instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
116
+ # - percent-based log sampler that decides should be RQL case instrumented or not;
117
+ # - works in tandem with <config.instr_sampling_enabled> and
118
+ # <config.instr_sampling_percent> configs;
119
+ # - based on the ultra simple percent-based (weight-based) algorithm that uses
120
+ # SecureRandom.rand method so the algorithm error is ~(0%..13%);
121
+ # - you can provide your own log sampler with bettter algorithm that should realize
122
+ # `sampling_happened?(percent) => boolean` interface
123
+ # (see `RedisQueuedLocks::Instrument::Sampler` for example);
89
124
  # @param [Block]
90
125
  # A block of code that should be executed after the successfully acquired lock.
91
126
  # @return [RedisQueuedLocks::Data,Hash<Symbol,Any>,yield]
@@ -94,7 +129,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
94
129
  #
95
130
  # @api private
96
131
  # @since 1.0.0
97
- # @version 1.4.0
132
+ # @version 1.6.0
98
133
  def acquire_lock(
99
134
  redis,
100
135
  lock_name,
@@ -118,6 +153,12 @@ module RedisQueuedLocks::Acquier::AcquireLock
118
153
  logger:,
119
154
  log_lock_try:,
120
155
  conflict_strategy:,
156
+ log_sampling_enabled:,
157
+ log_sampling_percent:,
158
+ log_sampler:,
159
+ instr_sampling_enabled:,
160
+ instr_sampling_percent:,
161
+ instr_sampler:,
121
162
  &block
122
163
  )
123
164
  # Step 0: Prevent argument type incompatabilities
@@ -165,6 +206,17 @@ module RedisQueuedLocks::Acquier::AcquireLock
165
206
  lock_key_queue = RedisQueuedLocks::Resource.prepare_lock_queue(lock_name)
166
207
  acquier_position = RedisQueuedLocks::Resource.calc_initial_acquier_position
167
208
 
209
+ log_sampled = RedisQueuedLocks::Logging.should_log?(
210
+ log_sampling_enabled,
211
+ log_sampling_percent,
212
+ log_sampler
213
+ )
214
+ instr_sampled = RedisQueuedLocks::Instrument.should_instrument?(
215
+ instr_sampling_enabled,
216
+ instr_sampling_percent,
217
+ instr_sampler
218
+ )
219
+
168
220
  # Step X: intermediate result observer
169
221
  acq_process = {
170
222
  lock_info: {},
@@ -183,7 +235,9 @@ module RedisQueuedLocks::Acquier::AcquireLock
183
235
  lock_key,
184
236
  lock_key_queue,
185
237
  queue_ttl,
186
- acquier_id
238
+ acquier_id,
239
+ log_sampled,
240
+ instr_sampled
187
241
  )
188
242
  end
189
243
 
@@ -194,7 +248,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
194
248
  "queue_ttl => #{queue_ttl} " \
195
249
  "acq_id => '#{acquier_id}'"
196
250
  end
197
- end
251
+ end if log_sampled
198
252
 
199
253
  # Step 2: try to lock with timeout
200
254
  with_acq_timeout(timeout, lock_key, raise_errors, on_timeout: acq_dequeue) do
@@ -209,7 +263,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
209
263
  "queue_ttl => #{queue_ttl} " \
210
264
  "acq_id => '{#{acquier_id}'"
211
265
  end
212
- end
266
+ end if log_sampled
213
267
 
214
268
  # Step 2.X: check the actual score: is it in queue ttl limit or not?
215
269
  if RedisQueuedLocks::Resource.dead_score_reached?(acquier_position, queue_ttl)
@@ -223,7 +277,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
223
277
  "queue_ttl => #{queue_ttl} " \
224
278
  "acq_id => '#{acquier_id}'"
225
279
  end
226
- end
280
+ end if log_sampled
227
281
  end
228
282
 
229
283
  try_to_lock(
@@ -238,7 +292,9 @@ module RedisQueuedLocks::Acquier::AcquireLock
238
292
  queue_ttl,
239
293
  fail_fast,
240
294
  conflict_strategy,
241
- meta
295
+ meta,
296
+ log_sampled,
297
+ instr_sampled
242
298
  ) => { ok:, result: }
243
299
 
244
300
  acq_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
@@ -261,7 +317,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
261
317
  "acq_id => '#{acquier_id}' " \
262
318
  "acq_time => #{acq_time} (ms)"
263
319
  end
264
- end
320
+ end if log_sampled
265
321
 
266
322
  run_non_critical do
267
323
  instrumenter.notify('redis_queued_locks.extendable_reentrant_lock_obtained', {
@@ -272,7 +328,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
272
328
  acq_time: acq_time,
273
329
  instrument:
274
330
  })
275
- end
331
+ end if instr_sampled
276
332
  elsif acq_process[:result][:process] == :conflict_work_through
277
333
  # instrumetnation: (reentrant lock without ttl extension)
278
334
  run_non_critical do
@@ -283,7 +339,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
283
339
  "acq_id => '#{acquier_id}' " \
284
340
  "acq_time => #{acq_time} (ms)"
285
341
  end
286
- end
342
+ end if log_sampled
287
343
 
288
344
  run_non_critical do
289
345
  instrumenter.notify('redis_queued_locks.reentrant_lock_obtained', {
@@ -294,7 +350,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
294
350
  acq_time: acq_time,
295
351
  instrument:
296
352
  })
297
- end
353
+ end if instr_sampled
298
354
  else
299
355
  # instrumentation: (classic lock obtain)
300
356
  # NOTE: classic is: acq_process[:result][:process] == :lock_obtaining
@@ -306,7 +362,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
306
362
  "acq_id => '#{acquier_id}' " \
307
363
  "acq_time => #{acq_time} (ms)"
308
364
  end
309
- end
365
+ end if log_sampled
310
366
 
311
367
  # Step X (instrumentation): lock obtained
312
368
  run_non_critical do
@@ -318,7 +374,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
318
374
  acq_time: acq_time,
319
375
  instrument:
320
376
  })
321
- end
377
+ end if instr_sampled
322
378
  end
323
379
 
324
380
  # Step 2.1.a: successfully acquired => build the result
@@ -426,6 +482,8 @@ module RedisQueuedLocks::Acquier::AcquireLock
426
482
  ttl_shift,
427
483
  ttl,
428
484
  queue_ttl,
485
+ log_sampled,
486
+ instr_sampled,
429
487
  should_expire, # NOTE: should expire the lock after the block execution
430
488
  should_decrease, # NOTE: should decrease the lock ttl in reentrant locks?
431
489
  &block
@@ -451,7 +509,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
451
509
  acq_time: acq_process[:acq_time],
452
510
  instrument:
453
511
  })
454
- end
512
+ end if instr_sampled
455
513
  else
456
514
  # Step X (instrumentation): lock_hold_and_release
457
515
  run_non_critical do
@@ -464,7 +522,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
464
522
  acq_time: acq_process[:acq_time],
465
523
  instrument:
466
524
  })
467
- end
525
+ end if instr_sampled
468
526
  end
469
527
  end
470
528
  else
@@ -10,11 +10,31 @@ module RedisQueuedLocks::Acquier::ClearDeadRequests
10
10
  # @param logger [::Logger,#debug]
11
11
  # @param instrumenter [#notify]
12
12
  # @param instrument [NilClass,Any]
13
+ # @param log_sampling_enabled [Boolean]
14
+ # @param log_sampling_percent [Integer]
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>]
13
19
  # @return [Hash<Symbol,Boolean|Hash<Symbol,Set<String>>>]
14
20
  #
15
21
  # @api private
16
22
  # @since 1.0.0
17
- def clear_dead_requests(redis_client, scan_size, dead_ttl, logger, instrumenter, instrument)
23
+ # @version 1.6.0
24
+ def clear_dead_requests(
25
+ redis_client,
26
+ scan_size,
27
+ dead_ttl,
28
+ logger,
29
+ instrumenter,
30
+ instrument,
31
+ log_sampling_enabled,
32
+ log_sampling_percent,
33
+ log_sampler,
34
+ instr_sampling_enabled,
35
+ instr_sampling_percent,
36
+ instr_sampler
37
+ )
18
38
  dead_score = RedisQueuedLocks::Resource.acquier_dead_score(dead_ttl / 1_000.0)
19
39
 
20
40
  result = Set.new.tap do |processed_queues|
@@ -17,11 +17,33 @@ 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]
22
+ # @param log_sampling_enabled [Boolean]
23
+ # @param log_sampling_percent [Integer]
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>]
20
28
  # @return [Hash<Symbol,Boolean|Symbol>]
21
29
  #
22
30
  # @api private
23
31
  # @since 1.0.0
24
- def extend_lock_ttl(redis_client, lock_name, milliseconds, logger)
32
+ # @version 1.6.0
33
+ def extend_lock_ttl(
34
+ redis_client,
35
+ lock_name,
36
+ milliseconds,
37
+ logger,
38
+ instrumenter,
39
+ instrument,
40
+ log_sampling_enabled,
41
+ log_sampling_percent,
42
+ log_sampler,
43
+ instr_sampling_enabled,
44
+ instr_sampling_percent,
45
+ instr_sampler
46
+ )
25
47
  lock_key = RedisQueuedLocks::Resource.prepare_lock_key(lock_name)
26
48
 
27
49
  # NOTE: EVAL signature -> <lua script>, (number of keys), *(keys), *(arguments)
@@ -20,29 +20,82 @@ module RedisQueuedLocks::Acquier::ReleaseAllLocks
20
20
  # - See RedisQueuedLocks::Logging::VoidLogger for example;
21
21
  # @param isntrumenter [#notify]
22
22
  # See RedisQueuedLocks::Instrument::ActiveSupport for example.
23
- # @option instrument [NilClass,Any]
23
+ # @param instrument [NilClass,Any]
24
24
  # - Custom instrumentation data wich will be passed to the instrumenter's payload
25
25
  # with :instrument key;
26
+ # @param log_sampling_enabled [Boolean]
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>;
30
+ # @param log_sampling_percent [Integer]
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;
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);
26
61
  # @return [RedisQueuedLocks::Data,Hash<Symbol,Any>]
27
62
  # Format: { ok: true, result: Hash<Symbol,Numeric> }
28
63
  #
29
64
  # @api private
30
65
  # @since 1.0.0
31
- # @version 1.4.0
32
- def release_all_locks(redis, batch_size, logger, instrumenter, instrument)
66
+ # @version 1.6.0
67
+ def release_all_locks(
68
+ redis,
69
+ batch_size,
70
+ logger,
71
+ instrumenter,
72
+ instrument,
73
+ log_sampling_enabled,
74
+ log_sampling_percent,
75
+ log_sampler,
76
+ instr_sampling_enabled,
77
+ instr_sampling_percent,
78
+ instr_sampler
79
+ )
33
80
  rel_start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
34
81
  fully_release_all_locks(redis, batch_size) => { ok:, result: }
35
82
  time_at = Time.now.to_f
36
83
  rel_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
37
84
  rel_time = ((rel_end_time - rel_start_time) / 1_000).ceil(2)
38
85
 
86
+ instr_sampled = RedisQueuedLocks::Instrument.should_instrument?(
87
+ instr_sampling_enabled,
88
+ instr_sampling_percent,
89
+ instr_sampler
90
+ )
91
+
39
92
  run_non_critical do
40
93
  instrumenter.notify('redis_queued_locks.explicit_all_locks_release', {
41
94
  at: time_at,
42
95
  rel_time: rel_time,
43
96
  rel_key_cnt: result[:rel_key_cnt]
44
97
  })
45
- end
98
+ end if instr_sampled
46
99
 
47
100
  RedisQueuedLocks::Data[
48
101
  ok: true,
@@ -23,13 +23,60 @@ module RedisQueuedLocks::Acquier::ReleaseLock
23
23
  # @param logger [::Logger,#debug]
24
24
  # - Logger object used from `configuration` layer (see config[:logger]);
25
25
  # - See RedisQueuedLocks::Logging::VoidLogger for example;
26
+ # @param log_sampling_enabled [Boolean]
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>;
30
+ # @param log_sampling_percent [Integer]
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;
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);
26
61
  # @return [RedisQueuedLocks::Data,Hash<Symbol,Boolean<Hash<Symbol,Numeric|String|Symbol>>]
27
62
  # Format: { ok: true/false, result: Hash<Symbol,Numeric|String|Symbol> }
28
63
  #
29
64
  # @api private
30
65
  # @since 1.0.0
31
- # @version 1.4.0
32
- def release_lock(redis, lock_name, instrumenter, logger)
66
+ # @version 1.6.0
67
+ # rubocop:disable Metrics/MethodLength
68
+ def release_lock(
69
+ redis,
70
+ lock_name,
71
+ instrumenter,
72
+ logger,
73
+ log_sampling_enabled,
74
+ log_sampling_percent,
75
+ log_sampler,
76
+ instr_sampling_enabled,
77
+ instr_sampling_percent,
78
+ instr_sampler
79
+ )
33
80
  lock_key = RedisQueuedLocks::Resource.prepare_lock_key(lock_name)
34
81
  lock_key_queue = RedisQueuedLocks::Resource.prepare_lock_queue(lock_name)
35
82
 
@@ -39,6 +86,12 @@ module RedisQueuedLocks::Acquier::ReleaseLock
39
86
  rel_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
40
87
  rel_time = ((rel_end_time - rel_start_time) / 1_000).ceil(2)
41
88
 
89
+ instr_sampled = RedisQueuedLocks::Instrument.should_instrument?(
90
+ instr_sampling_enabled,
91
+ instr_sampling_percent,
92
+ instr_sampler
93
+ )
94
+
42
95
  run_non_critical do
43
96
  instrumenter.notify('redis_queued_locks.explicit_lock_release', {
44
97
  lock_key: lock_key,
@@ -46,7 +99,7 @@ module RedisQueuedLocks::Acquier::ReleaseLock
46
99
  rel_time: rel_time,
47
100
  at: time_at
48
101
  })
49
- end
102
+ end if instr_sampled
50
103
 
51
104
  RedisQueuedLocks::Data[
52
105
  ok: true,
@@ -59,6 +112,7 @@ module RedisQueuedLocks::Acquier::ReleaseLock
59
112
  }
60
113
  ]
61
114
  end
115
+ # rubocop:enable Metrics/MethodLength
62
116
 
63
117
  private
64
118