redis_queued_locks 0.0.15 → 0.0.17

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: 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