redis_queued_locks 0.0.2 → 0.0.3

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: e984603ea8f509f8e5f7f685c356d1f838b16bc950e1de83ff8f66e6afa83fcf
4
- data.tar.gz: 1b2eee0f32c911caecf08214b2b404d620ffb10501ae72d7721235b64fc02362
3
+ metadata.gz: 5dbd11cefb9d18a816eb4c63fe1178f87325c30d75efc2bc806d29f41994fc2c
4
+ data.tar.gz: 9697f4bf452aaaa7cade45320c50d16b510c657074ba97513c5f8de9c51f6c49
5
5
  SHA512:
6
- metadata.gz: b7e2d574eeeff4a9a235f39ee25bde152880dcd12e7066196a8304e54098480ef05672edd89a6931ece225711b96602f23ce76304c555dbd623d30099e35ca0a
7
- data.tar.gz: d7f5075bbf136e672ebb1632da5c6a78b2b72886bc1d5e8b0d9425cf38b14f5eef5a097fdc835c78f5f2d26885512279926a33bdbdb559151b875453d5312c9e
6
+ metadata.gz: b28ef92375c7eef4d6c43fccb5e44105d653fb00e052edcf67316f8da13578ad7b39086b3799782592b38bf046760f8faa1238b92de293b83eb1e4a7c441413e
7
+ data.tar.gz: dfd3891ae8d0303b88ac95268056addc3b4e03f862a5200166de9553b412eee78b77f42193f133f9dd340793b67928806e2e05451517585c2282fb7e4c871d1f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.0.3] - 2024-02-26
4
+ ### Changed
5
+ - Instrumentation events:
6
+ - `"redis_queued_locks.explicit_all_locks_release"`
7
+ - re-factored with fully pipelined invocation;
8
+ - removed `rel_queue_cnt` and `rel_lock_cnt` because of the pipelined invocation
9
+ misses the concrete results and now we can receive only "released redis keys count";
10
+ - adde `rel_keys` payload data (released redis keys);
11
+
3
12
  ## [0.0.2] - 2024-02-26
4
13
  ### Added
5
14
  - Instrumentation events:
data/README.md CHANGED
@@ -22,7 +22,6 @@ Distributed lock implementation with "lock acquisition queue" capabilities based
22
22
  - `lock_key` - `string` - lock name;
23
23
  - `ts` - `integer`/`epoch` - the time when lock was obtained;
24
24
  - `acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
25
- - `rel_time` - `float`/`milliseconds` - time spent on lock "holding + releasing";
26
25
  - `"redis_queued_locks.explicit_lock_release"`
27
26
  - an event signalizes about the explicit lock release (invoked via `RedisQueuedLock#unlock`);
28
27
  - payload:
@@ -31,13 +30,8 @@ Distributed lock implementation with "lock acquisition queue" capabilities based
31
30
  - `lock_key` - `string` - released lock (lock name);
32
31
  - `lock_key_queue` - `string` - released lock queue (lock queue name);
33
32
  - `"redis_queued_locks.explicit_all_locks_release"`
34
- - an event signalizes about the explicit all locks release (invoked viaa `RedisQueuedLock#clear_locks`);
33
+ - an event signalizes about the explicit all locks release (invoked via `RedisQueuedLock#clear_locks`);
35
34
  - payload:
36
- - `rel_time` - `float`/`milliseconds` - time when the locks was released
35
+ - `rel_time` - `float`/`milliseconds` - time spent on the lock release;
37
36
  - `at` - `integer`/`epoch` - the time when the operation has ended;
38
- - `rel_lock_cnt` - `integer` - released lock count;
39
- - `rel_queue_cnt` - `integer` - released queue count;
40
-
41
- ## Todo
42
-
43
- - CI (github actions);
37
+ - `rel_keys` - `integer` - released redis keys count (`released queu keys` + `released lock keys`);
@@ -3,6 +3,8 @@
3
3
  # @api private
4
4
  # @since 0.1.0
5
5
  module RedisQueuedLocks::Acquier::Delay
6
+ # Sleep with random time-shifting (it is necessary for empty-time-slot lock acquirement).
7
+ #
6
8
  # @param retry_delay [Integer] In milliseconds
7
9
  # @param retry_jitter [Integer] In milliseconds
8
10
  # @return [void]
