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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3270e2a14a4eed87379d84fe0622f4a231a681fb1e48325a1d6e22becafb5a60
4
- data.tar.gz: b67274aeec060e1bef07d64933928c5be3632cc68f88f12cbe01b9105ae41478
3
+ metadata.gz: eb745a97296f4661f7a68d7719d7bdb894521348d6aa25b92b23b4fcffce56cd
4
+ data.tar.gz: e12566b1c653ad97bf973887d4511482c628d8ec27fd265901d1d4bfba9c112b
5
5
  SHA512:
6
- metadata.gz: a40531666d37dc44d738fba4e67e027520a58f029c392a46db8a2e465200d4162ad1fdd44e16b17001edab183b8f9f90621d35f6b026a9c2628abc00ab9ea5b3
7
- data.tar.gz: 40392bb5de7d615e52e4a92b5f193c7c72970d220a9963ef89346364788c2dc1d11ef5664b20d0864dd3b151aba6a1f6bb76b45cdc5c016882dd51883df25281
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 · [![Gem Version](https://badge.fury.io/rb/redis_queued_locks.svg)](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: 50)
92
- config.retry_jitter = 50
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 capabilites (redis, rubyvm);
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); end
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, 50 # NOTE: milliseconds
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
@@ -3,6 +3,12 @@
3
3
  # @api private
4
4
  # @since 0.1.0
5
5
  module RedisQueuedLocks::Resource
6
+ # @return [String]
7
+ #
8
+ # @api private
9
+ # @since 0.1.0
10
+ KEY_PATTERN = 'rql:lock*'
11
+
6
12
  # @return [String]
7
13
  #
8
14
  # @api private
@@ -5,6 +5,6 @@ module RedisQueuedLocks
5
5
  #
6
6
  # @api public
7
7
  # @since 0.0.1
8
- # @version 0.0.15
9
- VERSION = '0.0.15'
8
+ # @version 0.0.17
9
+ VERSION = '0.0.17'
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: 0.0.15
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-28 00:00:00.000000000 Z
11
+ date: 2024-02-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis-client