redis_queued_locks 0.0.30 → 0.0.33

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: '0920957ed856cf515d2867ecd6ed7b1911ba53466822f09d5bd98386a5be4296'
4
- data.tar.gz: 587fa64039e85a633fc2c65d5c0b012d7bfb29b9ccd25177e0895deb33850c38
3
+ metadata.gz: 43854a2842911dcf40e95329b2631bc6c76cdaff0cd204fb57eff55c0ee461aa
4
+ data.tar.gz: c361d55a71ac4c71d307fca4a7182069d055e302ec7b65274bcbc1a4ede6f768
5
5
  SHA512:
6
- metadata.gz: 388e0d43c66464672bbe019dc0f26e3bac44be0cc3e5dcdb502ccd9b2d0b82931de2608c6b7219b2c190b9024197a0cbf1263a7cc7d4f8ed6568480ce6120930
7
- data.tar.gz: 56158aca2424c21fc3fcbeebbd33625559aceca82143c2e18700334c6485e919d4005f4391ba3a07b0839e9122f57b9c5d223ebfd4bc38bdd4f61b58bc94f34f
6
+ metadata.gz: 6887f12891fce49f878427b501980695872e304c1429331d23b251d2f66f2db1f07a0994952f0b4ad6d9ec9b81ea769eaefe09ab1bef0c1abcadc9770ef38344
7
+ data.tar.gz: aa2f6d2cfbe72dd6bf5c637f5538ce1df7c8415579505f765b3016a4c243abc52cd1db246f51abba7efa51abef123d577e26bfee992a5484208cb1416ca00f50
data/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.0.33] - 2024-03-26
4
+ ### Added
5
+ - Logging: added current lock data info to the detailed `#try_to_lock` log to the cases when lock is still obtained. It is suitable
6
+ when you pass a custom metadata with lock obtainer (for example: the current string of code) and want to see this information
7
+ in logs when you can not acquire the concrete lock long time;
8
+
9
+ ## [0.0.32] - 2024-03-26
10
+ ### Added
11
+ - Support for custom metadata that merged to the lock data. This data also returned from `RedisQueudLocks::Client#lock_info` method;
12
+ - Custom metadata shou;d be represented as a `key => value` `Hash` data (or `NilClass` instead);
13
+ - Custom metadata values is returned as raw data from Redis (commonly as strings);
14
+ - Custom metadata can not contain reserved lock data keys;
15
+ - Reduced some memory consuption;
16
+ ### Changed
17
+ - `RedisQueuedLocks::Client#lock_info` - has keys is changed from `Symbol` type to `String` type;
18
+ - `RedisQueuedLocks::Client#queue_info` - hash keys is changed from `Symbol` type to `String` type;
19
+
20
+ ## [0.0.31] - 2024-03-25
21
+ ### Changed
22
+ - `:metadata` renamed to `:instrument` in order to reflect it's domain area;
23
+ - `:metadata` is renamed to `:meta` and reserved for the future updates;
24
+
3
25
  ## [0.0.30] - 2024-03-23
4
26
  ### Fixed
5
27
  - Re-enqueue problem: fixed a problem when the expired lock requests were infinitly re-added to the lock queue
data/README.md CHANGED
@@ -2,7 +2,9 @@
2
2
 
3
3
  Distributed locks with "lock acquisition queue" capabilities based on the Redis Database.
4
4
 