@@ -3,10 +3,10 @@
3
3
  # @api private
4
4
  # @since 0.1.0
5
5
  module RedisQueuedLocks::Acquier::Expire
6
- # @param redis [RedisClient]
7
- # @param lock_key [String]
8
- # @param block [Block]
9
- # @return [void]
6
+ # @param redis [RedisClient] Redis connection manager.
7
+ # @param lock_key [String] Lock key to be expired.
8
+ # @param block [Block] Custom logic that should be invoked unter the obtained lock.
9
+ # @return [Any,NilClass] nil is returned no block parametr is provided.
10
10
  #
11
11
  # @api private
12
12
  # @since 0.1.0
@@ -30,37 +30,31 @@ module RedisQueuedLocks::Acquier::Release
30
30
  # @api private
31
31
  # @since 0.1.0
32
32
  def fully_release_all_locks(redis, batch_size)
33
- rel_queue_cnt = 0
34
- rel_lock_cnt = 0
35
-
36
- # Step A: release all queus and their related locks
37
- redis.scan(
38
- 'MATCH',
39
- RedisQueuedLocks::Resource::LOCK_QUEUE_PATTERN,
40
- count: batch_size
41
- ) do |lock_queue|
42
- rel_queue_cnt += 1
43
- rel_lock_cnt += 1
44
-
45
- redis.pipelined do |pipeline|
33
+ result = redis.pipelined do |pipeline|
34
+ # Step A: release all queus and their related locks
35
+ redis.scan(
36
+ 'MATCH',
37
+ RedisQueuedLocks::Resource::LOCK_QUEUE_PATTERN,
38
+ count: batch_size
39
+ ) do |lock_queue|
40
+ puts "RELEASE (lock_queue): #{lock_queue}"
46
41
  pipeline.call('ZREMRANGEBYSCORE', lock_queue, '-inf', '+inf')
47
- pipeline.call('EXPIRE', RedisQueuedLocks::Resource.lock_key_from_queue(lock_queue))
42
+ pipeline.call('EXPIRE', RedisQueuedLocks::Resource.lock_key_from_queue(lock_queue), "0")
48
43
  end
49
- end
50
-
51
- # Step B: release all locks
52
- redis.pipelined do |pipeline|
53
- rel_lock_cnt += 1
54
44
 
45
+ # Step B: release all locks
55
46
  redis.scan(
56
47
  'MATCH',
57
48
  RedisQueuedLocks::Resource::LOCK_PATTERN,
58
49
  count: batch_size
59
50
  ) do |lock_key|
51
+ puts "RELEASE (lock_key): #{lock_key}"
60
52
  pipeline.call('EXPIRE', lock_key, '0')
61
53
  end
62
54
  end
63
55
 
64
- { ok: true, result: { rel_queue_cnt:, rel_lock_cnt: } }
56
+ rel_keys = result.count { |red_res| red_res == 0 }
57
+
58
+ { ok: true, result: { rel_keys: rel_keys } }
65
59
  end
66
60
  end
@@ -43,8 +43,8 @@ module RedisQueuedLocks::Acquier
43
43
  # Lifetime of the acuier's lock request. In seconds.
44
44
  # @option timeout [Integer]
45
45
  # Time period whe should try to acquire the lock (in seconds).
46
- # @option retry_count [Integer]
47
- # How many times we should try to acquire a lock.
46
+ # @option retry_count [Integer,NilClass]
47
+ # How many times we should try to acquire a lock. Nil means "infinite retries".
48
48
  # @option retry_delay [Integer]
49
49
  # A time-interval between the each retry (in milliseconds).
50
50
  # @option retry_jitter [Integer]
@@ -113,7 +113,7 @@ module RedisQueuedLocks::Acquier
113
113
  ) => { ok:, result: }
114
114
 
115
115
  acq_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
116
- acq_time = ((acq_end_time - acq_start_time) * 1_000).ceil
116
+ acq_time = ((acq_end_time - acq_start_time) * 1_000).ceil(2)
117
117
 
118
118
  # Step X: save the intermediate results to the result observer
119
119
  acq_process[:result] = result
@@ -139,20 +139,20 @@ module RedisQueuedLocks::Acquier
139
139
  acq_process[:acquired] = true
140
140
  acq_process[:should_try] = false
141
141
  acq_process[:acq_time] = acq_time
