redis_queued_locks 1.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5be49c89b572553d2de7af37b8bbc38439bcefc5b2b33cd4d8e542fb3c9dd3e3
4
- data.tar.gz: 8a3a5509e82bd45ff9576677e2c2017c670fdd6a825da8ad0281f0fc1b6e7e69
3
+ metadata.gz: dd9e0ee49d4c08d6ba572a825da792468926b2f676e872a2337b1ce2eff60836
4
+ data.tar.gz: ae635ac450f966539581c4b5a53098b92ba29562bb0641049571ca0ceb1461e7
5
5
  SHA512:
6
- metadata.gz: 8f151272fadc4be25fd1344f9b29a5b263bc8ab63d17e8d09c55178232e4e57acac456c5987c0040172643e0a479976f9c12d0ff81549ba223ed742adb0dfcd1
7
- data.tar.gz: c862d4040d1dbe421a7a8e2a10c8e24c8e22cd74c65d64746cb74ad96c2d31b3c1ec7a55f22cc0149ea0a65014f4d891d8a13f4336b07d6f56130673203ff384
6
+ metadata.gz: b678b3fbccd5273d17d07120c144b0eb1c6be32db73ff1aa2f9fb19f485d30499645f3c4592fad0cb4cdeab1ebcd37f437b62740e01a46dce999eaaa2814f146
7
+ data.tar.gz: b8cb79c79767cd1d3b831992454c84c8399f6bf7ac313b8a2d0a7527711f555cb2196a172ae435103df2fc4c3e483b0a5b35775fba4baee7b7bad6234103cb0a
data/CHANGELOG.md CHANGED
@@ -1,8 +1,14 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.6.0] - 2024-05-25
4
+ ### Added
5
+ - New Feature: **Instrumentation Sampling** - configurable instrumentation sampling based on `weight` algorithm (where the weight is a percentage of RQL cases that should be logged);
6
+ - Missing instrumenter customization in public `RedisQueuedLocks::Client` methods;
7
+ - Documentation updates;
8
+
3
9
  ## [1.5.0] - 2024-05-23
4
10
  ### Added
5
- - New Feature: **Log sampling** - configurable log sampling based on `weight` algorithm (where the weight is a percentage);
11
+ - New Feature: **Log sampling** - configurable log sampling based on `weight` algorithm (where the weight is a percentage of RQL cases that should be logged);
6
12
 
7
13
  ## [1.4.0] - 2024-05-13
8
14
  ### Added
data/README.md CHANGED
@@ -253,6 +253,27 @@ clinet = RedisQueuedLocks::Client.new(redis_client) do |config|
253
253
  # - you can provide your own log sampler with bettter algorithm that should realize
254
254
  # `sampling_happened?(percent) => boolean` interface (see `RedisQueuedLocks::Logging::Sampler` for example);
255
255
  config.log_sampler = RedisQueuedLocks::Logging::Sampler
256
+
257
+ # (default: false)
258
+ # - enables <instrumentaion sampling>: only the configured percent of RQL cases will be instrumented;
259
+ # - disabled by default;
260
+ # - works in tandem with <config.instr_sampling_percent and <log.instr_sampler>;
261
+ config.instr_sampling_enabled = false
262
+
263
+ # (default: 15)
264
+ # - the percent of cases that should be instrumented;
265
+ # - take an effect when <config.instr_sampling_enalbed> is true;
266
+ # - works in tandem with <config.instr_sampling_enabled> and <config.instr_sampler> configs;
267
+ config.instr_sampling_percent = 15
268
+
269
+ # (default: RedisQueuedLocks::Instrument::Sampler)
270
+ # - percent-based log sampler that decides should be RQL case instrumented or not;
271
+ # - works in tandem with <config.instr_sampling_enabled> and <config.instr_sampling_percent> configs;
272
+ # - based on the ultra simple percent-based (weight-based) algorithm that uses SecureRandom.rand
273
+ # method so the algorithm error is ~(0%..13%);
274
+ # - you can provide your own log sampler with bettter algorithm that should realize
275
+ # `sampling_happened?(percent) => boolean` interface (see `RedisQueuedLocks::Instrument::Sampler` for example);
276
+ config.instr_sampler = RedisQueuedLocks::Instrument::Sampler
256
277
  end
257
278
  ```
258
279
 
@@ -309,11 +330,15 @@ def lock(
309
330
  identity: uniq_identity, # (attr_accessor) calculated during client instantiation via config[:uniq_identifier] proc;
310
331
  meta: nil,
311
332
  instrument: nil,
333
+ instrumenter: config[:instrumenter],
312
334
  logger: config[:logger],
313
335
  log_lock_try: config[:log_lock_try],
314
336
  log_sampling_enabled: config[:log_sampling_enabled],
315
337
  log_sampling_percent: config[:log_sampling_percent],
316
338
  log_sampler: config[:log_sampler],
339
+ instr_sampling_enabled: config[:instr_sampling_enabled],
340
+ instr_sampling_percent: config[:instr_sampling_percent],
341
+ instr_sampler: config[:instr_sampler],
317
342
  &block
318
343
  )
319
344
  ```
