redis_queued_locks 0.0.15 → 0.0.17
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/CHANGELOG.md +10 -0
- data/README.md +108 -6
- data/lib/redis_queued_locks/acquier/delay.rb +1 -1
- data/lib/redis_queued_locks/acquier/release.rb +2 -0
- data/lib/redis_queued_locks/acquier.rb +60 -1
- data/lib/redis_queued_locks/client.rb +29 -1
- data/lib/redis_queued_locks/resource.rb +6 -0
- data/lib/redis_queued_locks/version.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb745a97296f4661f7a68d7719d7bdb894521348d6aa25b92b23b4fcffce56cd
|
4
|
+
data.tar.gz: e12566b1c653ad97bf973887d4511482c628d8ec27fd265901d1d4bfba9c112b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3e6f4c421c8e6fb9b3933c01ebcd23d5762f0bdb0581333e2d85ab478a8002d8bce360bfabaab29d819c92a4f148e6562d0fa426e5999b0fc2d1c9b59f3eaa7
|
7
|
+
data.tar.gz: 42a5800757191ea80797c7462b765c5dd4187771ce73c6c9af70f6653dea5599710d8569b5a2fc1a0c30cb0b3483abfac557bd888a7f4bcbde4ac4a7de308021
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.0.17] - 2024-02-29
|
4
|
+
### Added
|
5
|
+
- `RedisQueuedLocks::Client#locks` - list of obtained locks;
|
6
|
+
- `RedisQueuedLocks::Client#queues` - list of existing lock request queus;
|
7
|
+
- `RedisQueuedLocks::Client#keys` - get list of taken locks and queues;
|
8
|
+
|
9
|
+
## [0.0.16] - 2024-02-29
|
10
|
+
### Fixed
|
11
|
+
- Execution delay formula returns the value "in seconds" (should be "in milliseconds");
|
12
|
+
|
3
13
|
## [0.0.15] - 2024-02-28
|
4
14
|
### Added
|
5
15
|
- An ability to fail fast if the required lock is already obtained;
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# RedisQueuedLocks
|
1
|
+
# RedisQueuedLocks · [](https://badge.fury.io/rb/redis_queued_locks)
|
2
2
|
|
3
3
|
Distributed locks with "lock acquisition queue" capabilities based on the Redis Database.
|
4
4
|
|
@@ -21,6 +21,10 @@ Each lock request is put into the request queue and processed in order of their
|
|
21
21
|
- [queued?](#queued)
|
22
22
|
- [unlock](#unlock---release-a-lock)
|
23
23
|
- [clear_locks](#clear_locks---release-all-locks-and-lock-queues)
|
24
|
+
- [extend_lock_ttl](#extend_lock_ttl)
|
25
|
+
- [locks](#locks---get-list-of-obtained-locks)
|
26
|
+
- [queues](#queues---get-list-of-lock-request-queues)
|
27
|
+
- [keys](#keys---get-list-of-taken-locks-and-queues)
|
24
28
|
- [Instrumentation](#instrumentation)
|
25
29
|
- [Instrumentation Events](#instrumentation-events)
|
26
30
|
- [Roadmap](#roadmap)
|
@@ -32,7 +36,7 @@ Each lock request is put into the request queue and processed in order of their
|
|
32
36
|
|
33
37
|
### Algorithm
|
34
38
|
|
35
|
-
- soon
|
39
|
+
- soon
|
36
40
|
|
37
41
|
---
|
38
42
|
|
@@ -88,8 +92,8 @@ clinet = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
88
92
|
# (milliseconds) (default: 200)
|
89
93
|
config.retry_delay = 200
|
90
94
|
|
91
|
-
# (milliseconds) (default:
|
92
|
-
config.retry_jitter =
|
95
|
+
# (milliseconds) (default: 25)
|
96
|
+
config.retry_jitter = 25
|
93
97
|
|
94
98
|
# (seconds) (supports nil)
|
95
99
|
# - nil means "no timeout" and you are only limited by "retry_count" config;
|
@@ -104,10 +108,15 @@ clinet = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
104
108
|
config.default_queue_ttl = 15
|
105
109
|
|
106
110
|
# (default: 100)
|
107
|
-
# - how many items will be released at a time in RedisQueuedLocks::Client#clear_locks logic;
|
108
|
-
# - affects the performancs
|
111
|
+
# - how many items will be released at a time in RedisQueuedLocks::Client#clear_locks logic (uses SCAN);
|
112
|
+
# - affects the performancs of your Redis and Ruby Application (configure thoughtfully);
|
109
113
|
config.lock_release_batch_size = 100
|
110
114
|
|
115
|
+
# (default: 500)
|
116
|
+
# - how many items should be extracted from redis during the #locks, #queues and #keys operations (uses SCAN);
|
117
|
+
# - affects the performance of your Redis and Ruby Application (configure thoughtfully;)
|
118
|
+
config.key_extraction_batch_size = 500
|
119
|
+
|
111
120
|
# (default: RedisQueuedLocks::Instrument::VoidNotifier)
|
112
121
|
# - instrumentation layer;
|
113
122
|
# - you can provde your own instrumenter with `#notify(event, payload = {})`
|
@@ -138,6 +147,10 @@ end
|
|
138
147
|
- [queued?](#queued)
|
139
148
|
- [unlock](#unlock---release-a-lock)
|
140
149
|
- [clear_locks](#clear_locks---release-all-locks-and-lock-queues)
|
150
|
+
- [extend_lock_ttl](#extend_lock_ttl)
|
151
|
+
- [locks](#locks---get-list-of-obtained-locks)
|
152
|
+
- [queues](#queues---get-list-of-lock-request-queues)
|
153
|
+
- [keys](#keys---get-list-of-taken-locks-and-queues)
|
141
154
|
|
142
155
|
---
|
143
156
|
|
@@ -393,6 +406,94 @@ Return:
|
|
393
406
|
|
394
407
|
---
|
395
408
|
|
409
|
+
#### #extend_lock_ttl
|
410
|
+
|
411
|
+
- soon
|
412
|
+
|
413
|
+
---
|
414
|
+
|
415
|
+
#### #locks - get list of obtained locks
|
416
|
+
|
417
|
+
- uses redis `SCAN` under the hood;
|
418
|
+
- accepts `scan_size:`/`Integer` option (`config[:key_extraction_batch_size]` by default);
|
419
|
+
- returns `Set<String>`
|
420
|
+
|
421
|
+
```ruby
|
422
|
+
rql.locks # or rql.locks(scan_size: 123)
|
423
|
+
|
424
|
+
=>
|
425
|
+
#<Set:
|
426
|
+
{"rql:lock:locklock75",
|
427
|
+
"rql:lock:locklock9",
|
428
|
+
"rql:lock:locklock108",
|
429
|
+
"rql:lock:locklock7",
|
430
|
+
"rql:lock:locklock48",
|
431
|
+
"rql:lock:locklock104",
|
432
|
+
"rql:lock:locklock13",
|
433
|
+
"rql:lock:locklock62",
|
434
|
+
"rql:lock:locklock80",
|
435
|
+
"rql:lock:locklock28",
|
436
|
+
...}>
|
437
|
+
```
|
438
|
+
|
439
|
+
---
|
440
|
+
|
441
|
+
#### #queues - get list of lock request queues
|
442
|
+
|
443
|
+
- uses redis `SCAN` under the hood;
|
444
|
+
- accepts `scan_size:`/`Integer` option (`config[:key_extraction_batch_size]` by default);
|
445
|
+
- returns `Set<String>`
|
446
|
+
|
447
|
+
```ruby
|
448
|
+
rql.queues # or rql.queues(scan_size: 123)
|
449
|
+
|
450
|
+
=>
|
451
|
+
#<Set:
|
452
|
+
{"rql:lock_queue:locklock75",
|
453
|
+
"rql:lock_queue:locklock9",
|
454
|
+
"rql:lock_queue:locklock108",
|
455
|
+
"rql:lock_queue:locklock7",
|
456
|
+
"rql:lock_queue:locklock48",
|
457
|
+
"rql:lock_queue:locklock104",
|
458
|
+
"rql:lock_queue:locklock13",
|
459
|
+
"rql:lock_queue:locklock62",
|
460
|
+
"rql:lock_queue:locklock80",
|
461
|
+
"rql:lock_queue:locklock28",
|
462
|
+
...}>
|
463
|
+
```
|
464
|
+
|
465
|
+
---
|
466
|
+
|
467
|
+
#### #keys - get list of taken locks and queues
|
468
|
+
|
469
|
+
- uses redis `SCAN` under the hood;
|
470
|
+
- accepts `scan_size:`/`Integer` option (`config[:key_extraction_batch_size]` by default);
|
471
|
+
- returns `Set<String>`
|
472
|
+
|
473
|
+
```ruby
|
474
|
+
rql.keys # or rql.keys(scan_size: 123)
|
475
|
+
|
476
|
+
=>
|
477
|
+
#<Set:
|
478
|
+
{"rql:lock_queue:locklock75",
|
479
|
+
"rql:lock_queue:locklock9",
|
480
|
+
"rql:lock:locklock9",
|
481
|
+
"rql:lock_queue:locklock108",
|
482
|
+
"rql:lock_queue:locklock7",
|
483
|
+
"rql:lock:locklock7",
|
484
|
+
"rql:lock_queue:locklock48",
|
485
|
+
"rql:lock_queue:locklock104",
|
486
|
+
"rql:lock:locklock104",
|
487
|
+
"rql:lock_queue:locklock13",
|
488
|
+
"rql:lock_queue:locklock62",
|
489
|
+
"rql:lock_queue:locklock80",
|
490
|
+
"rql:lock:locklock80",
|
491
|
+
"rql:lock_queue:locklock28",
|
492
|
+
...}>
|
493
|
+
```
|
494
|
+
|
495
|
+
---
|
496
|
+
|
396
497
|
## Instrumentation
|
397
498
|
|
398
499
|
An instrumentation layer is incapsulated in `instrumenter` object stored in [config](#configuration) (`RedisQueuedLocks::Client#config[:instrumenter]`).
|
@@ -467,6 +568,7 @@ Detalized event semantics and payload structure:
|
|
467
568
|
whose ttl may expire before the block execution completes);
|
468
569
|
- an ability to add custom metadata to the lock and an ability to read this data;
|
469
570
|
- **Minor**
|
571
|
+
- GitHub Actions CI;
|
470
572
|
- `RedisQueuedLocks::Acquier::Try.try_to_lock` - detailed successful result analization;
|
471
573
|
- better code stylization and interesting refactorings;
|
472
574
|
|
@@ -12,7 +12,7 @@ module RedisQueuedLocks::Acquier::Delay
|
|
12
12
|
# @api private
|
13
13
|
# @since 0.1.0
|
14
14
|
def delay_execution(retry_delay, retry_jitter)
|
15
|
-
delay = (retry_delay + rand(retry_jitter)) / 1_000
|
15
|
+
delay = (retry_delay + rand(retry_jitter)).to_f / 1_000
|
16
16
|
sleep(delay)
|
17
17
|
end
|
18
18
|
end
|
@@ -37,6 +37,7 @@ module RedisQueuedLocks::Acquier::Release
|
|
37
37
|
RedisQueuedLocks::Resource::LOCK_QUEUE_PATTERN,
|
38
38
|
count: batch_size
|
39
39
|
) do |lock_queue|
|
40
|
+
# TODO: reduce unnecessary iterations
|
40
41
|
pipeline.call('ZREMRANGEBYSCORE', lock_queue, '-inf', '+inf')
|
41
42
|
pipeline.call('EXPIRE', RedisQueuedLocks::Resource.lock_key_from_queue(lock_queue), '0')
|
42
43
|
end
|
@@ -47,6 +48,7 @@ module RedisQueuedLocks::Acquier::Release
|
|
47
48
|
RedisQueuedLocks::Resource::LOCK_PATTERN,
|
48
49
|
count: batch_size
|
49
50
|
) do |lock_key|
|
51
|
+
# TODO: reduce unnecessary iterations
|
50
52
|
pipeline.call('EXPIRE', lock_key, '0')
|
51
53
|
end
|
52
54
|
end
|
@@ -437,7 +437,66 @@ module RedisQueuedLocks::Acquier
|
|
437
437
|
#
|
438
438
|
# @api private
|
439
439
|
# @since 0.1.0
|
440
|
-
def extend_lock_ttl(redis_client, lock_name, milliseconds)
|
440
|
+
def extend_lock_ttl(redis_client, lock_name, milliseconds)
|
441
|
+
# TODO: realize
|
442
|
+
end
|
443
|
+
|
444
|
+
# @param redis_client [RedisClient]
|
445
|
+
# @option scan_size [Integer]
|
446
|
+
# @return [Set<String>]
|
447
|
+
#
|
448
|
+
# @api private
|
449
|
+
# @since 0.1.0
|
450
|
+
def locks(redis_client, scan_size:)
|
451
|
+
Set.new.tap do |lock_keys|
|
452
|
+
redis_client.scan(
|
453
|
+
'MATCH',
|
454
|
+
RedisQueuedLocks::Resource::LOCK_PATTERN,
|
455
|
+
count: scan_size
|
456
|
+
) do |lock_key|
|
457
|
+
# TODO: reduce unnecessary iterations
|
458
|
+
lock_keys.add(lock_key)
|
459
|
+
end
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
# @param redis_client [RedisClient]
|
464
|
+
# @param scan_size [Integer]
|
465
|
+
# @return [Set<String>]
|
466
|
+
#
|
467
|
+
# @api private
|
468
|
+
# @since 0.1.0
|
469
|
+
def queues(redis_client, scan_size:)
|
470
|
+
Set.new.tap do |lock_queues|
|
471
|
+
redis_client.scan(
|
472
|
+
'MATCH',
|
473
|
+
RedisQueuedLocks::Resource::LOCK_QUEUE_PATTERN,
|
474
|
+
count: scan_size
|
475
|
+
) do |lock_queue|
|
476
|
+
# TODO: reduce unnecessary iterations
|
477
|
+
lock_queues.add(lock_queue)
|
478
|
+
end
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
# @param redis_client [RedisClient]
|
483
|
+
# @option scan_size [Integer]
|
484
|
+
# @return [Array<String>]
|
485
|
+
#
|
486
|
+
# @api private
|
487
|
+
# @since 0.1.0
|
488
|
+
def keys(redis_client, scan_size:)
|
489
|
+
Set.new.tap do |keys|
|
490
|
+
redis_client.scan(
|
491
|
+
'MATCH',
|
492
|
+
RedisQueuedLocks::Resource::KEY_PATTERN,
|
493
|
+
count: scan_size
|
494
|
+
) do |key|
|
495
|
+
# TODO: reduce unnecessary iterations
|
496
|
+
keys.add(key)
|
497
|
+
end
|
498
|
+
end
|
499
|
+
end
|
441
500
|
|
442
501
|
private
|
443
502
|
|
@@ -10,11 +10,12 @@ class RedisQueuedLocks::Client
|
|
10
10
|
configuration do
|
11
11
|
setting :retry_count, 3
|
12
12
|
setting :retry_delay, 200 # NOTE: milliseconds
|
13
|
-
setting :retry_jitter,
|
13
|
+
setting :retry_jitter, 25 # NOTE: milliseconds
|
14
14
|
setting :try_to_lock_timeout, 10 # NOTE: seconds
|
15
15
|
setting :default_lock_ttl, 5_000 # NOTE: milliseconds
|
16
16
|
setting :default_queue_ttl, 15 # NOTE: seconds
|
17
17
|
setting :lock_release_batch_size, 100
|
18
|
+
setting :key_extraction_batch_size, 500
|
18
19
|
setting :instrumenter, RedisQueuedLocks::Instrument::VoidNotifier
|
19
20
|
setting :uniq_identifier, -> { RedisQueuedLocks::Resource.calc_uniq_identity }
|
20
21
|
|
@@ -225,5 +226,32 @@ class RedisQueuedLocks::Client
|
|
225
226
|
def clear_locks(batch_size: config[:lock_release_batch_size])
|
226
227
|
RedisQueuedLocks::Acquier.release_all_locks!(redis_client, batch_size, config[:instrumenter])
|
227
228
|
end
|
229
|
+
|
230
|
+
# @option scan_size [Integer]
|
231
|
+
# @return [Set<String>]
|
232
|
+
#
|
233
|
+
# @api public
|
234
|
+
# @since 0.1.0
|
235
|
+
def locks(scan_size: config[:key_extraction_batch_size])
|
236
|
+
RedisQueuedLocks::Acquier.locks(redis_client, scan_size:)
|
237
|
+
end
|
238
|
+
|
239
|
+
# @option scan_size [Integer]
|
240
|
+
# @return [Set<String>]
|
241
|
+
#
|
242
|
+
# @api public
|
243
|
+
# @since 0.1.0
|
244
|
+
def queues(scan_size: config[:key_extraction_batch_size])
|
245
|
+
RedisQueuedLocks::Acquier.queues(redis_client, scan_size:)
|
246
|
+
end
|
247
|
+
|
248
|
+
# @option scan_size [Integer]
|
249
|
+
# @return [Set<String>]
|
250
|
+
#
|
251
|
+
# @api public
|
252
|
+
# @since 0.1.0
|
253
|
+
def keys(scan_size: config[:key_extraction_batch_size])
|
254
|
+
RedisQueuedLocks::Acquier.keys(redis_client, scan_size:)
|
255
|
+
end
|
228
256
|
end
|
229
257
|
# 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.17
|
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-02-
|
11
|
+
date: 2024-02-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis-client
|