142
+ acq_process[:acq_end_time] = acq_end_time
142
143
  else
143
144
  # Step 2.1.b: failed acquirement => retry
144
145
  acq_process[:tries] += 1
145
146
 
146
- if acq_process[:tries] >= retry_count
147
+ if retry_count != nil && acq_process[:tries] >= retry_count
147
148
  # NOTE: reached the retry limit => quit from the loop
148
149
  acq_process[:should_try] = false
149
150
  # NOTE: reached the retry limit => dequeue from the lock queue
150
151
  acq_dequeue.call
151
- else
152
+ elsif delay_execution(retry_delay, retry_jitter)
152
153
  # NOTE:
153
154
  # delay the exceution in order to prevent chaotic attempts
154
155
  # and to allow other processes and threads to obtain the lock too.
155
- delay_execution(retry_delay, retry_jitter)
156
156
  end
157
157
  end
158
158
  end
@@ -163,23 +163,24 @@ module RedisQueuedLocks::Acquier
163
163
  # Step 3.a: acquired successfully => run logic or return the result of acquirement
164
164
  if block_given?
165
165
  begin
166
- yield_with_expire(redis, lock_key, instrumenter, &block)
166
+ yield_with_expire(redis, lock_key, &block)
167
167
  ensure
168
168
  acq_process[:rel_time] = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
169
- acq_process[:hold_time] = ((
170
- acq_process[:rel_time] - acq_process[:acq_time]
171
- ) * 1000).ceil
169
+ acq_process[:hold_time] = (
170
+ (acq_process[:rel_time] - acq_process[:acq_end_time]) * 1000
171
+ ).ceil(2)
172
172
 
173
173
  # Step X (instrumentation): lock_hold_and_release
174
- instrumenter.notify('redis_queued_locks.lock_hold_and_release', {
175
- hold_time: acq_process[:hold_time],
176
- rel_time: acq_process[:rel_time],
177
- ttl: acq_process[:lock_info][:ttl],
178
- acq_id: acq_process[:lock_info][:acq_id],
179
- ts: acq_process[:lock_info][:ts],
180
- lock_key: acq_process[:lock_info][:lock_key],
181
- acq_time: acq_process[:acq_time]
182
- })
174
+ run_non_critical do
175
+ instrumenter.notify('redis_queued_locks.lock_hold_and_release', {
176
+ hold_time: acq_process[:hold_time],
177
+ ttl: acq_process[:lock_info][:ttl],
178
+ acq_id: acq_process[:lock_info][:acq_id],
179
+ ts: acq_process[:lock_info][:ts],
180
+ lock_key: acq_process[:lock_info][:lock_key],
181
+ acq_time: acq_process[:acq_time]
182
+ })
183
+ end
183
184
  end
184
185
  else
185
186
  { ok: true, result: acq_process[:lock_info] }
@@ -211,17 +212,19 @@ module RedisQueuedLocks::Acquier
211
212
  lock_key_queue = RedisQueuedLocks::Resource.prepare_lock_queue(lock_name)
212
213
 
213
214
  rel_start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
214
- result = fully_release_lock(redis, lock_key, lock_key_queue)
215
+ fully_release_lock(redis, lock_key, lock_key_queue) => { ok:, result: }
215
216
  time_at = Time.now.to_i
216
217
  rel_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
217
- rel_time = ((rel_end_time - rel_start_time) * 1_000).ceil
218
+ rel_time = ((rel_end_time - rel_start_time) * 1_000).ceil(2)
218
219
 
219
- instrumenter.notify('redis_queued_locks.explicit_lock_release', {
220
- lock_key: lock_key,
221
- lock_key_queue: lock_key_queue,
222
- rel_time: rel_time,
223
- at: time_at
224
- })
220
+ run_non_critical do
221
+ instrumenter.notify('redis_queued_locks.explicit_lock_release', {
222
+ lock_key: lock_key,
223
+ lock_key_queue: lock_key_queue,
224
+ rel_time: rel_time,
225
+ at: time_at
226
+ })
227
+ end
225
228
 
226
229
  { ok: true, result: result }
227
230
  end
@@ -239,17 +242,18 @@ module RedisQueuedLocks::Acquier
239
242
  # @since 0.1.0
240
243
  def release_all_locks!(redis, batch_size, instrumenter)
