redis_queued_locks 0.0.1 → 0.0.2

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: 7f78a90d3830a3797bfd973b8ce48a879ba8400c3daec8827c0c14c25c650bb3
4
- data.tar.gz: a8e09fae141ff16d142234a4200c86f05abea787fb627e23b4187dd11fa06086
3
+ metadata.gz: e984603ea8f509f8e5f7f685c356d1f838b16bc950e1de83ff8f66e6afa83fcf
4
+ data.tar.gz: 1b2eee0f32c911caecf08214b2b404d620ffb10501ae72d7721235b64fc02362
5
5
  SHA512:
6
- metadata.gz: fe28a66ec0d028453a0e43f2e140b907a100a70b8a547af8067f45f60392339dd9d0540ef9b8638d0bc9588dd0152f4e21ac488d031170cb872679c7ed4bc34a
7
- data.tar.gz: 6608fc1f6a9b2b2000254ba0b4a39d5d16c50f9265bcf58c7b138066e08c29c32d31047e3419db09aa0bfda88c09af10111254c3bf2ba35df775248f2d732d63
6
+ metadata.gz: b7e2d574eeeff4a9a235f39ee25bde152880dcd12e7066196a8304e54098480ef05672edd89a6931ece225711b96602f23ce76304c555dbd623d30099e35ca0a
7
+ data.tar.gz: d7f5075bbf136e672ebb1632da5c6a78b2b72886bc1d5e8b0d9425cf38b14f5eef5a097fdc835c78f5f2d26885512279926a33bdbdb559151b875453d5312c9e
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.0.2] - 2024-02-26
4
+ ### Added
5
+ - Instrumentation events:
6
+ - `"redis_queued_locks.lock_obtained"`;
7
+ - `"redis_queued_locks.lock_hold_and_release"`;
8
+ - `"redis_queued_locks.explicit_lock_release"`;
9
+ - `"redis_queued_locks.explicit_all_locks_release"`;
10
+
3
11
  ## [0.0.1] - 2024-02-26
4
12
 
5
13
  - Still the initial release version;
data/README.md CHANGED
@@ -5,13 +5,38 @@ Distributed lock implementation with "lock acquisition queue" capabilities based
5
5
  ## Instrumentation events
6
6
 
7
7
  - `"redis_queued_locks.lock_obtained"`
8
- - the moment when the lock was obtained;
8
+ - a moment when the lock was obtained;
9
9
  - payload:
10
10
  - `ttl` - `integer`/`milliseconds` - lock ttl;
11
11
  - `acq_id` - `string` - lock acquier identifier;
12
- - `lock_key` - `string` - lock name ;
12
+ - `lock_key` - `string` - lock name;
13
13
  - `ts` - `integer`/`epoch` - the time when the lock was obtaiend;
