redis_queued_locks 1.5.0 → 1.6.0

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