@@ -346,6 +371,9 @@ def lock(
346
371
  - See RedisQueuedLocks::Instrument::ActiveSupport for example;
347
372
  - See [Instrumentation](#instrumentation) section of docs;
348
373
  - pre-configured in `config[:isntrumenter]` with void notifier (`RedisQueuedLocks::Instrumenter::VoidNotifier`);
374
+ - `instrument` - (optional) `[NilClass,Any]`
375
+ - Custom instrumentation data wich will be passed to the instrumenter's payload with :instrument key;
376
+ - `nil` by default (means "no custom instrumentation data");
349
377
  - `raise_errors` - (optional) `[Boolean]`
350
378
  - Raise errors on library-related limits (such as timeout or retry count limit) and on lock conflicts (such as same-process dead locks);
351
379
  - `false` by default;
@@ -377,9 +405,6 @@ def lock(
377
405
  - A custom metadata wich will be passed to the lock data in addition to the existing data;
378
406
  - Custom metadata can not contain reserved lock data keys (such as `lock_key`, `acq_id`, `ts`, `ini_ttl`, `rem_ttl`);
379
407
  - `nil` by default (means "no metadata");
380
- - `instrument` - (optional) `[NilClass,Any]`
381
- - Custom instrumentation data wich will be passed to the instrumenter's payload with :instrument key;
382
- - `nil` by default (means "no custom instrumentation data");
383
408
  - `logger` - (optional) `[::Logger,#debug]`
384
409
  - Logger object used for loggin internal mutation oeprations and opertioan results / process progress;
385
410
  - pre-configured in `config[:logger]` with void logger `RedisQueuedLocks::Logging::VoidLogger`;
@@ -405,6 +430,24 @@ def lock(
405
430
  - you can provide your own log sampler with bettter algorithm that should realize
406
431
  `sampling_happened?(percent) => boolean` interface (see `RedisQueuedLocks::Logging::Sampler` for example);
407
432
  - pre-configured in `config[:log_sampler]`;
433
+ - `instr_sampling_enabled` - (optional) `[Boolean]`
434
+ - enables **instrumentaion sampling**: only the configured percent of RQL cases will be instrumented;
435
+ - disabled by default;
436
+ - works in tandem with `instr_sampling_percent` and `instr_sampler` options;
437
+ - pre-configured in `config[:instr_sampling_enabled]`;
438
+ - `instr_sampling_percent` - (optional) `[Integer]`
439
+ - the percent of cases that should be instrumented;
440
+ - take an effect when `instr_sampling_enalbed` is true;
441
+ - works in tandem with `instr_sampling_enabled` and `instr_sampler` options;
442
+ - pre-configured in `config[:instr_sampling_percent]`;
443
+ - `instr_sampler` - (optional) `[#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]`
444
+ - percent-based log sampler that decides should be RQL case instrumented or not;
445
+ - works in tandem with `instr_sampling_enabled` and `instr_sampling_percent` options;
446
+ - based on the ultra simple percent-based (weight-based) algorithm that uses SecureRandom.rand
447
+ method so the algorithm error is ~(0%..13%);
448
+ - you can provide your own log sampler with bettter algorithm that should realize
449
+ `sampling_happened?(percent) => boolean` interface (see `RedisQueuedLocks::Instrument::Sampler` for example);
450
+ - pre-configured in `config[:instr_sampler]`;
408
451
  - `block` - (optional) `[Block]`
409
452
  - A block of code that should be executed after the successfully acquired lock.
410
453
  - If block is **passed** the obtained lock will be released after the block execution or it's ttl (what will happen first);
@@ -558,7 +601,7 @@ rql.lock("my_lock", queue_ttl: 5, timeout: 10_000, retry_count: nil)
558
601
 
559
602
  # lock queue: =>
560
603
  [
561
- "rql:acq:123/456/567/676/374dd74324",
604
+ "rql:acq:123/456/567/676/374dd74324",
562
605
  "rql:acq:123/456/567/677/374dd74322", # <- long living lock
563
606
  "rql:acq:123/456/567/679/374dd74321",
564
607
  "rql:acq:123/456/567/683/374dd74322", # <== we are here
@@ -613,10 +656,14 @@ def lock!(
613
656
  logger: config[:logger],
614
657
  log_lock_try: config[:log_lock_try],
615
658
  instrument: nil,
659
+ instrumenter: config[:instrumenter],
616
660
  conflict_strategy: config[:default_conflict_strategy],
617
661
  log_sampling_enabled: config[:log_sampling_enabled],
618
662
  log_sampling_percent: config[:log_sampling_percent],
619
663
  log_sampler: config[:log_sampler],
664
+ instr_sampling_enabled: config[:instr_sampling_enabled],
665
+ instr_sampling_percent: config[:instr_sampling_percent],
666
+ instr_sampler: config[:instr_sampler],
620
667
  &block
621
668
  )
622
669
  ```
@@ -797,6 +844,15 @@ rql.queued?("your_lock_name") # => true/false
797
844
  - `:log_sampler` - (optional) `[#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]`
798
845
  - **log sampling**: percent-based log sampler that decides should be RQL case logged or not;
799
846
  - pre-configured in `config[:log_sampler]`;
847
+ - `:instr_sampling_enabled` - (optional) `[Boolean]`
848
+ - enables **instrumentaion sampling**;
849
+ - pre-configured in `config[:instr_sampling_enabled]`;
850
+ - `instr_sampling_percent` - (optional) `[Integer]`
851
+ - the percent of cases that should be instrumented;
852
+ - pre-configured in `config[:instr_sampling_percent]`;
853
+ - `instr_sampler` - (optional) `[#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]`
854
+ - percent-based log sampler that decides should be RQL case instrumented or not;
855
+ - pre-configured in `config[:instr_sampler]`;
800
856
  - if you try to unlock non-existent lock you will receive `ok: true` result with operation timings
801
857
  and `:nothing_to_release` result factor inside;
802
858
 
@@ -856,6 +912,15 @@ rql.unlock("your_lock_name")
856
912
  - `:log_sampler` - (optional) `[#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]`
857
913
  - **log sampling**: percent-based log sampler that decides should be RQL case logged or not;
858
914
  - pre-configured in `config[:log_sampler]`;
915
+ - `:instr_sampling_enabled` - (optional) `[Boolean]`
916
+ - enables **instrumentaion sampling**;
917
+ - pre-configured in `config[:instr_sampling_enabled]`;
918
+ - `instr_sampling_percent` - (optional) `[Integer]`
919
+ - the percent of cases that should be instrumented;
920
+ - pre-configured in `config[:instr_sampling_percent]`;
921
+ - `instr_sampler` - (optional) `[#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]`
922
+ - percent-based log sampler that decides should be RQL case instrumented or not;
923
+ - pre-configured in `config[:instr_sampler]`;
859
924
  - returns:
860
925
  - `[Hash<Symbol,Numeric>]` - Format: `{ ok: true, result: Hash<Symbol,Numeric> }`;
861
926
  - result data:
@@ -888,6 +953,12 @@ rql.clear_locks
888
953
  - the lock name which ttl should be extended;
889
954
  - `milliseconds` - (required) `[Integer]`
890
955
  - how many milliseconds should be added to the lock's TTL;
956
+ - `:instrumenter` - (optional) `[#notify]`
957
+ - custom instrumenter object;
958
+ - pre-configured in `config[:instrumetner]`;
959
+ - `:instrument` - (optional) `[NilClass,Any]`;
960
+ - custom instrumentation data wich will be passed to the instrumenter's payload with :instrument key;
961
+ - `nil` by default (no additional data);
891
962
  - `:logger` - (optional) `[::Logger,#debug]`
892
963
  - custom logger object;
893
964
  - pre-configured in `config[:logger]`;
@@ -900,6 +971,15 @@ rql.clear_locks
900
971
  - `:log_sampler` - (optional) `[#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]`
901
972
  - **log sampling**: percent-based log sampler that decides should be RQL case logged or not;
902
973
  - pre-configured in `config[:log_sampler]`;
974
+ - `:instr_sampling_enabled` - (optional) `[Boolean]`
975
+ - enables **instrumentaion sampling**;
976
+ - pre-configured in `config[:instr_sampling_enabled]`;
977
+ - `instr_sampling_percent` - (optional) `[Integer]`
978
+ - the percent of cases that should be instrumented;
979
+ - pre-configured in `config[:instr_sampling_percent]`;
980
+ - `instr_sampler` - (optional) `[#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]`
981
+ - percent-based log sampler that decides should be RQL case instrumented or not;
982
+ - pre-configured in `config[:instr_sampler]`;
903
983
  - returns `{ ok: true, result: :ttl_extended }` when ttl is extended;
904
984
  - returns `{ ok: false, result: :async_expire_or_no_lock }` when a lock not found or a lock is already expired during
905
985
  some steps of invocation (see **Important** section below);
@@ -1137,6 +1217,15 @@ Accepts:
1137
1217
  - `:log_sampler` - (optional) `[#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]`
1138
1218
  - **log sampling**: percent-based log sampler that decides should be RQL case logged or not;
1139
1219
  - pre-configured in `config[:log_sampler]`;
1220
+ - `:instr_sampling_enabled` - (optional) `[Boolean]`
1221
+ - enables **instrumentaion sampling**;
1222
+ - pre-configured in `config[:instr_sampling_enabled]`;
1223
+ - `instr_sampling_percent` - (optional) `[Integer]`
1224
+ - the percent of cases that should be instrumented;
1225
+ - pre-configured in `config[:instr_sampling_percent]`;
1226
+ - `instr_sampler` - (optional) `[#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]`
1227
+ - percent-based log sampler that decides should be RQL case instrumented or not;
1228
+ - pre-configured in `config[:instr_sampler]`;
1140
1229
 
1141
1230
  Returns: `{ ok: true, processed_queues: Set<String> }` returns the list of processed lock queues;
1142
1231
 
@@ -1335,7 +1424,6 @@ Detalized event semantics and payload structure:
1335
1424
  <sup>\[[back to top](#table-of-contents)\]</sup>
1336
1425
 
1337
1426
  - **Major**:
1338
- - precent-based instrumentation sampling (instrument the concrete % of cases via "sampling" strategy);
1339
1427
  - support for Random Access strategy (non-queued behavior);
1340
1428
  - lock request prioritization;
1341
1429
  - **strict redlock algorithm support** (support for many `RedisClient` instances);
@@ -29,11 +29,12 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
29
29
  # @param conflict_strategy [Symbol]
30
30
  # @param meta [NilClass,Hash<String|Symbol,Any>]
31
31
  # @param log_sampled [Boolean]
32
+ # @param instr_sampled [Boolean]
32
33
  # @return [Hash<Symbol,Any>] Format: { ok: true/false, result: Symbol|Hash<Symbol,Any> }
33
34
  #
34
35
  # @api private
35
36
  # @since 1.0.0
36
- # @version 1.5.0
37
+ # @version 1.6.0
37
38
  # rubocop:disable Metrics/MethodLength
38
39
  def try_to_lock(
39
40
  redis,
@@ -48,7 +49,8 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
48
49
  fail_fast,
49
50
  conflict_strategy,
50
51
  meta,
51
- log_sampled
52
+ log_sampled,
53
+ instr_sampled
52
54
  )
53
55
  # Step X: intermediate invocation results
54
56
  inter_result = nil
@@ -485,11 +487,12 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
485
487
  # @param queue_ttl [Integer]
486
488
  # @param acquier_id [String]
487
489
  # @param log_sampled [Boolean]
490
+ # @param instr_sampled [Boolean]
488
491
  # @return [Hash<Symbol,Any>] Format: { ok: true/false, result: Any }
489
492
  #
490
493
  # @api private
491
494
  # @since 1.0.0
492
- # @version 1.5.0
495
+ # @version 1.6.0
493
496
  def dequeue_from_lock_queue(
494
497
  redis,
495
498
  logger,
@@ -497,7 +500,8 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
497
500
  lock_key_queue,
498
501
  queue_ttl,
499
502
  acquier_id,
500
- log_sampled
503
+ log_sampled,
504
+ instr_sampled
501
505
  )
502
506
  result = redis.call('ZREM', lock_key_queue, acquier_id)
503
507
 
@@ -24,7 +24,8 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldExpire
24
24
  # @param ttl [Integer,NilClass] Lock's time to live (in ms). Nil means "without timeout".
25
25
  # @param queue_ttl [Integer] Lock request lifetime.
26
26
  # @param block [Block] Custom logic that should be invoked unter the obtained lock.
27
- # @param log_sampled [Boolean] Should the logic be logged or not (is log sample happened?).
27
+ # @param log_sampled [Boolean] Should the logic be logged or not.
28
+ # @param instr_sampled [Boolean] Should the logic be instrumented or not.
28
29
  # @param should_expire [Boolean] Should the lock be expired after the block invocation.
29
30
  # @param should_decrease [Boolean]
30
31
  # - Should decrease the lock TTL after the lock invocation;
@@ -33,7 +34,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldExpire
33
34
  #
34
35
  # @api private
35
36
  # @since 1.3.0
36
- # @version 1.5.0
37
+ # @version 1.6.0
37
38
  # rubocop:disable Metrics/MethodLength
38
39
  def yield_expire(
39
40
  redis,
@@ -45,6 +46,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldExpire
45
46
  ttl,
46
47
  queue_ttl,
47
48
  log_sampled,
49
+ instr_sampled,
48
50
  should_expire,
49
51
  should_decrease,
50
52
  &block
@@ -73,11 +75,13 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldExpire
73
75
  "acq_id => '#{acquier_id}'"
74
76
  end
75
77
  end if log_sampled
78
+
76
79
  redis.call('EXPIRE', lock_key, '0')
77
80
  elsif should_decrease
78
81
  finish_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :millisecond)
79
82
  spent_time = (finish_time - initial_time)
80
83
  decreased_ttl = ttl - spent_time - RedisQueuedLocks::Resource::REDIS_TIMESHIFT_ERROR
84
+
81
85
  if decreased_ttl > 0
82
86
  run_non_critical do
83
87
  logger.debug do
@@ -88,6 +92,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldExpire
88
92
  "acq_id => '#{acquier_id}' " \
89
93
  end
90
94
  end if log_sampled
95
+
91
96
  # NOTE:# NOTE: EVAL signature -> <lua script>, (number of keys), *(keys), *(arguments)
92
97
  redis.call('EVAL', DECREASE_LOCK_PTTL, 1, lock_key, decreased_ttl)
93
98
  # TODO: upload scripts to the redis
@@ -87,19 +87,40 @@ module RedisQueuedLocks::Acquier::AcquireLock
87
87
  # - `:wait_for_lock`;
88
88
  # - `:dead_locking`;
89
89
  # @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);
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>;
98
93
  # @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);
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;
102
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);
103
124
  # @param [Block]
104
125
  # A block of code that should be executed after the successfully acquired lock.
105
126
  # @return [RedisQueuedLocks::Data,Hash<Symbol,Any>,yield]
@@ -108,7 +129,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
108
129
  #
109
130
  # @api private
110
131
  # @since 1.0.0
111
- # @version 1.4.0
132
+ # @version 1.6.0
112
133
  def acquire_lock(
113
134
  redis,
114
135
  lock_name,
@@ -135,6 +156,9 @@ module RedisQueuedLocks::Acquier::AcquireLock
135
156
  log_sampling_enabled:,
136
157
  log_sampling_percent:,
137
158
  log_sampler:,
159
+ instr_sampling_enabled:,
160
+ instr_sampling_percent:,
161
+ instr_sampler:,
138
162
  &block
139
163
  )
140
164
  # Step 0: Prevent argument type incompatabilities
@@ -187,6 +211,11 @@ module RedisQueuedLocks::Acquier::AcquireLock
187
211
  log_sampling_percent,
188
212
  log_sampler
189
213
  )
214
+ instr_sampled = RedisQueuedLocks::Instrument.should_instrument?(
215
+ instr_sampling_enabled,
216
+ instr_sampling_percent,
217
+ instr_sampler
218
+ )
190
219
 
191
220
  # Step X: intermediate result observer
192
221
  acq_process = {
@@ -207,7 +236,8 @@ module RedisQueuedLocks::Acquier::AcquireLock
207
236
  lock_key_queue,
208
237
  queue_ttl,
209
238
  acquier_id,
210
- log_sampled
239
+ log_sampled,
240
+ instr_sampled
211
241
  )
212
242
  end
213
243
 
@@ -263,7 +293,8 @@ module RedisQueuedLocks::Acquier::AcquireLock
263
293
  fail_fast,
264
294
  conflict_strategy,
265
295
  meta,
266
- log_sampled
296
+ log_sampled,
297
+ instr_sampled
267
298
  ) => { ok:, result: }
268
299
 
269
300
  acq_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
@@ -297,7 +328,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
297
328
  acq_time: acq_time,
298
329
  instrument:
299
330
  })
300
- end
331
+ end if instr_sampled
301
332
  elsif acq_process[:result][:process] == :conflict_work_through
302
333
  # instrumetnation: (reentrant lock without ttl extension)
303
334
  run_non_critical do
@@ -319,7 +350,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
319
350
  acq_time: acq_time,
320
351
  instrument:
321
352
  })
322
- end
353
+ end if instr_sampled
323
354
  else
324
355
  # instrumentation: (classic lock obtain)
325
356
  # NOTE: classic is: acq_process[:result][:process] == :lock_obtaining
@@ -343,7 +374,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
343
374
  acq_time: acq_time,
344
375
  instrument:
345
376
  })
