redis_queued_locks 1.4.0 → 1.6.0

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