redis_queued_locks 1.3.0 → 1.4.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: 06ddeeb8f9ef0bd0c24742e68e83d10b237f8233ca5183c7f9612e7aebadd874
4
- data.tar.gz: df9a7ba009357fc3fa80efc9b5837cec56f10cb9004f54df19e5d08fd06f7f26
3
+ metadata.gz: 448f8b89e4a34bb2543b55d46877d80f76cf8e4b5d07735486f53eb1edb37432
4
+ data.tar.gz: 4fbefadd9a162ab298b100bca38387045c65c89918ffd70c3f4aa418aa72d22a
5
5
  SHA512:
6
- metadata.gz: 722aff2cb859419913d8f47c6118acaab378db70e49034290043cb1f9c3b4df115d15020d37effe815399d389fb360b68f2692a50537165043eb8a659428b6ba
7
- data.tar.gz: ef370212d73abc96b1fed6962e274b616bb9b1ed232576ebee0b1e2e2b98d62cbdbe7dbb113ac32b28e489d4142a3a2f91b7f96be1be6c11d285cb948711bfee
6
+ metadata.gz: 738c503d6189ca4673d1bc5af829066378654a070b78691ae7d1001e832795dbb91f1cde3e89252ff6b9831a659daf517eea1ea2ab721101c9140afe16b44974
7
+ data.tar.gz: cb2b4893238e55688b01c56f5f0004217d206122d5ae5ef3a0ea8f2d206323442e4c10bf78f3d504f947795864decc712f5385448b205151ccfe7ca39ab9ea0a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.4.0] - 2024-05-13
4
+ ### Added
5
+ - `#lock`/`#lock!`: reduced memory allocaiton during `:meta` attribute type checking;
6
+ ### Changed
7
+ - More accurate time analyzis operations;
8
+
9
+ ## [1.3.1] - 2024-05-10
10
+ ### Fixed
11
+ - `:meta` attribute type validation of `#lock`/`#lock!` was incorrect;
12
+ ### Added
13
+ - documentation updates and clarifications;
14
+
3
15
  ## [1.3.0] - 2024-05-08
4
16
  ### Added
5
17
  - **Major Feature**: support for **Reentrant Locks**;
data/README.md CHANGED
@@ -151,7 +151,9 @@ clinet = RedisQueuedLocks::Client.new(redis_client) do |config|
151
151
  config.is_timed_by_default = false
152
152
 
153
153
  # (symbol) (default: :wait_for_lock)
154
- # - The conflict strategy mode for cases when the process that obtained the lock want to acquire this lock again;
154
+ # - Global default conflict strategy mode;
155
+ # - Can be customized in methods `#lock` and `#lock` via `:conflict_strategy` attribute (see method signatures of #lock and #lock! methods);
156
+ # - Conflict strategy is a logical behavior for cases when the process that obtained the lock want to acquire this lock again;
155
157
  # - Realizes "reentrant locks" abstraction (same process conflict / same process deadlock);
156
158
  # - By default uses `:wait_for_lock` strategy (classic way);
157
159
  # - Strategies:
@@ -159,7 +161,6 @@ clinet = RedisQueuedLocks::Client.new(redis_client) do |config|
159
161
  # - `:extendable_work_through` - continue working under the lock <with> lock's TTL extension;
160
162
  # - `:wait_for_lock` - (default) - work in classic way (with timeouts, retry delays, retry limits, etc - in classic way :));
161
163
  # - `:dead_locking` - fail with deadlock exception;
162
- # - Can be customized in methods via `:conflict_strategy` attribute (see method signatures of #lock and #lock! methods);
163
164
  # - See "Dead locks and Reentrant Locks" documentation section in REDME.md for details;
164
165
  config.default_conflict_strategy = :wait_for_lock
165
166
 
@@ -262,10 +263,14 @@ end
262
263
 