5
- Each lock request is put into the request queue and processed in order of their priority (FIFO). Each lock request lives some period of time (RTTL) which guarantees that the request queue will never be stacked.
5
+ Provides flexible invocation flow, parametrized limits (lock request ttl, lock ttls, queue ttls, fast failing, etc), logging and instrumentation.
6
+
7
+ Each lock request is put into the request queue (each lock is hosted by it's own queue separately from other queues) and processed in order of their priority (FIFO). Each lock request lives some period of time (RTTL) which guarantees the request queue will never be stacked.
6
8
 
7
9
  ---
8
10
 
@@ -14,7 +16,7 @@ Each lock request is put into the request queue and processed in order of their
14
16
  - [Configuration](#configuration)
15
17
  - [Usage](#usage)
16
18
  - [lock](#lock---obtain-a-lock)
17
- - [lock!](#lock---exeptional-lock-obtaining)
19
+ - [lock!](#lock---exceptional-lock-obtaining)
18
20
  - [lock_info](#lock_info)
19
21
  - [queue_info](#queue_info)
20
22
  - [locked?](#locked)
@@ -36,7 +38,7 @@ Each lock request is put into the request queue and processed in order of their
36
38
 
37
39
  ### Algorithm
38
40
 
39
- > Each lock request is put into the request queue and processed in order of their priority (FIFO). Each lock request lives some period of time (RTTL) which guarantees that the request queue will never be stacked.
41
+ > Each lock request is put into the request queue (each lock is hosted by it's own queue separately from other queues) and processed in order of their priority (FIFO). Each lock request lives some period of time (RTTL) which guarantees that the request queue will never be stacked.
40
42
 
41
43
  **Soon**: detailed explanation.
42
44
 
@@ -138,18 +140,27 @@ clinet = RedisQueuedLocks::Client.new(redis_client) do |config|
138
140
  # (default: RedisQueuedLocks::Logging::VoidLogger)
139
141
  # - the logger object;
140
142
  # - should implement `debug(progname = nil, &block)` (minimal requirement) or be an instance of Ruby's `::Logger` class/subclass;
141
- # - at this moment the only debug logs are realised in 3 cases:
142
- # - start of lock obtaining: "[redis_queud_locks.start_lock_obtaining] lock_key => 'rql:lock:your_lock' acq_id => 'rql:acq:54307/3620/3640/3540/c1799ede93'"
143
- # - finish of the lock obtaining: "[redis_queued_locks.lock_obtained] lock_key => 'rql:lock:your_lock' acq_id => 'rql:acq:54307/3620/3640/3540/c1799ede93' acq_time => 123.456 (ms)"
144
- # - start of the lock expiration after `yield`: "[redis_queud_locks.expire_lock] lock_key => 'rql:lock:your_lock' acq_id => 'rql:acq:54307/3620/3640/3540/c1799ede93'"
143
+ # - at this moment the only debug logs are realised in following cases:
144
+ # - "[redis_queued_locks.start_lock_obtaining]" (logs "lock_key", "queue_ttl", "acq_id");
145
+ # - "[redis_queued_locks.start_try_to_lock_cycle]" (logs "lock_key", "queue_ttl", "acq_id");
146
+ # - "[redis_queued_locks.dead_score_reached__reset_acquier_position]" (logs "lock_key", "queue_ttl", "acq_id");
147
+ # - "[redis_queued_locks.lock_obtained]" (logs "lockkey", "queue_ttl", "acq_id", "acq_time");
145
148
  # - by default uses VoidLogger that does nothing;
146
149
  config.logger = RedisQueuedLocks::Logging::VoidLogger
147
150
 
148
151
  # (default: false)
149
- # - should be logged the each internal try-retry lock acquire (a lot of logs can be generated depending on your retry configurations);
150
- # - it adds 2 cases to the log in addition to the existing 3:
151
- # - start of a try: "[redis_queud_locks.try_lock_start] lock_key => 'rql:lock:your_lock' acq_id => 'rql:acq:54307/3620/3640/3540/c1799ede93'"
152
- # - redis connection is feteched from the pool after "start of a try": "[redis_queued_locks.try_lock_rconn_fetched] lock_key => 'rql:lock:your_lock' acq_id => 'rql:acq:54307/3620/3640/3540/c1799ede93'"
152
+ # - adds additional debug logs;
153
+ # - enables additional logs for each internal try-retry lock acquiring (a lot of logs can be generated depending on your retry configurations);
154
+ # - it adds following logs in addition to the existing:
155
+ # - "[redis_queued_locks.try_lock.start]" (logs "lock_key", "queue_ttl", "acq_id");
156
+ # - "[redis_queued_locks.try_lock.rconn_fetched]" (logs "lock_key", "queue_ttl", "acq_id");
157
+ # - "[redis_queued_locks.try_lock.acq_added_to_queue]" (logs "lock_key", "queue_ttl", "acq_id)";
158
+ # - "[redis_queued_locks.try_lock.remove_expired_acqs]" (logs "lock_key", "queue_ttl", "acq_id");
159
+ # - "[redis_queued_locks.try_lock.get_first_from_queue]" (logs "lock_key", "queue_ttl", "acq_id", "first_acq_id_in_queue");
160
+ # - "[redis_queued_locks.try_lock.exit__queue_ttl_reached]" (logs "lock_key", "queue_ttl", "acq_id");
161
+ # - "[redis_queued_locks.try_lock.exit__no_first]" (logs "lock_key", "queue_ttl", "acq_id", "first_acq_id_in_queue", "<current_lock_data>");
162
+ # - "[redis_queued_locks.try_lock.exit__still_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "first_acq_id_in_queue", "locked_by_acq_id", "<current_lock_data>");
163
+ # - "[redis_queued_locks.try_lock.run__free_to_acquire]" (logs "lock_key", "queue_ttl", "acq_id");
153
164
  # - if logger is not configured this option does not lead to any effect;
154
165
  config.log_lock_try = false
155
166
  end
@@ -194,7 +205,8 @@ def lock(
194
205
  raise_errors: false,
195
206
  fail_fast: false,
196
207
  identity: uniq_identity, # (attr_accessor) calculated during client instantiation via config[:uniq_identifier] proc;
197
- metadata: nil,
208
+ meta: nil,
209
+ instrument: nil,
198
210
  logger: config[:logger],
199
211
  log_lock_try: config[:log_lock_try],
200
212
  &block
@@ -232,8 +244,11 @@ def lock(
232
244
  pods or/and nodes of your application;
233
245
  - It is calculated once during `RedisQueuedLock::Client` instantiation and stored in `@uniq_identity`
234
246
  ivar (accessed via `uniq_dentity` accessor method);
235
- - `metadata` - `[NilClass,Any]`
236
- - A custom metadata wich will be passed to the instrumenter's payload with `:meta` key;
247
+ - `meta` - `[NilClass,Hash<String|Symbol,Any>]`
248
+ - A custom metadata wich will be passed to the lock data in addition to the existing data;
249
+ - Custom metadata can not contain reserved lock data keys (such as `lock_key`, `acq_id`, `ts`, `ini_ttl`, `rem_ttl`);
250
+ - `instrument` - `[NilClass,Any]`
251
+ - Custom instrumentation data wich will be passed to the instrumenter's payload with :instrument key;
237
252
  - `logger` - `[::Logger,#debug]`
238
253
  - Logger object used from the configuration layer (see config[:logger]);
239
254
  - See `RedisQueuedLocks::Logging::VoidLogger` for example;
@@ -293,7 +308,8 @@ def lock!(
293
308
  retry_jitter: config[:retry_jitter],
294
309
  identity: uniq_identity,
295
310
  fail_fast: false,
296
- metadata: nil,
311
+ meta: nil,
312
+ instrument: nil,
297
313
  logger: config[:logger],
298
314
  log_lock_try: config[:log_lock_try],
299
315
  &block
@@ -309,22 +325,42 @@ See `#lock` method [documentation](#lock---obtain-a-lock).
309
325
  - get the lock information;
310
326
  - returns `nil` if lock does not exist;
311
327
  - lock data (`Hash<Symbol,String|Integer>`):
312
- - `lock_key` - `string` - lock key in redis;
313
- - `acq_id` - `string` - acquier identifier (process_id/thread_id/fiber_id/ractor_id/identity);
314
- - `ts` - `integer`/`epoch` - the time lock was obtained;
315
- - `init_ttl` - `integer` - (milliseconds) initial lock key ttl;
316
- - `rem_ttl` - `integer` - (milliseconds) remaining lock key ttl;
328
+ - `"lock_key"` - `string` - lock key in redis;
329
+ - `"acq_id"` - `string` - acquier identifier (process_id/thread_id/fiber_id/ractor_id/identity);
330
+ - `"ts"` - `integer`/`epoch` - the time lock was obtained;
331
+ - `"init_ttl"` - `integer` - (milliseconds) initial lock key ttl;
332
+ - `"rem_ttl"` - `integer` - (milliseconds) remaining lock key ttl;
333
+ - custom metadata keys - `String` - custom metadata passed to the `lock`/`lock!`
334
+ methods via `meta:` keyword argument (see [lock]((#lock---obtain-a-lock)) method documentation);
335
+
336
+ ```ruby
337
+ # without custom metadata
338
+ rql.lock_info("your_lock_name")
339
+
340
+ # =>
341
+ {
342
+ "lock_key" => "rql:lock:your_lock_name",
343
+ "acq_id" => "rql:acq:123/456/567/678/374dd74324",
344
+ "ts" => 123456789,
345
+ "ini_ttl" => 123456789,
346
+ "rem_ttl" => 123456789
347
+ }
348
+ ```
317
349
 
318
350
  ```ruby
351
+ # with custom metadata
352
+ rql.lock("your_lock_name", meta: { "kek" => "pek", "bum" => 123 })
319
353
  rql.lock_info("your_lock_name")
320
354
 
321
355
  # =>
322
356
  {
323
- lock_key: "rql:lock:your_lock_name",
324
- acq_id: "rql:acq:123/456/567/678/374dd74324",
325
- ts: 123456789,
326
- ini_ttl: 123456789,
327
- rem_ttl: 123456789
357
+ "lock_key" => "rql:lock:your_lock_name",
358
+ "acq_id" => "rql:acq:123/456/567/678/374dd74324",
359
+ "ts" => 123456789,
360
+ "ini_ttl" => 123456789,
361
+ "rem_ttl" => 123456789,
362
+ "kek" => "pek",
363
+ "bum" => "123" # NOTE: returned as a raw string directly from Redis
328
364
  }
329
365
  ```
330
366
 
@@ -339,10 +375,10 @@ rql.lock_info("your_lock_name")
339
375
  - represents the acquier identifier and their score as an array of hashes;
340
376
  - returns `nil` if lock queue does not exist;
341
377
  - lock queue data (`Hash<Symbol,String|Array<Hash<Symbol,String|Numeric>>`):
342
- - `lock_queue` - `string` - lock queue key in redis;
343
- - `queue` - `array` - an array of lock requests (array of hashes):
344
- - `acq_id` - `string` - acquier identifier (process_id/thread_id/fiber_id/ractor_id/identity by default);
345
- - `score` - `float`/`epoch` - time when the lock request was made (epoch);
378
+ - `"lock_queue"` - `string` - lock queue key in redis;
379
+ - `"queue"` - `array` - an array of lock requests (array of hashes):
380
+ - `"acq_id"` - `string` - acquier identifier (process_id/thread_id/fiber_id/ractor_id/identity by default);
381
+ - `"score"` - `float`/`epoch` - time when the lock request was made (epoch);
346
382
 
347
383
  ```
348
384
  | Returns an information about the required lock queue by the lock name. The result
@@ -357,11 +393,11 @@ rql.queue_info("your_lock_name")
357
393
 
358
394
  # =>
359
395
  {
360
- lock_queue: "rql:lock_queue:your_lock_name",
361
- queue: [
362
- { acq_id: "rql:acq:123/456/567/678/fa76df9cc2", score: 1},
363
- { acq_id: "rql:acq:123/567/456/679/c7bfcaf4f9", score: 2},
364
- { acq_id: "rql:acq:555/329/523/127/7329553b11", score: 3},
396
+ "lock_queue" => "rql:lock_queue:your_lock_name",
397
+ "queue" => [
398
+ { "acq_id" => "rql:acq:123/456/567/678/fa76df9cc2", "score" => 1},
399
+ { "acq_id" => "rql:acq:123/567/456/679/c7bfcaf4f9", "score" => 2},
400
+ { "acq_id" => "rql:acq:555/329/523/127/7329553b11", "score" => 3},
365
401
  # ...etc
366
402
  ]
367
403
  }
@@ -604,7 +640,8 @@ Detalized event semantics and payload structure:
604
640
  - `100%` test coverage;
605
641
  - per-block-holding-the-lock sidecar `Ractor` and `in progress queue` in RedisDB that will extend
606
642
  the acquired lock for long-running blocks of code (that invoked "under" the lock
607
- whose ttl may expire before the block execution completes);
643
+ whose ttl may expire before the block execution completes). It only makes sens for non-`timed` locks
644
+ (for those locks where otaned with `timed: false` option);
608
645
  - an ability to add custom metadata to the lock and an ability to read this data;
609
646
  - lock prioritization;
610
647
  - support for LIFO strategy;
@@ -613,6 +650,7 @@ Detalized event semantics and payload structure:
613
650
  - `RedisQueuedLocks::Acquier::Try.try_to_lock` - detailed successful result analization;
614
651
  - better code stylization and interesting refactorings;
615
652
  - dead queue cleanup;
653
+ - statistics with UI;
616
654
  - support for `Dragonfly` DB backend;
617
655
  - support for `Garnet` DB backend;
618
656
 
@@ -17,6 +17,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
17
17
  # @param ttl [Integer]
18
18
  # @param queue_ttl [Integer]
19
19
  # @param fail_fast [Boolean]
20
+ # @param meta [NilClass,Hash<String|Symbol,Any>]
20
21
  # @return [Hash<Symbol,Any>] Format: { ok: true/false, result: Symbol|Hash<Symbol,Any> }
21
22
  #
22
23
  # @api private
@@ -32,7 +33,8 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
32
33
  acquier_position,
33
34
  ttl,
34
35
  queue_ttl,
35
- fail_fast
36
+ fail_fast,
37
+ meta
36
38
  )
37
39
  # Step X: intermediate invocation results
38
40
  inter_result = nil
@@ -44,7 +46,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
44
46
  "[redis_queued_locks.try_lock.start] " \
45
47
  "lock_key => '#{lock_key}' " \
46
48
  "queue_ttl => #{queue_ttl} " \
47
- "acq_id => '#{acquier_id}' "
49
+ "acq_id => '#{acquier_id}'"
48
50
  )
49
51
  end
50
52
  end
@@ -161,7 +163,8 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
161
163
  "lock_key => '#{lock_key}' " \
162
164
  "queue_ttl => #{queue_ttl} " \
163
165
  "acq_id => '#{acquier_id}' " \
164
- "first_acq_id_in_queue => '#{waiting_acquier}'"
166
+ "first_acq_id_in_queue => '#{waiting_acquier}' " \
167
+ "<current_lock_data> => <<#{rconn.call('HGETALL', lock_key).to_h}>>"
165
168
  )
166
169
  end
167
170
  end
@@ -197,7 +200,8 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
197
200
  "queue_ttl => #{queue_ttl} " \
198
201
  "acq_id => '#{acquier_id}' " \
199
202
  "first_acq_id_in_queue => '#{waiting_acquier}' " \
200
- "locked_by_acq_id => '#{locked_by_acquier}'"
203
+ "locked_by_acq_id => '#{locked_by_acquier}' " \
204
+ "<current_lock_data> => <<#{rconn.call('HGETALL', lock_key).to_h}>>"
201
205
  )
202
206
  end
203
207
  end
@@ -230,7 +234,8 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
230
234
  lock_key,
231
235
  'acq_id', acquier_id,
232
236
  'ts', (timestamp = Time.now.to_f),
233
- 'ini_ttl', ttl
237
+ 'ini_ttl', ttl,
238
+ *(meta.to_a if meta != nil)
234
239
  )
235
240
 
236
241
  # Step 6.3: set the lock expiration time in order to prevent "infinite locks"
@@ -21,9 +21,11 @@ module RedisQueuedLocks::Acquier::AcquireLock::WithAcqTimeout
21
21
  on_timeout.call unless on_timeout == nil
22
22
 
23
23
  if raise_errors
24
- raise(RedisQueuedLocks::LockAcquiermentTimeoutError, <<~ERROR_MESSAGE.strip)
25
- Failed to acquire the lock "#{lock_key}" for the given timeout (#{timeout} seconds).
26
- ERROR_MESSAGE
24
+ raise(
25
+ RedisQueuedLocks::LockAcquiermentTimeoutError,
26
+ "Failed to acquire the lock \"#{lock_key}\" " \
27
+ "for the given timeout (#{timeout} seconds)."
28
+ )
27
29
  end
28
30
  end
29
31
  end
@@ -63,8 +63,10 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldWithExpire
63
63
  def yield_with_timeout(timeout, lock_key, lock_ttl, &block)
64
64
  ::Timeout.timeout(timeout, &block)
65
65
  rescue ::Timeout::Error
66
- raise(RedisQueuedLocks::TimedLockTimeoutError, <<~ERROR_MESSAGE)
67
- Passed <timed> block of code exceeded the lock TTL (lock: "#{lock_key}", ttl: #{lock_ttl})
68
- ERROR_MESSAGE
66
+ raise(
67
+ RedisQueuedLocks::TimedLockTimeoutError,
68
+ "Passed <timed> block of code exceeded " \
69
+ "the lock TTL (lock: \"#{lock_key}\", ttl: #{lock_ttl})"
70
+ )
69
71
  end
70
72
  end
@@ -67,8 +67,9 @@ module RedisQueuedLocks::Acquier::AcquireLock
67
67
  # @option fail_fast [Boolean]
68
68
  # Should the required lock to be checked before the try and exit immidetly if lock is
69
69
  # already obtained.
70
- # @option metadata [NilClass,Any]
71
- # - A custom metadata wich will be passed to the instrumenter's payload with :meta key;
70
+ # @option meta [NilClass,Hash<String|Symbol,Any>]
71
+ # - A custom metadata wich will be passed to the lock data in addition to the existing data;
72
+ # - Metadata can not contain reserved lock data keys;
72
73
  # @option logger [::Logger,#debug]
73
74
  # - Logger object used from the configuration layer (see config[:logger]);
74
75
  # - See RedisQueuedLocks::Logging::VoidLogger for example;
@@ -76,6 +77,9 @@ module RedisQueuedLocks::Acquier::AcquireLock
76
77
  # - should be logged the each try of lock acquiring (a lot of logs can be generated depending
77
78
  # on your retry configurations);
78
79
  # - see `config[:log_lock_try]`;
80
+ # @option instrument [NilClass,Any]
81
+ # - Custom instrumentation data wich will be passed to the instrumenter's payload
82
+ # with :instrument key;
79
83
  # @param [Block]
80
84
  # A block of code that should be executed after the successfully acquired lock.
81
85
  # @return [RedisQueuedLocks::Data,Hash<Symbol,Any>,yield]
@@ -102,11 +106,38 @@ module RedisQueuedLocks::Acquier::AcquireLock
102
106
  instrumenter:,
103
107
  identity:,
104
108
  fail_fast:,
105
- metadata:,
109
+ meta:,
110
+ instrument:,
106
111
  logger:,
107
112
  log_lock_try:,
108
113
  &block
109
114
  )
115
+ # Step 0: Prevent argument type incompatabilities
116
+ # Step 0.1: prevent :meta incompatabiltiies (type)
117
+ case meta # NOTE: do not ask why case/when is used here
118
+ when Hash, NilClass then nil
119
+ else
120
+ raise(
121
+ RedisQueuedLocks::ArgumentError,
122
+ "`:meta` argument should be a type of NilClass or Hash, got #{meta.class}."
123
+ )
124
+ end
125
+
126
+ # Step 0.2: prevent :meta incompatabiltiies (structure)
127
+ if meta == ::Hash && (meta.keys.any? do |key|
128
+ key == 'acq_id' ||
129
+ key == 'ts' ||
130
+ key == 'ini_ttl' ||
131
+ key == 'lock_key' ||
132
+ key == 'rem_ttl'
133
+ end)
134
+ raise(
135
+ RedisQueuedLocks::ArgumentError,
136
+ '`:meta` keys can not overlap reserved lock data keys' \
137
+ '"acq_id", "ts", "ini_ttl", "lock_key", "rem_ttl"'
138
+ )
139
+ end
140
+
110
141
  # Step 1: prepare lock requirements (generate lock name, calc lock ttl, etc).
111
142
  acquier_id = RedisQueuedLocks::Resource.acquier_identifier(
112
143
  process_id,
@@ -185,7 +216,8 @@ module RedisQueuedLocks::Acquier::AcquireLock
185
216
  acquier_position,
186
217
  lock_ttl,
187
218
  queue_ttl,
188
- fail_fast
219
+ fail_fast,
220
+ meta
189
221
  ) => { ok:, result: }
190
222
 
191
223
  acq_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
@@ -215,7 +247,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
215
247
  acq_id: result[:acq_id],
216
248
  ts: result[:ts],
217
249
  acq_time: acq_time,
218
- meta: metadata
250
+ instrument:
219
251
  })
220
252
  end
221
253
 
@@ -233,9 +265,10 @@ module RedisQueuedLocks::Acquier::AcquireLock
233
265
  elsif fail_fast && acq_process[:result] == :fail_fast_no_try
234
266
  acq_process[:should_try] = false
235
267
  if raise_errors
236
- raise(RedisQueuedLocks::LockAlreadyObtainedError, <<~ERROR_MESSAGE.strip)
237
- Lock "#{lock_key}" is already obtained.
238
- ERROR_MESSAGE
268
+ raise(
269
+ RedisQueuedLocks::LockAlreadyObtainedError,
270
+ "Lock \"#{lock_key}\" is already obtained."
271
+ )
239
272
  end
240
273
  else
241
274
  # Step 2.1.b: failed acquirement => retry
@@ -255,14 +288,16 @@ module RedisQueuedLocks::Acquier::AcquireLock
255
288
 
256
289
  # NOTE: check and raise an error
257
290
  if fail_fast && raise_errors
258
- raise(RedisQueuedLocks::LockAlreadyObtainedError, <<~ERROR_MESSAGE.strip)
259
- Lock "#{lock_key}" is already obtained.
260
- ERROR_MESSAGE
291
+ raise(
292
+ RedisQueuedLocks::LockAlreadyObtainedError,
293
+ "Lock \"#{lock_key}\" is already obtained."
294
+ )
261
295
  elsif raise_errors
262
- raise(RedisQueuedLocks::LockAcquiermentRetryLimitError, <<~ERROR_MESSAGE.strip)
263
- Failed to acquire the lock "#{lock_key}"
264
- for the given retry_count limit (#{retry_count} times).
265
- ERROR_MESSAGE
296
+ raise(
297
+ RedisQueuedLocks::LockAcquiermentRetryLimitError,
298
+ "Failed to acquire the lock \"#{lock_key}\" " \
299
+ "for the given retry_count limit (#{retry_count} times)."
300
+ )
266
301
  end
267
302
  else
268
303
  # NOTE:
@@ -307,7 +342,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
307
342
  ts: acq_process[:lock_info][:ts],
308
343
  lock_key: acq_process[:lock_info][:lock_key],
309
344
  acq_time: acq_process[:acq_time],
310
- meta: metadata
345
+ instrument:
311
346
  })
312
347
  end
313
348
  end
@@ -6,14 +6,14 @@ module RedisQueuedLocks::Acquier::LockInfo
6
6
  class << self
7
7
  # @param redis_client [RedisClient]
8
8
  # @param lock_name [String]
9
- # @return [Hash<Symbol,String|Numeric>,NilClass]
9
+ # @return [Hash<String,String|Numeric>,NilClass]
10
10
  # - `nil` is returned when lock key does not exist or expired;
11
11
  # - result format: {
12
- # lock_key: "rql:lock:your_lockname", # acquired lock key
13
- # acq_id: "rql:acq:process_id/thread_id", # lock acquier identifier
14
- # ts: 123456789.2649841, # <locked at> time stamp (epoch, seconds.microseconds)
15
- # ini_ttl: 123456789, # initial lock key ttl (milliseconds),
16
- # rem_ttl: 123456789, # remaining lock key ttl (milliseconds)
12
+ # 'lock_key' => "rql:lock:your_lockname", # acquired lock key
13
+ # 'acq_id' => "rql:acq:process_id/thread_id", # lock acquier identifier
14
+ # 'ts' => 123456789.2649841, # <locked at> time stamp (epoch, seconds.microseconds)
15
+ # 'ini_ttl' => 123456789, # initial lock key ttl (milliseconds),
16
+ # 'rem_ttl' => 123456789, # remaining lock key ttl (milliseconds)
17
17
  # }
18
18
  #
19
19
  # @api private
@@ -43,13 +43,12 @@ module RedisQueuedLocks::Acquier::LockInfo
43
43
  # NOTE: the result of MULTI-command is an array of results of each internal command
44
44
  # - result[0] (HGETALL) (Hash<String,String>)
45
45
  # - result[1] (PTTL) (Integer)
46
- {
47
- lock_key: lock_key,
48
- acq_id: hget_cmd_res['acq_id'],
49
- ts: Float(hget_cmd_res['ts']),
50
- ini_ttl: Integer(hget_cmd_res['ini_ttl']),
51
- rem_ttl: ((pttl_cmd_res == -1) ? Infinity : pttl_cmd_res)
52
- }
46
+ hget_cmd_res.tap do |lock_data|
47
+ lock_data['lock_key'] = lock_key
48
+ lock_data['ts'] = Float(lock_data['ts'])
49
+ lock_data['ini_ttl'] = Integer(lock_data['ini_ttl'])
50
+ lock_data['rem_ttl'] = ((pttl_cmd_res == -1) ? Infinity : pttl_cmd_res)
51
+ end
53
52
  end
54
53
  end
55
54
  end
@@ -12,13 +12,13 @@ module RedisQueuedLocks::Acquier::QueueInfo
12
12
  #
13
13
  # @param redis_client [RedisClient]
14
14
  # @param lock_name [String]
15
- # @return [Hash<Symbol,String|Array<Hash<Symbol,String|Float>>,NilClass]
15
+ # @return [Hash<String|Array<Hash<String,String|Numeric>>,NilClass]
16
16
  # - `nil` is returned when lock queue does not exist;
17
17
  # - result format: {
18
- # lock_queue: "rql:lock_queue:your_lock_name", # lock queue key in redis,
18
+ # "lock_queue" => "rql:lock_queue:your_lock_name", # lock queue key in redis,
19
19
  # queue: [
20
- # { acq_id: "rql:acq:process_id/thread_id", score: 123 },
21
- # { acq_id: "rql:acq:process_id/thread_id", score: 456 },
20
+ # { "acq_id" => "rql:acq:process_id/thread_id", "score" => 123 },
21
+ # { "acq_id" => "rql:acq:process_id/thread_id", "score" => 456 },
22
22
  # ] # ordered set (by score) with information about an acquier and their position in queue
23
23
  # }
24
24
  #
@@ -38,8 +38,8 @@ module RedisQueuedLocks::Acquier::QueueInfo
38
38
  if exists_cmd_res == 1
39
39
  # NOTE: queue existed during the piepline invocation
40
40
  {
41
- lock_queue: lock_key_queue,
42
- queue: zrange_cmd_res.map { |val| { acq_id: val[0], score: val[1] } }
41
+ 'lock_queue' => lock_key_queue,
42
+ 'queue' => zrange_cmd_res.map { |val| { 'acq_id' => val[0], 'score' => val[1] } }
43
43
  }
44
44
  else
45
45
  # NOTE: queue did not exist during the pipeline invocation
@@ -93,8 +93,9 @@ class RedisQueuedLocks::Client
93
93
  # already obtained;
94
94
  # - Should the logic exit immidietly after the first try if the lock was obtained
95
95
  # by another process while the lock request queue was initially empty;
96
- # @option metadata [NilClass,Any]
97
- # - A custom metadata wich will be passed to the instrumenter's payload with :meta key;
96
+ # @option meta [NilClass,Hash<String|Symbol,Any>]
97
+ # - A custom metadata wich will be passed to the lock data in addition to the existing data;
98
+ # - Metadata can not contain reserved lock data keys;
98
99
  # @option logger [::Logger,#debug]
99
100
  # - Logger object used from the configuration layer (see config[:logger]);
100
101
  # - See `RedisQueuedLocks::Logging::VoidLogger` for example;
@@ -102,6 +103,9 @@ class RedisQueuedLocks::Client
102
103
  # - should be logged the each try of lock acquiring (a lot of logs can
103
104
  # be generated depending on your retry configurations);
104
105
  # - see `config[:log_lock_try]`;
106
+ # @option instrument [NilClass,Any]
107
+ # - Custom instrumentation data wich will be passed to the instrumenter's payload
108
+ # with :instrument key;
105
109
  # @param block [Block]
106
110
  # A block of code that should be executed after the successfully acquired lock.
107
111
  # @return [RedisQueuedLocks::Data,Hash<Symbol,Any>,yield]
@@ -122,9 +126,10 @@ class RedisQueuedLocks::Client
122
126
  raise_errors: false,
123
127
  fail_fast: false,
124
128
  identity: uniq_identity,
125
- metadata: nil,
129
+ meta: nil,
126
130
  logger: config[:logger],
127
131
  log_lock_try: config[:log_lock_try],
132
+ instrument: nil,
128
133
  &block
129
134
  )
130
135
  RedisQueuedLocks::Acquier::AcquireLock.acquire_lock(
@@ -145,9 +150,10 @@ class RedisQueuedLocks::Client
145
150
  instrumenter: config[:instrumenter],
146
151
  identity:,
147
152
  fail_fast:,
148
- metadata:,
153
+ meta:,
149
154
  logger: config[:logger],
150
155
  log_lock_try: config[:log_lock_try],
156
+ instrument:,
151
157
  &block
152
158
  )
153
159
  end
@@ -167,9 +173,10 @@ class RedisQueuedLocks::Client
167
173
  retry_jitter: config[:retry_jitter],
168
174
  fail_fast: false,
169
175
  identity: uniq_identity,
170
- metadata: nil,
176
+ meta: nil,
171
177
  logger: config[:logger],
172
178
  log_lock_try: config[:log_lock_try],
179
+ instrument: nil,
173
180
  &block
174
181
  )
175
182
  lock(
@@ -184,7 +191,8 @@ class RedisQueuedLocks::Client
184
191
  raise_errors: true,
185
192
  identity:,
186
193
  fail_fast:,
187
- metadata:,
194
+ meta:,
195
+ instrument:,
188
196
  &block
189
197
  )
190
198
  end
@@ -224,7 +232,7 @@ class RedisQueuedLocks::Client
224
232
  end
225
233
 
226
234
  # @param lock_name [String]
227
- # @return [Hash,NilClass]
235
+ # @return [Hash<String,String|Numeric>,NilClass]
228
236
  #
229
237
  # @api public
230
238
  # @since 0.1.0
@@ -233,7 +241,7 @@ class RedisQueuedLocks::Client
233
241
  end
234
242
 
235
243
  # @param lock_name [String]
236
- # @return [Hash,NilClass]
244
+ # @return [Hash<String|Array<Hash<String,String|Numeric>>,NilClass]
237
245
  #
238
246
  # @api public
239
247
  # @since 0.1.0
@@ -5,6 +5,6 @@ module RedisQueuedLocks
5
5
  #
6
6
  # @api public
7
7
  # @since 0.0.1
8
- # @version 0.0.30
9
- VERSION = '0.0.30'
8
+ # @version 0.0.33
9
+ VERSION = '0.0.33'
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.30
4
+ version: 0.0.33
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-23 00:00:00.000000000 Z
11
+ date: 2024-03-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis-client
@@ -107,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
107
  - !ruby/object:Gem::Version
108
108
  version: '0'
109
109
  requirements: []
110
- rubygems_version: 3.3.7
110
+ rubygems_version: 3.5.1
111
111
  signing_key:
112
112
  specification_version: 4
113
113
  summary: Queued distributed locks based on Redis.