redis_queued_locks 0.0.38 → 0.0.40
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 +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +32 -0
- data/README.md +393 -78
- data/Rakefile +14 -5
- data/lib/redis_queued_locks/acquier/acquire_lock/try_to_lock.rb +35 -73
- data/lib/redis_queued_locks/acquier/acquire_lock/yield_with_expire.rb +2 -2
- data/lib/redis_queued_locks/acquier/acquire_lock.rb +25 -15
- data/lib/redis_queued_locks/acquier/clear_dead_requests.rb +52 -0
- data/lib/redis_queued_locks/acquier/extend_lock_ttl.rb +21 -2
- data/lib/redis_queued_locks/acquier/lock_info.rb +3 -3
- data/lib/redis_queued_locks/acquier/locks.rb +7 -5
- data/lib/redis_queued_locks/acquier/queues.rb +4 -2
- data/lib/redis_queued_locks/acquier/release_all_locks.rb +14 -13
- data/lib/redis_queued_locks/acquier/release_lock.rb +25 -6
- data/lib/redis_queued_locks/acquier.rb +1 -0
- data/lib/redis_queued_locks/client.rb +96 -17
- data/lib/redis_queued_locks/data.rb +0 -1
- data/lib/redis_queued_locks/version.rb +2 -2
- metadata +4 -3
@@ -23,8 +23,8 @@ module RedisQueuedLocks::Acquier::ReleaseLock
|
|
23
23
|
# @param logger [::Logger,#debug]
|
24
24
|
# - Logger object used from `configuration` layer (see config[:logger]);
|
25
25
|
# - See RedisQueuedLocks::Logging::VoidLogger for example;
|
26
|
-
# @return [RedisQueuedLocks::Data,Hash<Symbol,
|
27
|
-
# Format: { ok: true/false, result: Hash<
|
26
|
+
# @return [RedisQueuedLocks::Data,Hash<Symbol,Boolean<Hash<Symbol,Numeric|String|Symbol>>]
|
27
|
+
# Format: { ok: true/false, result: Hash<Symbol,Numeric|String|Symbol> }
|
28
28
|
#
|
29
29
|
# @api private
|
30
30
|
# @since 0.1.0
|
@@ -34,7 +34,7 @@ module RedisQueuedLocks::Acquier::ReleaseLock
|
|
34
34
|
|
35
35
|
rel_start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
36
36
|
fully_release_lock(redis, lock_key, lock_key_queue) => { ok:, result: }
|
37
|
-
time_at = Time.now.
|
37
|
+
time_at = Time.now.to_f
|
38
38
|
rel_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
39
39
|
rel_time = ((rel_end_time - rel_start_time) * 1_000).ceil(2)
|
40
40
|
|
@@ -49,7 +49,13 @@ module RedisQueuedLocks::Acquier::ReleaseLock
|
|
49
49
|
|
50
50
|
RedisQueuedLocks::Data[
|
51
51
|
ok: true,
|
52
|
-
result: {
|
52
|
+
result: {
|
53
|
+
rel_time: rel_time,
|
54
|
+
rel_key: lock_key,
|
55
|
+
rel_queue: lock_key_queue,
|
56
|
+
queue_res: result[:queue],
|
57
|
+
lock_res: result[:lock]
|
58
|
+
}
|
53
59
|
]
|
54
60
|
end
|
55
61
|
|
@@ -60,7 +66,14 @@ module RedisQueuedLocks::Acquier::ReleaseLock
|
|
60
66
|
# @param redis [RedisClient]
|
61
67
|
# @param lock_key [String]
|
62
68
|
# @param lock_key_queue [String]
|
63
|
-
# @return [RedisQueuedLocks::Data,Hash<Symbol,
|
69
|
+
# @return [RedisQueuedLocks::Data,Hash<Symbol,Boolean|Hash<Symbol,Symbol>>]
|
70
|
+
# Format: {
|
71
|
+
# ok: true/false,
|
72
|
+
# result: {
|
73
|
+
# queue: :released/:nothing_to_release,
|
74
|
+
# lock: :released/:nothing_to_release
|
75
|
+
# }
|
76
|
+
# }
|
64
77
|
#
|
65
78
|
# @api private
|
66
79
|
# @since 0.1.0
|
@@ -72,7 +85,13 @@ module RedisQueuedLocks::Acquier::ReleaseLock
|
|
72
85
|
end
|
73
86
|
end
|
74
87
|
|
75
|
-
RedisQueuedLocks::Data[
|
88
|
+
RedisQueuedLocks::Data[
|
89
|
+
ok: true,
|
90
|
+
result: {
|
91
|
+
queue: (result[0] != 0) ? :released : :nothing_to_release,
|
92
|
+
lock: (result[1] != 0) ? :released : :nothing_to_release
|
93
|
+
}
|
94
|
+
]
|
76
95
|
end
|
77
96
|
end
|
78
97
|
end
|
@@ -20,6 +20,8 @@ class RedisQueuedLocks::Client
|
|
20
20
|
setting :uniq_identifier, -> { RedisQueuedLocks::Resource.calc_uniq_identity }
|
21
21
|
setting :logger, RedisQueuedLocks::Logging::VoidLogger
|
22
22
|
setting :log_lock_try, false
|
23
|
+
setting :dead_request_ttl, (1 * 24 * 60 * 60 * 1000) # NOTE: 1 day in milliseconds
|
24
|
+
setting :is_timed_by_default, false
|
23
25
|
|
24
26
|
validate('retry_count') { |val| val == nil || (val.is_a?(::Integer) && val >= 0) }
|
25
27
|
validate('retry_delay') { |val| val.is_a?(::Integer) && val >= 0 }
|
@@ -32,6 +34,8 @@ class RedisQueuedLocks::Client
|
|
32
34
|
validate('uniq_identifier', :proc)
|
33
35
|
validate('logger') { |val| RedisQueuedLocks::Logging.valid_interface?(val) }
|
34
36
|
validate('log_lock_try', :boolean)
|
37
|
+
validate('dead_request_ttl') { |val| val.is_a?(::Integer) && val > 0 }
|
38
|
+
validate('is_timed_by_default', :boolean)
|
35
39
|
end
|
36
40
|
|
37
41
|
# @return [RedisClient]
|
@@ -40,15 +44,14 @@ class RedisQueuedLocks::Client
|
|
40
44
|
# @since 0.1.0
|
41
45
|
attr_reader :redis_client
|
42
46
|
|
47
|
+
# NOTE: attr_access here is chosen intentionally in order to have an ability to change
|
48
|
+
# uniq_identity value for debug purposes in runtime;
|
43
49
|
# @return [String]
|
44
50
|
#
|
45
51
|
# @api private
|
46
52
|
# @since 0.1.0
|
47
53
|
attr_accessor :uniq_identity
|
48
54
|
|
49
|
-
# NOTE: attr_access is chosen intentionally in order to have an ability to change
|
50
|
-
# uniq_identity values for debug purposes in runtime;
|
51
|
-
|
52
55
|
# @param redis_client [RedisClient]
|
53
56
|
# Redis connection manager, which will be used for the lock acquierment and distribution.
|
54
57
|
# It should be an instance of RedisClient.
|
@@ -119,7 +122,7 @@ class RedisQueuedLocks::Client
|
|
119
122
|
ttl: config[:default_lock_ttl],
|
120
123
|
queue_ttl: config[:default_queue_ttl],
|
121
124
|
timeout: config[:try_to_lock_timeout],
|
122
|
-
timed:
|
125
|
+
timed: config[:is_timed_by_default],
|
123
126
|
retry_count: config[:retry_count],
|
124
127
|
retry_delay: config[:retry_delay],
|
125
128
|
retry_jitter: config[:retry_jitter],
|
@@ -167,7 +170,7 @@ class RedisQueuedLocks::Client
|
|
167
170
|
ttl: config[:default_lock_ttl],
|
168
171
|
queue_ttl: config[:default_queue_ttl],
|
169
172
|
timeout: config[:try_to_lock_timeout],
|
170
|
-
timed:
|
173
|
+
timed: config[:is_timed_by_default],
|
171
174
|
retry_count: config[:retry_count],
|
172
175
|
retry_delay: config[:retry_delay],
|
173
176
|
retry_jitter: config[:retry_jitter],
|
@@ -191,20 +194,38 @@ class RedisQueuedLocks::Client
|
|
191
194
|
raise_errors: true,
|
192
195
|
identity:,
|
193
196
|
fail_fast:,
|
197
|
+
logger:,
|
198
|
+
log_lock_try:,
|
194
199
|
meta:,
|
195
200
|
instrument:,
|
196
201
|
&block
|
197
202
|
)
|
198
203
|
end
|
199
204
|
|
200
|
-
# @param lock_name [String]
|
201
|
-
#
|
205
|
+
# @param lock_name [String] The lock name that should be released.
|
206
|
+
# @option logger [::Logger,#debug]
|
207
|
+
# @option instrumenter [#notify]
|
208
|
+
# @option instrument [NilClass,Any]
|
202
209
|
# @return [RedisQueuedLocks::Data, Hash<Symbol,Any>]
|
203
|
-
# Format: {
|
210
|
+
# Format: {
|
211
|
+
# ok: true/false,
|
212
|
+
# result: {
|
213
|
+
# rel_time: Integer, # <millisecnds>
|
214
|
+
# rel_key: String, # lock key
|
215
|
+
# rel_queue: String, # lock queue
|
216
|
+
# queue_res: Symbol, # :released or :nothing_to_release
|
217
|
+
# lock_res: Symbol # :released or :nothing_to_release
|
218
|
+
# }
|
219
|
+
# }
|
204
220
|
#
|
205
221
|
# @api public
|
206
222
|
# @since 0.1.0
|
207
|
-
def unlock(
|
223
|
+
def unlock(
|
224
|
+
lock_name,
|
225
|
+
logger: config[:logger],
|
226
|
+
instrumenter: config[:instrumenter],
|
227
|
+
instrument: nil
|
228
|
+
)
|
208
229
|
RedisQueuedLocks::Acquier::ReleaseLock.release_lock(
|
209
230
|
redis_client,
|
210
231
|
lock_name,
|
@@ -249,33 +270,60 @@ class RedisQueuedLocks::Client
|
|
249
270
|
RedisQueuedLocks::Acquier::QueueInfo.queue_info(redis_client, lock_name)
|
250
271
|
end
|
251
272
|
|
273
|
+
# This method is non-atomic cuz redis does not provide an atomic function for TTL/PTTL extension.
|
274
|
+
# So the methid is spliited into the two commands:
|
275
|
+
# (1) read current pttl
|
276
|
+
# (2) set new ttl that is calculated as "current pttl + additional milliseconds"
|
277
|
+
# What can happen during these steps
|
278
|
+
# - lock is expired between commands or before the first command;
|
279
|
+
# - lock is expired before the second command;
|
280
|
+
# - lock is expired AND newly acquired by another process (so you will extend the
|
281
|
+
# totally new lock with fresh PTTL);
|
282
|
+
# Use it at your own risk and consider async nature when calling this method.
|
283
|
+
#
|
252
284
|
# @param lock_name [String]
|
253
285
|
# @param milliseconds [Integer] How many milliseconds should be added.
|
254
|
-
# @return [
|
286
|
+
# @return [Hash<Symbol,Boolean|Symbol>]
|
287
|
+
# - { ok: true, result: :ttl_extended }
|
288
|
+
# - { ok: false, result: :async_expire_or_no_lock }
|
255
289
|
#
|
256
290
|
# @api public
|
257
291
|
# @since 0.1.0
|
258
|
-
def extend_lock_ttl(lock_name, milliseconds)
|
292
|
+
def extend_lock_ttl(lock_name, milliseconds, logger: config[:logger])
|
259
293
|
RedisQueuedLocks::Acquier::ExtendLockTTL.extend_lock_ttl(
|
260
294
|
redis_client,
|
261
295
|
lock_name,
|
262
296
|
milliseconds,
|
263
|
-
|
297
|
+
logger
|
264
298
|
)
|
265
299
|
end
|
266
300
|
|
301
|
+
# Releases all queues and locks.
|
302
|
+
# Returns:
|
303
|
+
# - :rel_time - (milliseconds) - time spent to release all locks and queues;
|
304
|
+
# - :rel_key_cnt - (integer) - the number of released redis keys (queus+locks);
|
305
|
+
#
|
267
306
|
# @option batch_size [Integer]
|
268
|
-
# @
|
269
|
-
#
|
307
|
+
# @option logger [::Logger,#debug]
|
308
|
+
# @option instrumenter [#notify]
|
309
|
+
# @option instrument [NilClass,Any]
|
310
|
+
# @return [RedisQueuedLocks::Data,Hash<Symbol,Boolean|Hash<Symbol,Numeric>>]
|
311
|
+
# Example: { ok: true, result { rel_key_cnt: 100, rel_time: 0.01 } }
|
270
312
|
#
|
271
313
|
# @api public
|
272
314
|
# @since 0.1.0
|
273
|
-
def clear_locks(
|
315
|
+
def clear_locks(
|
316
|
+
batch_size: config[:lock_release_batch_size],
|
317
|
+
logger: config[:logger],
|
318
|
+
instrumenter: config[:instrumenter],
|
319
|
+
instrument: nil
|
320
|
+
)
|
274
321
|
RedisQueuedLocks::Acquier::ReleaseAllLocks.release_all_locks(
|
275
322
|
redis_client,
|
276
323
|
batch_size,
|
277
|
-
|
278
|
-
|
324
|
+
logger,
|
325
|
+
instrumenter,
|
326
|
+
instrument
|
279
327
|
)
|
280
328
|
end
|
281
329
|
|
@@ -352,5 +400,36 @@ class RedisQueuedLocks::Client
|
|
352
400
|
def keys(scan_size: config[:key_extraction_batch_size])
|
353
401
|
RedisQueuedLocks::Acquier::Keys.keys(redis_client, scan_size:)
|
354
402
|
end
|
403
|
+
|
404
|
+
# @option dead_ttl [Integer]
|
405
|
+
# - the time period (in millsiecnds) after whcih the lock request is
|
406
|
+
# considered as dead;
|
407
|
+
# - `config[:dead_request_ttl]` is used by default;
|
408
|
+
# @option scan_size [Integer]
|
409
|
+
# The batch of scanned keys for Redis'es SCAN command.
|
410
|
+
# @option logger [::Logger,#debug]
|
411
|
+
# @option instrumenter [#notify]
|
412
|
+
# @option instrument [NilClass,Any]
|
413
|
+
# @return [Hash<Symbol,Boolean|Hash<Symbol,Set<String>>>]
|
414
|
+
# Format: { ok: true, result: { processed_queus: Set<String> } }
|
415
|
+
#
|
416
|
+
# @api public
|
417
|
+
# @since 0.1.0
|
418
|
+
def clear_dead_requests(
|
419
|
+
dead_ttl: config[:dead_request_ttl],
|
420
|
+
scan_size: config[:key_extraction_batch_size],
|
421
|
+
logger: config[:logger],
|
422
|
+
instrumenter: config[:instrumenter],
|
423
|
+
instrument: nil
|
424
|
+
)
|
425
|
+
RedisQueuedLocks::Acquier::ClearDeadRequests.clear_dead_requests(
|
426
|
+
redis_client,
|
427
|
+
scan_size,
|
428
|
+
dead_ttl,
|
429
|
+
logger,
|
430
|
+
instrumenter,
|
431
|
+
instrument
|
432
|
+
)
|
433
|
+
end
|
355
434
|
end
|
356
435
|
# rubocop:enable Metrics/ClassLength
|
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: 0.0.
|
4
|
+
version: 0.0.40
|
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-03-
|
11
|
+
date: 2024-03-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis-client
|
@@ -61,6 +61,7 @@ files:
|
|
61
61
|
- lib/redis_queued_locks/acquier/acquire_lock/try_to_lock.rb
|
62
62
|
- lib/redis_queued_locks/acquier/acquire_lock/with_acq_timeout.rb
|
63
63
|
- lib/redis_queued_locks/acquier/acquire_lock/yield_with_expire.rb
|
64
|
+
- lib/redis_queued_locks/acquier/clear_dead_requests.rb
|
64
65
|
- lib/redis_queued_locks/acquier/extend_lock_ttl.rb
|
65
66
|
- lib/redis_queued_locks/acquier/is_locked.rb
|
66
67
|
- lib/redis_queued_locks/acquier/is_queued.rb
|
@@ -107,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
107
108
|
- !ruby/object:Gem::Version
|
108
109
|
version: '0'
|
109
110
|
requirements: []
|
110
|
-
rubygems_version: 3.
|
111
|
+
rubygems_version: 3.3.7
|
111
112
|
signing_key:
|
112
113
|
specification_version: 4
|
113
114
|
summary: Queued distributed locks based on Redis.
|