346
- end
377
+ end if instr_sampled
347
378
  end
348
379
 
349
380
  # Step 2.1.a: successfully acquired => build the result
@@ -452,6 +483,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
452
483
  ttl,
453
484
  queue_ttl,
454
485
  log_sampled,
486
+ instr_sampled,
455
487
  should_expire, # NOTE: should expire the lock after the block execution
456
488
  should_decrease, # NOTE: should decrease the lock ttl in reentrant locks?
457
489
  &block
@@ -477,7 +509,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
477
509
  acq_time: acq_process[:acq_time],
478
510
  instrument:
479
511
  })
480
- end
512
+ end if instr_sampled
481
513
  else
482
514
  # Step X (instrumentation): lock_hold_and_release
483
515
  run_non_critical do
@@ -490,7 +522,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
490
522
  acq_time: acq_process[:acq_time],
491
523
  instrument:
492
524
  })
493
- end
525
+ end if instr_sampled
494
526
  end
495
527
  end
496
528
  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
 
@@ -26,6 +26,9 @@ class RedisQueuedLocks::Client
26
26
  setting :log_sampling_enabled, false
27
27
  setting :log_sampling_percent, 15
28
28
  setting :log_sampler, RedisQueuedLocks::Logging::Sampler
29
+ setting :instr_sampling_enabled, false
30
+ setting :instr_sampling_percent, 15
31
+ setting :instr_sampler, RedisQueuedLocks::Instrument::Sampler
29
32
 