241
244
  rel_start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
242
- result = fully_release_all_locks(redis, batch_size)
245
+ fully_release_all_locks(redis, batch_size) => { ok:, result: }
243
246
  time_at = Time.now.to_i
244
247
  rel_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
245
- rel_time = ((rel_end_time - rel_start_time) * 1_000).ceil
248
+ rel_time = ((rel_end_time - rel_start_time) * 1_000).ceil(2)
246
249
 
247
- instrumenter.notify('redis_queued_locks.explicit_all_locks_release', {
248
- at: time_at,
249
- rel_time: rel_time,
250
- rel_lock_cnt: result[:rel_lock_cnt],
251
- rel_queue_cnt: result[:rel_queue_cnt]
252
- })
250
+ run_non_critical do
251
+ instrumenter.notify('redis_queued_locks.explicit_all_locks_release', {
252
+ at: time_at,
253
+ rel_time: rel_time,
254
+ rel_keys: result[:rel_keys]
255
+ })
256
+ end
253
257
 
254
258
  { ok: true, result: result }
255
259
  end
@@ -279,6 +283,15 @@ module RedisQueuedLocks::Acquier
279
283
  ERROR_MESSAGE
280
284
  end
281
285
  end
286
+
287
+ # @param block [Block]
288
+ # @return [Any]
289
+ #
290
+ # @api private
291
+ # @since 0.1.0
292
+ def run_non_critical(&block)
293
+ yield rescue nil
294
+ end
282
295
  end
283
296
  # rubocop:enable Metrics/ClassLength
284
297
  end
@@ -12,15 +12,15 @@ class RedisQueuedLocks::Client
12
12
  setting :retry_jitter, 50 # NOTE: milliseconds
13
13
  setting :default_timeout, 10 # NOTE: seconds
14
14
  setting :exp_precision, 1 # NOTE: milliseconds
15
- setting :default_lock_ttl, 10_000 # NOTE: milliseconds
16
- setting :default_queue_ttl, 5 # NOTE: seconds
15
+ setting :default_lock_ttl, 5_000 # NOTE: milliseconds
16
+ setting :default_queue_ttl, 30 # NOTE: seconds
17
17
  setting :lock_release_batch_size, 100
18
18
  setting :instrumenter, RedisQueuedLocks::Instrument::VoidNotifier
19
19
 
20
20
  # TODO: setting :logger, Logger.new(IO::NULL)
21
21
  # TODO: setting :debug, true/false
22
22
 
23
- validate('retry_count', :integer)
23
+ validate('retry_count') { |val| val == nil || (val.is_a?(::Integer) && val >= 0) }
24
24
  validate('retry_delay', :integer)
25
25
  validate('retry_jitter', :integer)
26
26
  validate('default_timeout', :integer)
@@ -28,7 +28,7 @@ class RedisQueuedLocks::Client
28
28
  validate('default_lock_tt', :integer)
29
29
  validate('default_queue_ttl', :integer)
30
30
  validate('lock_release_batch_size', :integer)
31
- validate('instrumenter') { |instr| RedisQueuedLocks::Instrument.valid_interface?(instr) }
31
+ validate('instrumenter') { |val| RedisQueuedLocks::Instrument.valid_interface?(val) }
32
32
  end
33
33
 
34
34
  # @return [RedisClient]
@@ -63,8 +63,8 @@ class RedisQueuedLocks::Client
63
63
  # Lifetime of the acuier's lock request. In seconds.
64
64
  # @option timeout [Integer,NilClass]
65
65
  # Time period whe should try to acquire the lock (in seconds). Nil means "without timeout".
66
- # @option retry_count [Integer]
67
- # How many times we should try to acquire a lock.
66
+ # @option retry_count [Integer,NilClass]
67
+ # How many times we should try to acquire a lock. Nil means "infinite retries".
68
68
  # @option retry_delay [Integer]
69
69
  # A time-interval between the each retry (in milliseconds).
70
70
  # @option retry_jitter [Integer]
@@ -4,6 +4,6 @@ module RedisQueuedLocks
4
4
  # @return [String]
5
5
  #
6
6
  # @api public
7
- # @since 0.0.2
8
- VERSION = '0.0.2'
7
+ # @since 0.0.3
8
+ VERSION = '0.0.3'
9
9
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis_queued_locks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rustam Ibragimov