14
14
  - `acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
15
+ - `"redis_queued_locks.lock_hold_and_release"`
16
+ - an event signalizes about the "hold+and+release" process
17
+ when the lock obtained and hold by the block of logic;
18
+ - payload:
19
+ - `hold_time` - `float`/`milliseconds` - lock hold time;
20
+ - `ttl` - `integer`/`milliseconds` - lock ttl;
21
+ - `acq_id` - `string` - lock acquier identifier;
22
+ - `lock_key` - `string` - lock name;
23
+ - `ts` - `integer`/`epoch` - the time when lock was obtained;
24
+ - `acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
25
+ - `rel_time` - `float`/`milliseconds` - time spent on lock "holding + releasing";
26
+ - `"redis_queued_locks.explicit_lock_release"`
27
+ - an event signalizes about the explicit lock release (invoked via `RedisQueuedLock#unlock`);
28
+ - payload:
29
+ - `at` - `integer`/`epoch` - the time when the lock was released;
30
+ - `rel_time` - `float`/`milliseconds` - time spent on lock releasing;
31
+ - `lock_key` - `string` - released lock (lock name);
32
+ - `lock_key_queue` - `string` - released lock queue (lock queue name);
33
+ - `"redis_queued_locks.explicit_all_locks_release"`
34
+ - an event signalizes about the explicit all locks release (invoked viaa `RedisQueuedLock#clear_locks`);
35
+ - payload:
36
+ - `rel_time` - `float`/`milliseconds` - time when the locks was released
37
+ - `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;
15
40
 
16
41
  ## Todo
17
42
 
@@ -8,32 +8,40 @@ module RedisQueuedLocks::Acquier::Release
8
8
  # @param redis [RedisClient]
9
9
  # @param lock_key [String]
10
10
  # @param lock_key_queue [String]
11
- # @return [void]
11
+ # @return [Hash<Symbol,Any>] Format: { ok: true/false, result: Any }
12
12
  #
13
13
  # @api private
14
14
  # @since 0.1.0
15
15
  def fully_release_lock(redis, lock_key, lock_key_queue)
16
- redis.multi do |transact|
16
+ result = redis.multi do |transact|
17
17
  transact.call('ZREMRANGEBYSCORE', lock_key_queue, '-inf', '+inf')
18
18
  transact.call('EXPIRE', lock_key, '0')
19
19
  end
20
+
21
+ { ok: true, result: }
20
22
  end
21
23
 
22
24
  # Release all locks: clear all lock queus and expire all locks.
23
25
  #
24
26
  # @param redis [RedisClient]
25
27
  # @param batch_size [Integer]
26
- # @return [void]
28
+ # @return [Hash<Symbol,Any>] Format: { ok: true/false, result: Any }
27
29
  #
28
30
  # @api private
29
31
  # @since 0.1.0
30
32
  def fully_release_all_locks(redis, batch_size)
33
+ rel_queue_cnt = 0
34
+ rel_lock_cnt = 0
35
+
31
36
  # Step A: release all queus and their related locks
32
37
  redis.scan(
33
38
  'MATCH',
34
39
  RedisQueuedLocks::Resource::LOCK_QUEUE_PATTERN,
35
40
  count: batch_size
36
41
  ) do |lock_queue|
42
+ rel_queue_cnt += 1
43
+ rel_lock_cnt += 1
44
+
37
45
  redis.pipelined do |pipeline|
38
46
  pipeline.call('ZREMRANGEBYSCORE', lock_queue, '-inf', '+inf')
39
47
  pipeline.call('EXPIRE', RedisQueuedLocks::Resource.lock_key_from_queue(lock_queue))
@@ -42,6 +50,8 @@ module RedisQueuedLocks::Acquier::Release
42
50
 
43
51
  # Step B: release all locks
44
52
  redis.pipelined do |pipeline|
53
+ rel_lock_cnt += 1
54
+
45
55
  redis.scan(
46
56
  'MATCH',
47
57
  RedisQueuedLocks::Resource::LOCK_PATTERN,
@@ -50,5 +60,7 @@ module RedisQueuedLocks::Acquier::Release
50
60
  pipeline.call('EXPIRE', lock_key, '0')
51
61
  end
52
62
  end
63
+
64
+ { ok: true, result: { rel_queue_cnt:, rel_lock_cnt: } }
53
65
  end
54
66
  end
@@ -37,10 +37,10 @@ module RedisQueuedLocks::Acquier
37
37
  # The process that want to acquire the lock.
38
38
  # @option thread_id [Integer,String]
39
39
  # The process's thread that want to acquire the lock.
40
- # @option ttl [Integer]
41
- # Lock's time to live (in milliseconds).
40
+ # @option ttl [Integer,NilClass]
41
+ # Lock's time to live (in milliseconds). Nil means "without timeout".
42
42
  # @option queue_ttl [Integer]
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
46
  # @option retry_count [Integer]
@@ -90,7 +90,9 @@ module RedisQueuedLocks::Acquier
90
90
  tries: 0,
91
91
  acquired: false,
92
92
  result: nil,
93
- acq_time: nil # NOTE: in milliseconds
93
+ acq_time: nil, # NOTE: in milliseconds
94
+ hold_time: nil, # NOTE: in milliseconds
95
+ rel_time: nil # NOTE: in milliseconds
94
96
  }
95
97
  acq_dequeue = -> { dequeue_from_lock_queue(redis, lock_key_queue, acquier_id) }
96
98
 
@@ -118,7 +120,7 @@ module RedisQueuedLocks::Acquier
118
120
 
119
121
  # Step 2.1: analyze an acquirement attempt
120
122
  if ok
121
- # INSTRUMENT: lock obtained
123
+ # Step X (instrumentation): lock obtained
122
124
  instrumenter.notify('redis_queued_locks.lock_obtained', {
123
125
  lock_key: result[:lock_key],
124
126
  ttl: result[:ttl],
@@ -160,7 +162,25 @@ module RedisQueuedLocks::Acquier
160
162
  if acq_process[:acquired]
161
163
  # Step 3.a: acquired successfully => run logic or return the result of acquirement
162
164
  if block_given?
163
- yield_with_expire(redis, lock_key, &block) # INSTRUMENT: lock release
165
+ begin
166
+ yield_with_expire(redis, lock_key, instrumenter, &block)
167
+ ensure
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
172
+
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
+ })
183
+ end
164
184
  else
165
185
  { ok: true, result: acq_process[:lock_info] }
166
186
  end
@@ -181,16 +201,28 @@ module RedisQueuedLocks::Acquier
181
201
  #
182
202
  # @param redis [RedisClient] Redis connection client.
183
203
  # @param lock_name [String] The lock name that should be released.
204
+ # @param isntrumenter [#notify] See RedisQueuedLocks::Instrument::ActiveSupport for example.
184
205
  # @return [Hash<Symbol,Any>] Format: { ok: true/false, result: Any }
185
206
  #
186
207
  # @api private
187
208
  # @since 0.1.0
188
- def release_lock!(redis, lock_name)
209
+ def release_lock!(redis, lock_name, instrumenter)
189
210
  lock_key = RedisQueuedLocks::Resource.prepare_lock_key(lock_name)
190
211
  lock_key_queue = RedisQueuedLocks::Resource.prepare_lock_queue(lock_name)
191
212
 
192
- # INSTRUMENT: lock release
213
+ rel_start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
193
214
  result = fully_release_lock(redis, lock_key, lock_key_queue)
215
+ time_at = Time.now.to_i
216
+ rel_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
217
+ rel_time = ((rel_end_time - rel_start_time) * 1_000).ceil
218
+
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
+ })
225
+
194
226
  { ok: true, result: result }
195
227
  end
196
228
 
@@ -200,13 +232,25 @@ module RedisQueuedLocks::Acquier
200
232
  #
201
233
  # @param redis [RedisClient] Redis connection client.
202
234
  # @param batch_size [Integer] The number of lock keys that should be released in a time.
235
+ # @param isntrumenter [#notify] See RedisQueuedLocks::Instrument::ActiveSupport for example.
203
236
  # @return [Hash<Symbol,Any>] Format: { ok: true/false, result: Any }
204
237
  #
205
238
  # @api private
206
239
  # @since 0.1.0
207
- def release_all_locks!(redis, batch_size)
208
- # INSTRUMENT: all locks released
240
+ def release_all_locks!(redis, batch_size, instrumenter)
241
+ rel_start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
209
242
  result = fully_release_all_locks(redis, batch_size)
243
+ time_at = Time.now.to_i
244
+ rel_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
245
+ rel_time = ((rel_end_time - rel_start_time) * 1_000).ceil
246
+
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
+ })
253
+
210
254
  { ok: true, result: result }
211
255
  end
212
256
 
@@ -60,9 +60,9 @@ class RedisQueuedLocks::Client
60
60
  # @option ttl [Integer]
61
61
  # Lock's time to live (in milliseconds).
62
62
  # @option queue_ttl [Integer]
63
- # ?
64
- # @option timeout [Integer]
65
- # Time period whe should try to acquire the lock (in seconds).
63
+ # Lifetime of the acuier's lock request. In seconds.
64
+ # @option timeout [Integer,NilClass]
65
+ # Time period whe should try to acquire the lock (in seconds). Nil means "without timeout".
66
66
  # @option retry_count [Integer]
67
67
  # How many times we should try to acquire a lock.
68
68
  # @option retry_delay [Integer]
@@ -147,14 +147,23 @@ class RedisQueuedLocks::Client
147
147
  # @api public
148
148
  # @since 0.1.0
149
149
  def unlock(lock_name)
150
- RedisQueuedLocks::Acquier.release_lock!(redis_client, lock_name)
150
+ RedisQueuedLocks::Acquier.release_lock!(
151
+ redis_client,
152
+ lock_name,
153
+ config[:instrumenter]
154
+ )
151
155
  end
152
156
 
157
+ # @option batch_size [Integer]
153
158
  # @return [Hash<Symbol,Any>] Format: { ok: true/false, result: Symbol/Hash }.
154
159
  #
155
160
  # @api public
156
161
  # @since 0.1.0
157
162
  def clear_locks(batch_size: config[:lock_release_batch_size])
158
- RedisQueuedLocks::Acquier.release_all_locks!(redis_client, batch_size)
163
+ RedisQueuedLocks::Acquier.release_all_locks!(
164
+ redis_client,
165
+ batch_size,
166
+ config[:instrumenter]
167
+ )
159
168
  end
160
169
  end
@@ -4,6 +4,6 @@ module RedisQueuedLocks
4
4
  # @return [String]
5
5
  #
6
6
  # @api public
7
- # @since 0.0.1
8
- VERSION = '0.0.1'
7
+ # @since 0.0.2
8
+ VERSION = '0.0.2'
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.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rustam Ibragimov