263
264
  <sup>\[[back to top](#usage)\]</sup>
264
265
 
265
- - If block is passed the obtained lock will be released after the block execution or the lock's ttl (what will happen first);
266
- - If block is not passed the obtained lock will be released after lock's ttl;
267
- - If block is passed the block's yield result will be returned;
268
- - If block is not passed the lock information will be returned;
266
+ - `#lock` - obtain a lock;
267
+ - If block is passed:
268
+ - the obtained lock will be released after the block execution or the lock's ttl (what will happen first);
269
+ - if you want to timeout (fail with timeout) the block execution with lock's TTL use `timed: true` option;
270
+ - the block's result will be returned;
271
+ - If block is not passed:
272
+ - the obtained lock will be released after lock's ttl;
273
+ - the lock information will be returned (hash with technical info that contains: lock key, acquier identifier, acquirement timestamp, lock's ttl, type of obtaining process, etc);
269
274
 
270
275
  ```ruby
271
276
  def lock(
@@ -543,10 +548,12 @@ rql.lock("my_lock", queue_ttl: 5, timeout: 10_000, retry_count: nil)
543
548
 
544
549
  <sup>\[[back to top](#usage)\]</sup>
545
550
 
551
+ - `#lock!` - exceptional lock obtaining;
546
552
  - fails when (and with):
547
553
  - (`RedisQueuedLocks::LockAlreadyObtainedError`) when `fail_fast` is `true` and lock is already obtained;
548
554
  - (`RedisQueuedLocks::LockAcquiermentTimeoutError`) `timeout` limit reached before lock is obtained;
549
555
  - (`RedisQueuedLocks::LockAcquiermentRetryLimitError`) `retry_count` limit reached before lock is obtained;
556
+ - (`RedisQueuedLocks::ConflictLockObtainError`) when `conflict_strategy: :dead_locking` is used and the "same-process-dead-lock" is happened (see [Dead locks and Reentrant locks](#dead-locks-and-reentrant-locks) for details);
550
557
 
551
558
  ```ruby
552
559
  def lock!(
@@ -648,7 +655,7 @@ rql.lock_info("your_lock_name")
648
655
  # ==> keys for extendable reentarnt locks with `:extendable_work_through` strategy:
649
656
  "spc_ext_ttl" => 5_000, # sum of TTL of the each <extendable> reentrant lock (3_000 + 2_000)
650
657
  "l_spc_ext_ini_ttl" => 2_000, # TTL of the last <extendable> reentrant lock
651
- "l_spc_ext_ts" => 123456792.12345 # timestamp of the last <extendable> reentrant lock obtaining
658
+ "l_spc_ext_ts" => 123456792.12345, # timestamp of the last <extendable> reentrant lock obtaining
652
659
  # ==> keys for non-extendable locks with `:work_through` strategy:
653
660
  "l_spc_ts" => 123456.789 # timestamp of the last <non-extendable> reentrant lock obtaining
654
661
  }
@@ -1073,13 +1080,17 @@ rql.clear_dead_requests(dead_ttl: 60 * 60 * 1000) # 1 hour in milliseconds
1073
1080
 
1074
1081
  <sup>\[[back to top](#table-of-contents)\]</sup>
1075
1082
 
1076
- - **documentation is in progress**
1077
- - (little details for a context of current implementation and feautres):
1078
- - at this moment we support only **reentrant locks**: they works via customizable conflict strategy behavior;
1079
- - in non-reentrant conflict cases your lock obtaining process will work in a classic way (some dead lock conflict => work in "wait for lock" style);
1083
+ - **this documentation section is in progress**;
1084
+ - (little details for a context of the current implementation and feautres):
1085
+ - at this moment we support only **reentrant locks**: they works via customizable conflict strategy behavior
1086
+ (`:wait_for_lock` (default), `:work_through`, `:extendable_work_through`, `:dead_locking`);
1087
+ - by default behavior (`:wait_for_lock`) your lock obtaining process will work in a classic way (limits, retries, etc);
1088
+ - `:work_through`, `:extendable_work_through` works with limits too (timeouts, delays, etc), but the decision of
1089
+ "is your lock are obtained or not" is made as you work with **reentrant locks** (your process continues to use the lock without/with
1090
+ lock's TTL accordingly);
1080
1091
  - for current implementation details check:
1081
- - (Configuration)[#configuration] documentation: see `config.default_conflict_strategy` config docs;
1082
- - (#lock)[#lock] method documentation: see `conflict_strategy` attribute docs;
1092
+ - [Configuration](#configuration) documentation: see `config.default_conflict_strategy` config docs;
1093
+ - [#lock](#lock---obtain-a-lock) method documentation: see `conflict_strategy` attribute docs and the method result data;
1083
1094
 
1084
1095
  ---
1085
1096
 
@@ -49,7 +49,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldExpire
49
49
  initial_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :millisecond)
50
50
 
51
51
  if block_given?
52
- timeout = ((ttl - ttl_shift) / 1000.0).yield_self do |time|
52
+ timeout = ((ttl - ttl_shift) / 1_000.0).yield_self do |time|
53
53
  # NOTE: time in <seconds> cuz Ruby's Timeout requires <seconds>
54
54
  (time < 0) ? 0.0 : time
55
55
  end
@@ -94,7 +94,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
94
94
  #
95
95
  # @api private
96
96
  # @since 1.0.0
97
- # @version 1.3.0
97
+ # @version 1.4.0
98
98
  def acquire_lock(
99
99
  redis,
100
100
  lock_name,
@@ -132,7 +132,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
132
132
  end
133
133
 
134
134
  # Step 0.2: prevent :meta incompatabiltiies (structure)
135
- if meta == ::Hash && (meta.keys.any? do |key|
135
+ if meta.is_a?(::Hash) && (meta.any? do |key, _value|
136
136
  key == 'acq_id' ||
137
137
  key == 'ts' ||
138
138
  key == 'ini_ttl' ||
@@ -198,7 +198,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
198
198
 
199
199
  # Step 2: try to lock with timeout
200
200
  with_acq_timeout(timeout, lock_key, raise_errors, on_timeout: acq_dequeue) do
201
- acq_start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
201
+ acq_start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
202
202
 
203
203
  # Step 2.1: cyclically try to obtain the lock
204
204
  while acq_process[:should_try]
@@ -241,8 +241,8 @@ module RedisQueuedLocks::Acquier::AcquireLock
241
241
  meta
242
242
  ) => { ok:, result: }
243
243
 
244
- acq_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
245
- acq_time = ((acq_end_time - acq_start_time) * 1_000).ceil(2)
244
+ acq_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
245
+ acq_time = ((acq_end_time - acq_start_time) / 1_000.0).ceil(2)
246
246
 
247
247
  # Step X: save the intermediate results to the result observer
248
248
  acq_process[:result] = result
@@ -403,10 +403,10 @@ module RedisQueuedLocks::Acquier::AcquireLock
403
403
  # Step 3.a: acquired successfully => run logic or return the result of acquirement
404
404
  if block_given?
405
405
  begin
406
- yield_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
406
+ yield_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
407
407
 
408
408
  ttl_shift = (
409
- (yield_time - acq_process[:acq_end_time]) * 1000 -
409
+ (yield_time - acq_process[:acq_end_time]) / 1_000.0 -
410
410
  RedisQueuedLocks::Resource::REDIS_TIMESHIFT_ERROR
411
411
  ).ceil(2)
412
412
 
@@ -431,9 +431,11 @@ module RedisQueuedLocks::Acquier::AcquireLock
431
431
  &block
432
432
  )
433
433
  ensure
434
- acq_process[:rel_time] = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
434
+ acq_process[:rel_time] = ::Process.clock_gettime(
435
+ ::Process::CLOCK_MONOTONIC, :microsecond
436
+ )
435
437
  acq_process[:hold_time] = (
436
- (acq_process[:rel_time] - acq_process[:acq_end_time]) * 1000
438
+ (acq_process[:rel_time] - acq_process[:acq_end_time]) / 1_000.0
437
439
  ).ceil(2)
438
440
 
439
441
  if acq_process[:result][:process] == :extendable_conflict_work_through ||
@@ -15,7 +15,7 @@ module RedisQueuedLocks::Acquier::ClearDeadRequests
15
15
  # @api private
16
16
  # @since 1.0.0
17
17
  def clear_dead_requests(redis_client, scan_size, dead_ttl, logger, instrumenter, instrument)
18
- dead_score = RedisQueuedLocks::Resource.acquier_dead_score(dead_ttl / 1000.0)
18
+ dead_score = RedisQueuedLocks::Resource.acquier_dead_score(dead_ttl / 1_000.0)
19
19
 
20
20
  result = Set.new.tap do |processed_queues|
21
21
  redis_client.with do |rconn|
@@ -28,12 +28,13 @@ module RedisQueuedLocks::Acquier::ReleaseAllLocks
28
28
  #
29
29
  # @api private
30
30
  # @since 1.0.0
31
+ # @version 1.4.0
31
32
  def release_all_locks(redis, batch_size, logger, instrumenter, instrument)
32
- rel_start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
33
+ rel_start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
33
34
  fully_release_all_locks(redis, batch_size) => { ok:, result: }
34
35
  time_at = Time.now.to_f
35
- rel_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
36
- rel_time = ((rel_end_time - rel_start_time) * 1_000).ceil(2)
36
+ rel_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
37
+ rel_time = ((rel_end_time - rel_start_time) / 1_000).ceil(2)
37
38
 
38
39
  run_non_critical do
39
40
  instrumenter.notify('redis_queued_locks.explicit_all_locks_release', {
@@ -28,15 +28,16 @@ module RedisQueuedLocks::Acquier::ReleaseLock
28
28
  #
29
29
  # @api private
30
30
  # @since 1.0.0
31
+ # @version 1.4.0
31
32
  def release_lock(redis, lock_name, instrumenter, logger)
32
33
  lock_key = RedisQueuedLocks::Resource.prepare_lock_key(lock_name)
33
34
  lock_key_queue = RedisQueuedLocks::Resource.prepare_lock_queue(lock_name)
34
35
 
35
- rel_start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
36
+ rel_start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
36
37
  fully_release_lock(redis, lock_key, lock_key_queue) => { ok:, result: }
37
38
  time_at = Time.now.to_f
38
- rel_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
39
- rel_time = ((rel_end_time - rel_start_time) * 1_000).ceil(2)
39
+ rel_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
40
+ rel_time = ((rel_end_time - rel_start_time) / 1_000).ceil(2)
40
41
 
41
42
  run_non_critical do
42
43
  instrumenter.notify('redis_queued_locks.explicit_lock_release', {
@@ -5,6 +5,6 @@ module RedisQueuedLocks
5
5
  #
6
6
  # @api public
7
7
  # @since 0.0.1
8
- # @version 1.3.0
9
- VERSION = '1.3.0'
8
+ # @version 1.4.0
9
+ VERSION = '1.4.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.3.0
4
+ version: 1.4.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-08 00:00:00.000000000 Z
11
+ date: 2024-05-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis-client
@@ -108,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
108
108
  - !ruby/object:Gem::Version
109
109
  version: '0'
110
110
  requirements: []
111
- rubygems_version: 3.3.7
111
+ rubygems_version: 3.5.1
112
112
  signing_key:
113
113
  specification_version: 4
114
114
  summary: Queued distributed locks based on Redis.