30
33
  validate('retry_count') { |val| val == nil || (val.is_a?(::Integer) && val >= 0) }
31
34
  validate('retry_delay') { |val| val.is_a?(::Integer) && val >= 0 }
@@ -43,6 +46,9 @@ class RedisQueuedLocks::Client
43
46
  validate('log_sampler') { |val| RedisQueuedLocks::Logging.valid_sampler?(val) }
44
47
  validate('log_sampling_enabled', :boolean)
45
48
  validate('log_sampling_percent') { |val| val.is_a?(::Integer) && val >= 0 && val <= 100 }
49
+ validate('instr_sampling_enabled', :boolean)
50
+ validate('instr_sampling_percent') { |val| val.is_a?(::Integer) && val >= 0 && val <= 100 }
51
+ validate('instr_sampler') { |val| RedisQueuedLocks::Instrument.valid_sampler?(val) }
46
52
  validate('default_conflict_strategy') do |val|
47
53
  # rubocop:disable Layout/MultilineOperationIndentation
48
54
  val == :work_through ||
@@ -98,8 +104,6 @@ class RedisQueuedLocks::Client
98
104
  # A time-interval between the each retry (in milliseconds).
99
105
  # @option retry_jitter [Integer]
100
106
  # Time-shift range for retry-delay (in milliseconds).
101
- # @option instrumenter [#notify]
102
- # See RedisQueuedLocks::Instrument::ActiveSupport for example.
103
107
  # @option raise_errors [Boolean]
104
108
  # Raise errors on library-related limits such as timeout or failed lock obtain.
105
109
  # @option identity [String]
@@ -133,6 +137,12 @@ class RedisQueuedLocks::Client
133
137
  # - should be logged the each try of lock acquiring (a lot of logs can
134
138
  # be generated depending on your retry configurations);
135
139
  # - see `config[:log_lock_try]`;
140
+ # @option instrumenter [#notify]
141
+ # - Custom instrumenter that will be invoked via #notify method with `event` and `payload` data;
142
+ # - See RedisQueuedLocks::Instrument::ActiveSupport for examples and implementation details;
143
+ # - See [Instrumentation](#instrumentation) section of docs;
144
+ # - pre-configured in `config[:isntrumenter]` with void notifier
145
+ # (`RedisQueuedLocks::Instrumenter::VoidNotifier`);
136
146
  # @option instrument [NilClass,Any]
137
147
  # - Custom instrumentation data wich will be passed to the instrumenter's payload
138
148
  # with :instrument key;
@@ -153,6 +163,24 @@ class RedisQueuedLocks::Client
153
163
  # - you can provide your own log sampler with bettter algorithm that should realize
154
164
  # `sampling_happened?(percent) => boolean` interface
155
165
  # (see `RedisQueuedLocks::Logging::Sampler` for example);
166
+ # @option instr_sampling_enabled [Boolean]
167
+ # - enables <instrumentaion sampling>: only the configured percent
168
+ # of RQL cases will be instrumented;
169
+ # - disabled by default;
170
+ # - works in tandem with <config.instr_sampling_percent and <log.instr_sampler>;
171
+ # @option instr_sampling_percent [Integer]
172
+ # - the percent of cases that should be instrumented;
173
+ # - take an effect when <config.instr_sampling_enalbed> is true;
174
+ # - works in tandem with <config.instr_sampling_enabled> and <config.instr_sampler> configs;
175
+ # @option instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
176
+ # - percent-based log sampler that decides should be RQL case instrumented or not;
177
+ # - works in tandem with <config.instr_sampling_enabled> and
178
+ # <config.instr_sampling_percent> configs;
179
+ # - based on the ultra simple percent-based (weight-based) algorithm that uses SecureRandom.rand
180
+ # method so the algorithm error is ~(0%..13%);
181
+ # - you can provide your own log sampler with bettter algorithm that should realize
182
+ # `sampling_happened?(percent) => boolean` interface
183
+ # (see `RedisQueuedLocks::Instrument::Sampler` for example);
156
184
  # @param block [Block]
157
185
  # A block of code that should be executed after the successfully acquired lock.
158
186
  # @return [RedisQueuedLocks::Data,Hash<Symbol,Any>,yield]
@@ -161,7 +189,7 @@ class RedisQueuedLocks::Client
161
189
  #
162
190
  # @api public
163
191
  # @since 1.0.0
164
- # @version 1.5.0
192
+ # @version 1.6.0
165
193
  # rubocop:disable Metrics/MethodLength
166
194
  def lock(
167
195
  lock_name,
@@ -179,10 +207,14 @@ class RedisQueuedLocks::Client
179
207
  meta: nil,
180
208
  logger: config[:logger],
181
209
  log_lock_try: config[:log_lock_try],
210
+ instrumenter: config[:instrumenter],
182
211
  instrument: nil,
183
212
  log_sampling_enabled: config[:log_sampling_enabled],
184
213
  log_sampling_percent: config[:log_sampling_percent],
185
214
  log_sampler: config[:log_sampler],
215
+ instr_sampling_enabled: config[:instr_sampling_enabled],
216
+ instr_sampling_percent: config[:instr_sampling_percent],
217
+ instr_sampler: config[:instr_sampler],
186
218
  &block
187
219
  )
188
220
  RedisQueuedLocks::Acquier::AcquireLock.acquire_lock(
@@ -200,7 +232,7 @@ class RedisQueuedLocks::Client
200
232
  retry_delay:,
201
233
  retry_jitter:,
202
234
  raise_errors:,
203
- instrumenter: config[:instrumenter],
235
+ instrumenter:,
204
236
  identity:,
205
237
  fail_fast:,
206
238
  conflict_strategy:,
@@ -211,6 +243,9 @@ class RedisQueuedLocks::Client
211
243
  log_sampling_enabled:,
212
244
  log_sampling_percent:,
213
245
  log_sampler:,
246
+ instr_sampling_enabled:,
247
+ instr_sampling_percent:,
248
+ instr_sampler:,
214
249
  &block
215
250
  )
216
251
  end
@@ -220,7 +255,8 @@ class RedisQueuedLocks::Client
220
255
  #
221
256
  # @api public
222
257
  # @since 1.0.0
223
- # @version 1.5.0
258
+ # @version 1.6.0
259
+ # rubocop:disable Metrics/MethodLength
224
260
  def lock!(
225
261
  lock_name,
226
262
  ttl: config[:default_lock_ttl],
@@ -233,6 +269,7 @@ class RedisQueuedLocks::Client
233
269
  fail_fast: false,
234
270
  conflict_strategy: config[:default_conflict_strategy],
235
271
  identity: uniq_identity,
272
+ instrumenter: config[:instrumenter],
236
273
  meta: nil,
237
274
  logger: config[:logger],
238
275
  log_lock_try: config[:log_lock_try],
@@ -240,6 +277,9 @@ class RedisQueuedLocks::Client
240
277
  log_sampling_enabled: config[:log_sampling_enabled],
241
278
  log_sampling_percent: config[:log_sampling_percent],
242
279
  log_sampler: config[:log_sampler],
280
+ instr_sampling_enabled: config[:instr_sampling_enabled],
281
+ instr_sampling_percent: config[:instr_sampling_percent],
282
+ instr_sampler: config[:instr_sampler],
243
283
  &block
244
284
  )
245
285
  lock(
@@ -258,13 +298,18 @@ class RedisQueuedLocks::Client
258
298
  log_lock_try:,
259
299
  meta:,
260
300
  instrument:,
301
+ instrumenter:,
261
302
  conflict_strategy:,
262
303
  log_sampling_enabled:,
263
304
  log_sampling_percent:,
264
305
  log_sampler:,
306
+ instr_sampling_enabled:,
307
+ instr_sampling_percent:,
308
+ instr_sampler:,
265
309
  &block
266
310
  )
267
311
  end
312
+ # rubocop:enable Metrics/MethodLength
268
313
 
269
314
  # @param lock_name [String] The lock name that should be released.
270
315
  # @option logger [::Logger,#debug]
@@ -273,6 +318,9 @@ class RedisQueuedLocks::Client
273
318
  # @option log_sampling_enabled [Boolean]
274
319
  # @option log_sampling_percent [Integer]
275
320
  # @option log_sampler [#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]
321
+ # @option instr_sampling_enabled [Boolean]
322
+ # @option instr_sampling_percent [Integer]
323
+ # @option instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
276
324
  # @return [RedisQueuedLocks::Data, Hash<Symbol,Any>]
277
325
  # Format: {
278
326
  # ok: true/false,
@@ -287,7 +335,7 @@ class RedisQueuedLocks::Client
287
335
  #
288
336
  # @api public
289
337
  # @since 1.0.0
290
- # @version 1.5.0
338
+ # @version 1.6.0
291
339
  def unlock(
292
340
  lock_name,
293
341
  logger: config[:logger],
@@ -295,16 +343,22 @@ class RedisQueuedLocks::Client
295
343
  instrument: nil,
296
344
  log_sampling_enabled: config[:log_sampling_enabled],
297
345
  log_sampling_percent: config[:log_sampling_percent],
298
- log_sampler: config[:log_sampler]
346
+ log_sampler: config[:log_sampler],
347
+ instr_sampling_enabled: config[:instr_sampling_enabled],
348
+ instr_sampling_percent: config[:instr_sampling_percent],
349
+ instr_sampler: config[:instr_sampler]
299
350
  )
300
351
  RedisQueuedLocks::Acquier::ReleaseLock.release_lock(
301
352
  redis_client,
302
353
  lock_name,
303
- config[:instrumenter],
304
- config[:logger],
354
+ instrumenter,
355
+ logger,
305
356
  log_sampling_enabled,
306
357
  log_sampling_percent,
307
- log_sampler
358
+ log_sampler,
359
+ instr_sampling_enabled,
360
+ instr_sampling_percent,
361
+ instr_sampler
308
362
  )
309
363
  end
310
364
 
@@ -358,43 +412,47 @@ class RedisQueuedLocks::Client
358
412
  # @param lock_name [String]
359
413
  # @param milliseconds [Integer] How many milliseconds should be added.
360
414
  # @option logger [::Logger,#debug]
415
+ # @option instrumenter [#notify] See `config[:instrumenter]` docs for details.
416
+ # @option instrument [NilClass,Any]
361
417
  # @option log_sampling_enabled [Boolean]
362
- # - The percent of cases that should be logged;
363
- # - Sampling algorithm is super simple and works via SecureRandom.rand method
364
- # on the base of "weight" algorithm;
365
- # - You can provide your own sampler via config[:log_sampler] config and :sampler option
366
- # (see `RedisQueuedLocks::Logging::Sampler` for examples);
367
- # - The spread of guaranteed percent is approximately +13% (rand method spread);
368
- # - Take an effect when <log_sampling_enabled> parameter has <true> value
369
- # (when log sampling is enabled);
370
418
  # @option log_sampling_percent [Integer]
371
- # - The percent of cases that should be logged;
372
- # - Take an effect when <log_sampling_enabled> parameter has <true> value
373
- # (when log sampling is enabled);
374
419
  # @option log_sampler [#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]
420
+ # @option instr_sampling_enabled [Boolean]
421
+ # @option instr_sampling_percent [Integer]
422
+ # @option instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
375
423
  # @return [Hash<Symbol,Boolean|Symbol>]
376
424
  # - { ok: true, result: :ttl_extended }
377
425
  # - { ok: false, result: :async_expire_or_no_lock }
378
426
  #
379
427
  # @api public
380
428
  # @since 1.0.0
381
- # @version 1.5.0
429
+ # @version 1.6.0
382
430
  def extend_lock_ttl(
383
431
  lock_name,
384
432
  milliseconds,
385
433
  logger: config[:logger],
434
+ instrumenter: config[:instrumenter],
435
+ instrument: nil,
386
436
  log_sampling_enabled: config[:log_sampling_enabled],
387
437
  log_sampling_percent: config[:log_sampling_percent],
388
- log_sampler: config[:log_sampler]
438
+ log_sampler: config[:log_sampler],
439
+ instr_sampling_enabled: config[:instr_sampling_enabled],
440
+ instr_sampling_percent: config[:instr_sampling_percent],
441
+ instr_sampler: config[:instr_sampler]
389
442
  )
390
443
  RedisQueuedLocks::Acquier::ExtendLockTTL.extend_lock_ttl(
391
444
  redis_client,
392
445
  lock_name,
393
446
  milliseconds,
394
447
  logger,
448
+ instrumenter,
449
+ instrument,
395
450
  log_sampling_enabled,
396
451
  log_sampling_percent,
397
- log_sampler
452
+ log_sampler,
453
+ instr_sampling_enabled,
454
+ instr_sampling_percent,
455
+ instr_sampler
398
456
  )
399
457
  end
400
458
 
@@ -405,17 +463,20 @@ class RedisQueuedLocks::Client
405
463
  #
406
464
  # @option batch_size [Integer]
407
465
  # @option logger [::Logger,#debug]
408
- # @option instrumenter [#notify]
466
+ # @option instrumenter [#notify] See `config[:instrumenter]` docs for details.
409
467
  # @option instrument [NilClass,Any]
410
468
  # @option log_sampling_enabled [Boolean]
411
469
  # @option log_sampling_percent [Integer]
412
470
  # @option log_sampler [#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]
471
+ # @option instr_sampling_enabled [Boolean]
472
+ # @option instr_sampling_percent [Integer]
473
+ # @option instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
413
474
  # @return [RedisQueuedLocks::Data,Hash<Symbol,Boolean|Hash<Symbol,Numeric>>]
414
475
  # Example: { ok: true, result { rel_key_cnt: 100, rel_time: 0.01 } }
415
476
  #
416
477
  # @api public
417
478
  # @since 1.0.0
418
- # @version 1.5.0
479
+ # @version 1.6.0
419
480
  def clear_locks(
420
481
  batch_size: config[:lock_release_batch_size],
421
482
  logger: config[:logger],
@@ -423,7 +484,10 @@ class RedisQueuedLocks::Client
423
484
  instrument: nil,
424
485
  log_sampling_enabled: config[:log_sampling_enabled],
425
486
  log_sampling_percent: config[:log_sampling_percent],
426
- log_sampler: config[:log_sampler]
487
+ log_sampler: config[:log_sampler],
488
+ instr_sampling_enabled: config[:instr_sampling_enabled],
489
+ instr_sampling_percent: config[:instr_sampling_percent],
490
+ instr_sampler: config[:instr_sampler]
427
491
  )
428
492
  RedisQueuedLocks::Acquier::ReleaseAllLocks.release_all_locks(
429
493
  redis_client,
@@ -433,7 +497,10 @@ class RedisQueuedLocks::Client
433
497
  instrument,
434
498
  log_sampling_enabled,
435
499
  log_sampling_percent,
436
- log_sampler
500
+ log_sampler,
501
+ instr_sampling_enabled,
502
+ instr_sampling_percent,
503
+ instr_sampler
437
504
  )
438
505
  end
439
506
 
@@ -524,12 +591,15 @@ class RedisQueuedLocks::Client
524
591
  # @option log_sampling_enabled [Boolean]
525
592
  # @option log_sampling_percent [Integer]
526
593
  # @option log_sampler [#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]
594
+ # @option instr_sampling_enabled [Boolean]
595
+ # @option instr_sampling_percent [Integer]
596
+ # @option instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
527
597
  # @return [Hash<Symbol,Boolean|Hash<Symbol,Set<String>>>]
528
598
  # Format: { ok: true, result: { processed_queus: Set<String> } }
529
599
  #
530
600
  # @api public
531
601
  # @since 1.0.0
532
- # @version 1.5.0
602
+ # @version 1.6.0
533
603
  def clear_dead_requests(
534
604
  dead_ttl: config[:dead_request_ttl],
535
605
  scan_size: config[:lock_release_batch_size],
@@ -538,7 +608,10 @@ class RedisQueuedLocks::Client
538
608
  instrument: nil,
539
609
  log_sampling_enabled: config[:log_sampling_enabled],
540
610
  log_sampling_percent: config[:log_sampling_percent],
541
- log_sampler: config[:log_sampler]
611
+ log_sampler: config[:log_sampler],
612
+ instr_sampling_enabled: config[:instr_sampling_enabled],
613
+ instr_sampling_percent: config[:instr_sampling_percent],
614
+ instr_sampler: config[:instr_sampler]
542
615
  )
543
616
  RedisQueuedLocks::Acquier::ClearDeadRequests.clear_dead_requests(
544
617
  redis_client,
@@ -549,7 +622,10 @@ class RedisQueuedLocks::Client
549
622
  instrument,
550
623
  log_sampling_enabled,
551
624
  log_sampling_percent,
552
- log_sampler
625
+ log_sampler,
626
+ instr_sampling_enabled,
627
+ instr_sampling_percent,
628
+ instr_sampler
553
629
  )
554
630
  end
555
631
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api public
4
+ # @since 1.6.0
5
+ module RedisQueuedLocks::Instrument::Sampler
6
+ # @return [Range]
7
+ #
8
+ # @api private
9
+ # @since 1.6.0
10
+ SAMPLING_PERCENT_RANGE = (0..100)
11
+
12
+ class << self
13
+ # Super simple probalistic function based on the `weight` of <true>/<false> values.
14
+ # Requires the <percent> parameter as the required percent of <true> values sampled.
15
+ #
16
+ # @param sampling_percent [Integer]
17
+ # - percent of <true> values in range of 0..100;
18
+ # @return [Boolean]
19
+ # - <true> for <sampling_percent>% of invocations (and <false> for the rest invocations)
20
+ #
21
+ # @api public
22
+ # @since 1.6.0
23
+ def sampling_happened?(sampling_percent)
24
+ sampling_percent >= SecureRandom.rand(SAMPLING_PERCENT_RANGE)
25
+ end
26
+ end
27
+ end
@@ -2,11 +2,58 @@
2
2
 
3
3
  # @api public
4
4
  # @since 1.0.0
5
+ # @version 1.6.0
5
6
  module RedisQueuedLocks::Instrument
6
7
  require_relative 'instrument/void_notifier'
7
8
  require_relative 'instrument/active_support'
9
+ require_relative 'instrument/sampler'
8
10
 
9
11
  class << self
12
+ # @param instr_sampling_enabled [Boolean]
13
+ # @param instr_sampling_percent [Integer]
14
+ # @param instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
15
+ # @return [Boolean]
16
+ #
17
+ # @api private
18
+ # @since 1.6.0
19
+ def should_instrument?(instr_sampling_enabled, instr_sampling_percent, instr_sampler)
20
+ return true unless instr_sampling_enabled
21
+ instr_sampler.sampling_happened?(instr_sampling_percent)
22
+ end
23
+
24
+ # @param sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
25
+ # @return [Boolean]
26
+ #
27
+ # @api private
28
+ # @since 1.6.0
29
+ def valid_sampler?(sampler)
30
+ return false unless sampler.respond_to?(:sampling_happened?)
31
+
32
+ m_obj = sampler.method(:sampling_happened?)
33
+ m_sig = m_obj.parameters
34
+
35
+ # NOTE:
36
+ # Required method signature (sampling_percent)
37
+ # => [[:req, :sampling_percent]]
38
+ # => [[:opt, :sampling_percent]]
39
+ # => [[:req, :sampling_percent], [:block, :block]]
40
+ # => [[:opt, :sampling_percent], [:block, :block]]
41
+ if m_sig.size == 1
42
+ prm = m_sig[0][0]
43
+ prm == :req || prm == :opt
44
+ elsif m_sig.size == 2
45
+ f_prm = m_sig[0][0]
46
+ s_prm = m_sign[1][0]
47
+
48
+ # rubocop:disable Layout/MultilineOperationIndentation
49
+ f_prm == :req && s_prm == :block ||
50
+ f_prm == :opt && s_prm == :block
51
+ # rubocop:enable Layout/MultilineOperationIndentation
52
+ else
53
+ false
54
+ end
55
+ end
56
+
10
57
  # @param instrumenter [Class,Module,Object]
11
58
  # @return [Boolean]
12
59
  #
@@ -1,8 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # @api private
3
+ # @api public
4
4
  # @since 1.5.0
5
5
  module RedisQueuedLocks::Logging::Sampler
6
+ # @return [Range]
7
+ #
8
+ # @api private
9
+ # @since 1.6.0
10
+ SAMPLING_PERCENT_RANGE = (0..100)
11
+
6
12
  class << self
7
13
  # Super simple probalistic function based on the `weight` of <true>/<false> values.
8
14
  # Requires the <percent> parameter as the required percent of <true> values sampled.
@@ -12,10 +18,11 @@ module RedisQueuedLocks::Logging::Sampler
12
18
  # @return [Boolean]
13
19
  # - <true> for <sampling_percent>% of invocations (and <false> for the rest invocations)
14
20
  #
15
- # @api private
21
+ # @api public
16
22
  # @since 1.5.0
23
+ # @version 1.6.0
17
24
  def sampling_happened?(sampling_percent)
18
- sampling_percent >= SecureRandom.rand(0..100)
25
+ sampling_percent >= SecureRandom.rand(SAMPLING_PERCENT_RANGE)
19
26
  end
20
27
  end
21
28
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  # @api public
4
4
  # @since 1.0.0
5
+ # @version 1.5.0
5
6
  module RedisQueuedLocks::Logging
6
7
  require_relative 'logging/void_logger'
7
8
  require_relative 'logging/sampler'
@@ -5,6 +5,6 @@ module RedisQueuedLocks
5
5
  #
6
6
  # @api public
7
7
  # @since 0.0.1
8
- # @version 1.5.0
9
- VERSION = '1.5.0'
8
+ # @version 1.6.0
9
+ VERSION = '1.6.0'
10
10
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis_queued_locks
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rustam Ibragimov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-05-23 00:00:00.000000000 Z
11
+ date: 2024-05-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis-client
@@ -79,6 +79,7 @@ files:
79
79
  - lib/redis_queued_locks/errors.rb
80
80
  - lib/redis_queued_locks/instrument.rb
81
81
  - lib/redis_queued_locks/instrument/active_support.rb
82
+ - lib/redis_queued_locks/instrument/sampler.rb
82
83
  - lib/redis_queued_locks/instrument/void_notifier.rb
83
84
  - lib/redis_queued_locks/logging.rb
84
85
  - lib/redis_queued_locks/logging/sampler.rb
@@ -109,7 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
109
110
  - !ruby/object:Gem::Version
110
111
  version: '0'
111
112
  requirements: []
112
- rubygems_version: 3.5.1
113
+ rubygems_version: 3.3.7
113
114
  signing_key:
114
115
  specification_version: 4
115
116
  summary: Queued distributed locks based on Redis.