redis_queued_locks 0.0.21 → 0.0.23

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: 2ea008491f8c631aea2677c6571a206db919c0ca5d599b58adc37fa82c372145
4
- data.tar.gz: 41e5bece44a3b1af1031d28a4d7bfe480641151573b97c89d62841f296cbc3ca
3
+ metadata.gz: 05a8fc14cae41bf9870bedf4eae9fa298c3704b57d25df428c2e2c8bd9bf0223
4
+ data.tar.gz: 14a941048edc7679df1d80805a96d3db4019d45acfc80ac5444d99e12cc3eadc
5
5
  SHA512:
6
- metadata.gz: cf96a4535a3fa8f6c7a62797805263e7957f198997213df65c164b9261d5199526f7dbc6e2a3f5e13a912e79cc7cd718ed193b0f197586cd03f1f3f95d958fca
7
- data.tar.gz: 46726b0d18cd73d108b57b34872f1369ab63771a7d6c899aee021ab628ced537e6e0a7e255aecee259b902d682f5d13a12519dcc7033b786ef832c035a61f12a
6
+ metadata.gz: 292f4ed74242a1a53b96e290e7759db0b1c918070f0875805bedbe78360fac71b6ac81ec09b63836002b2a17ab247ca06f9cf7ff828a4a07f924b2588e24fd3f
7
+ data.tar.gz: 270749b6a32418e2a57b3995b5ef3b826b6066a97a56efd08f9305f0ed314ed7da13f9f395f891ab619ceea3d4bde53c880278758fbf42fb1e84b98fa5b14690
data/CHANGELOG.md CHANGED
@@ -1,8 +1,17 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.0.23] - 2024-03-21
4
+ ### Changed
5
+ - Composed redis commands are invoked on the one conenction
6
+ (instead of mutiple connection fetchiong from redis connection pool on each redis command);
7
+
8
+ ## [0.0.22] - 2024-03-21
9
+ ### Added
10
+ - Logging infrastructure. Initial implementation includes the only debugging features.
11
+
3
12
  ## [0.0.21] - 2024-03-19
4
13
  ### Changed
5
- - `RedisQueuedLocks::Acquier` refactirngs;
14
+ - Refactored `RedisQueuedLocks::Acquier`;
6
15
 
7
16
  ## [0.0.20] - 2024-03-14
8
17
  ### Added
data/README.md CHANGED
@@ -134,6 +134,16 @@ clinet = RedisQueuedLocks::Client.new(redis_client) do |config|
134
134
  # - it is calculated once per `RedisQueudLocks::Client` instance;
135
135
  # - expects the proc object;
136
136
  config.uniq_identifier = -> { RedisQueuedLocks::Resource.calc_uniq_identity }
137
+
138
+ # (default: RedisQueuedLocks::Logging::VoidLogger)
139
+ # - the logger object;
140
+ # - 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'"
143
+ # - finish of the lock obtaining: "[redis_queued_locks.lock_obtained] lock_key => 'rql:lock:your_lock' acq_time => 123.456 (ms)"
144
+ # - start of the lock expiration after `yield`: "[redis_queud_locks.expire_lock] lock_key => 'rql:lock:your_lock'"
145
+ # - by default uses VoidLogger that does nothing;
146
+ config.logger = RedisQueuedLocks::Logging::VoidLogger
137
147
  end
138
148
  ```
139
149
 
@@ -581,6 +591,7 @@ Detalized event semantics and payload structure:
581
591
  - an ability to add custom metadata to the lock and an ability to read this data;
582
592
  - lock prioritization;
583
593
  - support for LIFO strategy;
594
+ - structured logging;
584
595
  - **Minor**
585
596
  - GitHub Actions CI;
586
597
  - `RedisQueuedLocks::Acquier::Try.try_to_lock` - detailed successful result analization;
@@ -31,95 +31,101 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
31
31
  timestamp = nil
32
32
 
33
33
  # Step 0: watch the lock key changes (and discard acquirement if lock is already acquired)
34
- result = redis.multi(watch: [lock_key]) do |transact|
35
- # Fast-Step X0: fail-fast check
36
- if fail_fast && redis.call('HGET', lock_key, 'acq_id')
37
- # Fast-Step X1: is lock already obtained. fail fast - no try.
38
- inter_result = :fail_fast_no_try
39
- else
40
- # Step 1: add an acquier to the lock acquirement queue
41
- res = redis.call('ZADD', lock_key_queue, 'NX', acquier_position, acquier_id)
42
-
43
- RedisQueuedLocks.debug(
44
- "Step №1: добавление в очередь (#{acquier_id}). [ZADD to the queue: #{res}]"
45
- )
46
-
47
- # Step 2.1: drop expired acquiers from the lock queue
48
- res = redis.call(
49
- 'ZREMRANGEBYSCORE',
50
- lock_key_queue,
51
- '-inf',
52
- RedisQueuedLocks::Resource.acquier_dead_score(queue_ttl)
53
- )
54
-
55
- RedisQueuedLocks.debug(
56
- "Step №2: дропаем из очереди просроченных ожидающих. [ZREMRANGE: #{res}]"
57
- )
58
-
59
- # Step 3: get the actual acquier waiting in the queue
60
- waiting_acquier = Array(redis.call('ZRANGE', lock_key_queue, '0', '0')).first
61
-
62
- RedisQueuedLocks.debug(
63
- "Step №3: какой процесс в очереди сейчас ждет. " \
64
- "[ZRANGE <следующий процесс>: #{waiting_acquier} :: <текущий процесс>: #{acquier_id}]"
65
- )
66
-
67
- # Step 4: check the actual acquier: is it ours? are we aready to lock?
68
- unless waiting_acquier == acquier_id
69
- # Step ROLLBACK 1.1: our time hasn't come yet. retry!
34
+ result = redis.with do |rconn|
35
+ rconn.multi(watch: [lock_key]) do |transact|
36
+ # Fast-Step X0: fail-fast check
37
+ if fail_fast && rconn.call('HGET', lock_key, 'acq_id')
38
+ # Fast-Step X1: is lock already obtained. fail fast - no try.
39
+ inter_result = :fail_fast_no_try
40
+ else
41
+ # Step 1: add an acquier to the lock acquirement queue
42
+ res = rconn.call('ZADD', lock_key_queue, 'NX', acquier_position, acquier_id)
70
43
 
71
44
  RedisQueuedLocks.debug(
72
- "Step ROLLBACK №1: не одинаковые ключи. выходим. " \
73
- "[Ждет: #{waiting_acquier}. А нужен: #{acquier_id}]"
45
+ "Step №1: добавление в очередь (#{acquier_id}). [ZADD to the queue: #{res}]"
74
46
  )
75
47
 
76
- inter_result = :acquier_is_not_first_in_queue
77
- else
78
- # NOTE: our time has come! let's try to acquire the lock!
48
+ # Step 2.1: drop expired acquiers from the lock queue
49
+ res = rconn.call(
50
+ 'ZREMRANGEBYSCORE',
51
+ lock_key_queue,
52
+ '-inf',
53
+ RedisQueuedLocks::Resource.acquier_dead_score(queue_ttl)
54
+ )
55
+
56
+ RedisQueuedLocks.debug(
57
+ "Step №2: дропаем из очереди просроченных ожидающих. [ZREMRANGE: #{res}]"
58
+ )
79
59
 
80
- # Step 5: check if the our lock is already acquired
81
- locked_by_acquier = redis.call('HGET', lock_key, 'acq_id')
60
+ # Step 3: get the actual acquier waiting in the queue
61
+ waiting_acquier = Array(rconn.call('ZRANGE', lock_key_queue, '0', '0')).first
82
62
 
83
63
  RedisQueuedLocks.debug(
84
- "Ste5: Ищем требуемый лок. " \
85
- "[HGET<#{lock_key}>: " \
86
- "#{(locked_by_acquier == nil) ? 'не занят' : "занят процессом <#{locked_by_acquier}>"}"
64
+ "Step3: какой процесс в очереди сейчас ждет. " \
65
+ "[ZRANGE <следующий процесс>: #{waiting_acquier} :: <текущий процесс>: #{acquier_id}]"
87
66
  )
88
67
 
89
- if locked_by_acquier
90
- # Step ROLLBACK 2: required lock is stil acquired. retry!
68
+ # Step 4: check the actual acquier: is it ours? are we aready to lock?
69
+ unless waiting_acquier == acquier_id
70
+ # Step ROLLBACK 1.1: our time hasn't come yet. retry!
91
71
 
92
72
  RedisQueuedLocks.debug(
93
- "Step ROLLBACK №2: Ключ уже занят. Ничего не делаем. " \
94
- "[Занят процессом: #{locked_by_acquier}]"
73
+ "Step ROLLBACK №1: не одинаковые ключи. выходим. " \
74
+ "[Ждет: #{waiting_acquier}. А нужен: #{acquier_id}]"
95
75
  )
96
76
 
97
- inter_result = :lock_is_still_acquired
77
+ inter_result = :acquier_is_not_first_in_queue
98
78
  else
99
- # NOTE: required lock is free and ready to be acquired! acquire!
79
+ # NOTE: our time has come! let's try to acquire the lock!
100
80
 
101
- # Step 6.1: remove our acquier from waiting queue
102
- transact.call('ZPOPMIN', lock_key_queue, '1')
81
+ # Step 5: check if the our lock is already acquired
82
+ locked_by_acquier = rconn.call('HGET', lock_key, 'acq_id')
103
83
 
84
+ # rubocop:disable Layout/LineLength
104
85
  RedisQueuedLocks.debug(
105
- 'Step4: Забираем наш текущий процесс из очереди. [ZPOPMIN]'
86
+ "Ste5: Ищем требуемый лок. " \
87
+ "[HGET<#{lock_key}>: " \
88
+ "#{(locked_by_acquier == nil) ? 'не занят' : "занят процессом <#{locked_by_acquier}>"}"
106
89
  )
107
-
108
- RedisQueuedLocks.debug(
109
- "===> <FINAL> Step №6: закрепляем лок за процессом [HSET<#{lock_key}>: #{acquier_id}]"
110
- )
111
-
112
- # Step 6.2: acquire a lock and store an info about the acquier
113
- transact.call(
114
- 'HSET',
115
- lock_key,
116
- 'acq_id', acquier_id,
117
- 'ts', (timestamp = Time.now.to_i),
118
- 'ini_ttl', ttl
119
- )
120
-
121
- # Step 6.3: set the lock expiration time in order to prevent "infinite locks"
122
- transact.call('PEXPIRE', lock_key, ttl) # NOTE: in milliseconds
90
+ # rubocop:enable Layout/LineLength
91
+
92
+ if locked_by_acquier
93
+ # Step ROLLBACK 2: required lock is stil acquired. retry!
94
+
95
+ RedisQueuedLocks.debug(
96
+ "Step ROLLBACK №2: Ключ уже занят. Ничего не делаем. " \
97
+ "[Занят процессом: #{locked_by_acquier}]"
98
+ )
99
+
100
+ inter_result = :lock_is_still_acquired
101
+ else
102
+ # NOTE: required lock is free and ready to be acquired! acquire!
103
+
104
+ # Step 6.1: remove our acquier from waiting queue
105
+ transact.call('ZPOPMIN', lock_key_queue, '1')
106
+
107
+ RedisQueuedLocks.debug(
108
+ 'Step №4: Забираем наш текущий процесс из очереди. [ZPOPMIN]'
109
+ )
110
+
111
+ # rubocop:disable Layout/LineLength
112
+ RedisQueuedLocks.debug(
113
+ "===> <FINAL> Step №6: закрепляем лок за процессом [HSET<#{lock_key}>: #{acquier_id}]"
114
+ )
115
+ # rubocop:enable Layout/LineLength
116
+
117
+ # Step 6.2: acquire a lock and store an info about the acquier
118
+ transact.call(
119
+ 'HSET',
120
+ lock_key,
121
+ 'acq_id', acquier_id,
122
+ 'ts', (timestamp = Time.now.to_i),
123
+ 'ini_ttl', ttl
124
+ )
125
+
126
+ # Step 6.3: set the lock expiration time in order to prevent "infinite locks"
127
+ transact.call('PEXPIRE', lock_key, ttl) # NOTE: in milliseconds
128
+ end
123
129
  end
124
130
  end
125
131
  end
@@ -3,7 +3,11 @@
3
3
  # @api private
4
4
  # @since 0.1.0
5
5
  module RedisQueuedLocks::Acquier::AcquireLock::YieldWithExpire
6
+ # @since 0.1.0
7
+ extend RedisQueuedLocks::Utilities
8
+
6
9
  # @param redis [RedisClient] Redis connection manager.
10
+ # @param logger [#debug] Logger object.
7
11
  # @param lock_key [String] Lock key to be expired.
8
12
  # @param timed [Boolean] Should the lock be wrapped by Tiemlout with with lock's ttl
9
13
  # @param ttl_shift [Float] Lock's TTL shifting. Should affect block's ttl. In millisecodns.
@@ -13,7 +17,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldWithExpire
13
17
  #
14
18
  # @api private
15
19
  # @since 0.1.0
16
- def yield_with_expire(redis, lock_key, timed, ttl_shift, ttl, &block)
20
+ def yield_with_expire(redis, logger, lock_key, timed, ttl_shift, ttl, &block)
17
21
  if block_given?
18
22
  if timed && ttl != nil
19
23
  timeout = ((ttl - ttl_shift) / 1000.0).yield_self { |time| (time < 0) ? 0.0 : time }
@@ -23,6 +27,9 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldWithExpire
23
27
  end
24
28
  end
25
29
  ensure
30
+ run_non_critical do
31
+ logger.debug("[redis_queued_locks.expire_lock] lock_key => '#{lock_key}'")
32
+ end
26
33
  redis.call('EXPIRE', lock_key, '0')
27
34
  end
28
35
 
@@ -69,6 +69,9 @@ module RedisQueuedLocks::Acquier::AcquireLock
69
69
  # already obtained.
70
70
  # @option metadata [NilClass,Any]
71
71
  # - A custom metadata wich will be passed to the instrumenter's payload with :meta key;
72
+ # @option logger [#debug]
73
+ # - Logger object used from `configuration` layer (see config[:logger]);
74
+ # - See RedisQueuedLocks::Logging::VoidLogger for example;
72
75
  # @param [Block]
73
76
  # A block of code that should be executed after the successfully acquired lock.
74
77
  # @return [RedisQueuedLocks::Data,Hash<Symbol,Any>,yield]
@@ -96,6 +99,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
96
99
  identity:,
97
100
  fail_fast:,
98
101
  metadata:,
102
+ logger:,
99
103
  &block
100
104
  )
101
105
  # Step 1: prepare lock requirements (generate lock name, calc lock ttl, etc).
@@ -127,6 +131,13 @@ module RedisQueuedLocks::Acquier::AcquireLock
127
131
  }
128
132
  acq_dequeue = -> { dequeue_from_lock_queue(redis, lock_key_queue, acquier_id) }
129
133
 
134
+ run_non_critical do
135
+ logger.debug(
136
+ "[redis_queued_locks.start_lock_obtaining] " \
137
+ "lock_key => '#{lock_key}'"
138
+ )
139
+ end
140
+
130
141
  # Step 2: try to lock with timeout
131
142
  with_acq_timeout(timeout, lock_key, raise_errors, on_timeout: acq_dequeue) do
132
143
  acq_start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
@@ -153,15 +164,25 @@ module RedisQueuedLocks::Acquier::AcquireLock
153
164
 
154
165
  # Step 2.1: analyze an acquirement attempt
155
166
  if ok
167
+ run_non_critical do
168
+ logger.debug(
169
+ "[redis_queued_locks.lock_obtained] " \
170
+ "lock_key => '#{result[:lock_key]}'" \
171
+ "acq_time => #{acq_time} (ms)"
172
+ )
173
+ end
174
+
156
175
  # Step X (instrumentation): lock obtained
157
- instrumenter.notify('redis_queued_locks.lock_obtained', {
158
- lock_key: result[:lock_key],
159
- ttl: result[:ttl],
160
- acq_id: result[:acq_id],
161
- ts: result[:ts],
162
- acq_time: acq_time,
163
- meta: metadata
164
- })
176
+ run_non_critical do
177
+ instrumenter.notify('redis_queued_locks.lock_obtained', {
178
+ lock_key: result[:lock_key],
179
+ ttl: result[:ttl],
180
+ acq_id: result[:acq_id],
181
+ ts: result[:ts],
182
+ acq_time: acq_time,
183
+ meta: metadata
184
+ })
185
+ end
165
186
 
166
187
  # Step 2.1.a: successfully acquired => build the result
167
188
  acq_process[:lock_info] = {
@@ -225,7 +246,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
225
246
  begin
226
247
  yield_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
227
248
  ttl_shift = ((yield_time - acq_process[:acq_end_time]) * 1000).ceil(2)
228
- yield_with_expire(redis, lock_key, timed, ttl_shift, ttl, &block)
249
+ yield_with_expire(redis, logger, lock_key, timed, ttl_shift, ttl, &block)
229
250
  ensure
230
251
  acq_process[:rel_time] = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
231
252
  acq_process[:hold_time] = (
@@ -7,11 +7,12 @@ module RedisQueuedLocks::Acquier::ExtendLockTTL
7
7
  # @param redis_client [RedisClient]
8
8
  # @param lock_name [String]
9
9
  # @param milliseconds [Integer]
10
+ # @param logger [#debug]
10
11
  # @return [?]
11
12
  #
12
13
  # @api private
13
14
  # @since 0.1.0
14
- def extend_lock_ttl(redis_client, lock_name, milliseconds)
15
+ def extend_lock_ttl(redis_client, lock_name, milliseconds, logger)
15
16
  # TODO: realize
16
17
  end
17
18
  end
@@ -17,12 +17,15 @@ module RedisQueuedLocks::Acquier::ReleaseAllLocks
17
17
  # The number of lock keys that should be released in a time.
18
18
  # @param isntrumenter [#notify]
19
19
  # See RedisQueuedLocks::Instrument::ActiveSupport for example.
20
+ # @param logger [#debug]
21
+ # - Logger object used from `configuration` layer (see config[:logger]);
22
+ # - See RedisQueuedLocks::Logging::VoidLogger for example;
20
23
  # @return [RedisQueuedLocks::Data,Hash<Symbol,Any>]
21
24
  # Format: { ok: true/false, result: Hash<Symbol,Numeric> }
22
25
  #
23
26
  # @api private
24
27
  # @since 0.1.0
25
- def release_all_locks(redis, batch_size, instrumenter)
28
+ def release_all_locks(redis, batch_size, instrumenter, logger)
26
29
  rel_start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
27
30
  fully_release_all_locks(redis, batch_size) => { ok:, result: }
28
31
  time_at = Time.now.to_i
@@ -54,26 +57,28 @@ module RedisQueuedLocks::Acquier::ReleaseAllLocks
54
57
  # @api private
55
58
  # @since 0.1.0
56
59
  def fully_release_all_locks(redis, batch_size)
57
- result = redis.pipelined do |pipeline|
58
- # Step A: release all queus and their related locks
59
- redis.scan(
60
- 'MATCH',
61
- RedisQueuedLocks::Resource::LOCK_QUEUE_PATTERN,
62
- count: batch_size
63
- ) do |lock_queue|
64
- # TODO: reduce unnecessary iterations
65
- pipeline.call('ZREMRANGEBYSCORE', lock_queue, '-inf', '+inf')
66
- pipeline.call('EXPIRE', RedisQueuedLocks::Resource.lock_key_from_queue(lock_queue), '0')
67
- end
60
+ result = redis.with do |rconn|
61
+ rconn.pipelined do |pipeline|
62
+ # Step A: release all queus and their related locks
63
+ rconn.scan(
64
+ 'MATCH',
65
+ RedisQueuedLocks::Resource::LOCK_QUEUE_PATTERN,
66
+ count: batch_size
67
+ ) do |lock_queue|
68
+ # TODO: reduce unnecessary iterations
69
+ pipeline.call('ZREMRANGEBYSCORE', lock_queue, '-inf', '+inf')
70
+ pipeline.call('EXPIRE', RedisQueuedLocks::Resource.lock_key_from_queue(lock_queue), '0')
71
+ end
68
72
 
69
- # Step B: release all locks
70
- redis.scan(
71
- 'MATCH',
72
- RedisQueuedLocks::Resource::LOCK_PATTERN,
73
- count: batch_size
74
- ) do |lock_key|
75
- # TODO: reduce unnecessary iterations
76
- pipeline.call('EXPIRE', lock_key, '0')
73
+ # Step B: release all locks
74
+ rconn.scan(
75
+ 'MATCH',
76
+ RedisQueuedLocks::Resource::LOCK_PATTERN,
77
+ count: batch_size
78
+ ) do |lock_key|
79
+ # TODO: reduce unnecessary iterations
80
+ pipeline.call('EXPIRE', lock_key, '0')
81
+ end
77
82
  end
78
83
  end
79
84
 
@@ -20,12 +20,15 @@ module RedisQueuedLocks::Acquier::ReleaseLock
20
20
  # The lock name that should be released.
21
21
  # @param isntrumenter [#notify]
22
22
  # See RedisQueuedLocks::Instrument::ActiveSupport for example.
23
+ # @param logger [#debug]
24
+ # - Logger object used from `configuration` layer (see config[:logger]);
25
+ # - See RedisQueuedLocks::Logging::VoidLogger for example;
23
26
  # @return [RedisQueuedLocks::Data,Hash<Symbol,Any>]
24
27
  # Format: { ok: true/false, result: Hash<Symbil,Numeric|String> }
25
28
  #
26
29
  # @api private
27
30
  # @since 0.1.0
28
- def release_lock(redis, lock_name, instrumenter)
31
+ def release_lock(redis, lock_name, instrumenter, logger)
29
32
  lock_key = RedisQueuedLocks::Resource.prepare_lock_key(lock_name)
30
33
  lock_key_queue = RedisQueuedLocks::Resource.prepare_lock_queue(lock_name)
31
34
 
@@ -62,9 +65,11 @@ module RedisQueuedLocks::Acquier::ReleaseLock
62
65
  # @api private
63
66
  # @since 0.1.0
64
67
  def fully_release_lock(redis, lock_key, lock_key_queue)
65
- result = redis.multi do |transact|
66
- transact.call('ZREMRANGEBYSCORE', lock_key_queue, '-inf', '+inf')
67
- transact.call('EXPIRE', lock_key, '0')
68
+ result = redis.with do |rconn|
69
+ rconn.multi do |transact|
70
+ transact.call('ZREMRANGEBYSCORE', lock_key_queue, '-inf', '+inf')
71
+ transact.call('EXPIRE', lock_key, '0')
72
+ end
68
73
  end
69
74
 
70
75
  RedisQueuedLocks::Data[ok: true, result:]
@@ -9,18 +9,16 @@ class RedisQueuedLocks::Client
9
9
 
10
10
  configuration do
11
11
  setting :retry_count, 3
12
- setting :retry_delay, 200 # NOTE: milliseconds
13
- setting :retry_jitter, 25 # NOTE: milliseconds
14
- setting :try_to_lock_timeout, 10 # NOTE: seconds
15
- setting :default_lock_ttl, 5_000 # NOTE: milliseconds
16
- setting :default_queue_ttl, 15 # NOTE: seconds
12
+ setting :retry_delay, 200 # NOTE: in milliseconds
13
+ setting :retry_jitter, 25 # NOTE: in milliseconds
14
+ setting :try_to_lock_timeout, 10 # NOTE: in seconds
15
+ setting :default_lock_ttl, 5_000 # NOTE: in milliseconds
16
+ setting :default_queue_ttl, 15 # NOTE: in seconds
17
17
  setting :lock_release_batch_size, 100
18
18
  setting :key_extraction_batch_size, 500
19
19
  setting :instrumenter, RedisQueuedLocks::Instrument::VoidNotifier
20
20
  setting :uniq_identifier, -> { RedisQueuedLocks::Resource.calc_uniq_identity }
21
-
22
- # TODO: setting :logger, Logger.new(IO::NULL)
23
- # TODO: setting :debug, true/false
21
+ setting :logger, RedisQueuedLocks::Logging::VoidLogger
24
22
 
25
23
  validate('retry_count') { |val| val == nil || (val.is_a?(::Integer) && val >= 0) }
26
24
  validate('retry_delay') { |val| val.is_a?(::Integer) && val >= 0 }
@@ -30,6 +28,7 @@ class RedisQueuedLocks::Client
30
28
  validate('default_queue_ttl', :integer)
31
29
  validate('lock_release_batch_size', :integer)
32
30
  validate('instrumenter') { |val| RedisQueuedLocks::Instrument.valid_interface?(val) }
31
+ validate('logger') { |val| RedisQueuedLocks::Logging.valid_interface?(val) }
33
32
  validate('uniq_identifier', :proc)
34
33
  end
35
34
 
@@ -136,6 +135,7 @@ class RedisQueuedLocks::Client
136
135
  identity:,
137
136
  fail_fast:,
138
137
  metadata:,
138
+ logger: config[:logger],
139
139
  &block
140
140
  )
141
141
  end
@@ -186,7 +186,8 @@ class RedisQueuedLocks::Client
186
186
  RedisQueuedLocks::Acquier::ReleaseLock.release_lock(
187
187
  redis_client,
188
188
  lock_name,
189
- config[:instrumenter]
189
+ config[:instrumenter],
190
+ config[:logger]
190
191
  )
191
192
  end
192
193
 
@@ -233,7 +234,12 @@ class RedisQueuedLocks::Client
233
234
  # @api public
234
235
  # @since 0.1.0
235
236
  def extend_lock_ttl(lock_name, milliseconds)
236
- RedisQueuedLocks::Acquier::ExtendLockTTL.extend_lock_ttl(redis_client, lock_name)
237
+ RedisQueuedLocks::Acquier::ExtendLockTTL.extend_lock_ttl(
238
+ redis_client,
239
+ lock_name,
240
+ milliseconds,
241
+ config[:logger]
242
+ )
237
243
  end
238
244
 
239
245
  # @option batch_size [Integer]
@@ -246,7 +252,8 @@ class RedisQueuedLocks::Client
246
252
  RedisQueuedLocks::Acquier::ReleaseAllLocks.release_all_locks(
247
253
  redis_client,
248
254
  batch_size,
249
- config[:instrumenter]
255
+ config[:instrumenter],
256
+ config[:logger]
250
257
  )
251
258
  end
252
259
 
@@ -8,7 +8,7 @@ module RedisQueuedLocks::Instrument::ActiveSupport
8
8
  # @param payload [Hash<String|Symbol,Any>]
9
9
  # @return [void]
10
10
  #
11
- # @api private
11
+ # @api public
12
12
  # @since 0.1.0
13
13
  def notify(event, payload = {})
14
14
  ::ActiveSupport::Notifications.instrument(event, payload)
@@ -8,7 +8,7 @@ module RedisQueuedLocks::Instrument::VoidNotifier
8
8
  # @param payload [Hash<String|Symbol,Any>]
9
9
  # @return [void]
10
10
  #
11
- # @api private
11
+ # @api public
12
12
  # @since 0.1.0
13
13
  def notify(event, payload = {}); end
14
14
  end
@@ -25,10 +25,10 @@ module RedisQueuedLocks::Instrument
25
25
  m_obj = instrumenter.method(:notify)
26
26
  m_sig = m_obj.parameters
27
27
 
28
- f_prm = m_sig[0][0]
29
- s_prm = m_sig[1][0]
30
-
31
28
  if m_sig.size == 2
29
+ f_prm = m_sig[0][0]
30
+ s_prm = m_sig[1][0]
31
+
32
32
  # rubocop:disable Layout/MultilineOperationIndentation
33
33
  # NOTE: check the signature vairants
34
34
  f_prm == :req && s_prm == :req ||
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api public
4
+ # @since 0.1.0
5
+ module RedisQueuedLocks::Logging::VoidLogger
6
+ class << self
7
+ # @api public
8
+ # @since 0.1.0
9
+ def warn(progname = nil, &block); end
10
+
11
+ # @api public
12
+ # @since 0.1.0
13
+ def unknown(progname = nil, &block); end
14
+
15
+ # @api public
16
+ # @since 0.1.0
17
+ def log(progname = nil, &block); end
18
+
19
+ # @api public
20
+ # @since 0.1.0
21
+ def info(progname = nil, &block); end
22
+
23
+ # @api public
24
+ # @since 0.1.0
25
+ def error(progname = nil, &block); end
26
+
27
+ # @api public
28
+ # @since 0.1.0
29
+ def fatal(progname = nil, &block); end
30
+
31
+ # @api public
32
+ # @since 0.1.0
33
+ def debug(progname = nil, &block); end
34
+
35
+ # @api public
36
+ # @since 0.1.0
37
+ def add(*, &block); end
38
+
39
+ # @api public
40
+ # @since 0.1.0
41
+ def <<(message); end
42
+ end
43
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api public
4
+ # @since 0.1.0
5
+ module RedisQueuedLocks::Logging
6
+ require_relative 'logging/void_logger'
7
+
8
+ class << self
9
+ # @param logger [#debug]
10
+ # @return [Boolean]
11
+ #
12
+ # @api public
13
+ # @since 0.1.0
14
+ def valid_interface?(logger)
15
+ return true if logger.is_a?(::Logger)
16
+
17
+ # NOTE: should provide `#debug` method.
18
+ return false unless logger.respond_to?(:debug)
19
+
20
+ # NOTE:
21
+ # `#debug` method should have appropriated signature `(progname, &block)`
22
+ # Required method signature (progname, &block):
23
+ # => [[:opt, :progname], [:block, :block]]
24
+ # => [[:req, :progname], [:block, :block]]
25
+ # => [[:opt, :progname]]
26
+ # => [[:req, :progname]]
27
+ # => [[:rest], [:block, :block]]
28
+ # => [[:rest]]
29
+
30
+ m_obj = logger.method(:debug)
31
+ m_sig = m_obj.parameters
32
+
33
+ if m_sig.size == 2
34
+ # => [[:opt, :progname], [:block, :block]]
35
+ # => [[:req, :progname], [:block, :block]]
36
+ # => [[:rest], [:block, :block]]
37
+ f_prm = m_sig[0][0]
38
+ s_prm = m_sig[1][0]
39
+
40
+ # rubocop:disable Layout/MultilineOperationIndentation
41
+ f_prm == :opt && s_prm == :block ||
42
+ f_prm == :req && s_prm == :block ||
43
+ f_prm == :rest && s_prm == :block
44
+ # rubocop:enable Layout/MultilineOperationIndentation
45
+ elsif m_sig.size == 1
46
+ # => [[:opt, :progname]]
47
+ # => [[:req, :progname]]
48
+ # => [[:rest]]
49
+ prm = m_sig[0][0]
50
+
51
+ prm == :opt || prm == :req || prm == :rest
52
+ else
53
+ false
54
+ end
55
+ end
56
+ end
57
+ end
@@ -5,6 +5,6 @@ module RedisQueuedLocks
5
5
  #
6
6
  # @api public
7
7
  # @since 0.0.1
8
- # @version 0.0.21
9
- VERSION = '0.0.21'
8
+ # @version 0.0.23
9
+ VERSION = '0.0.23'
10
10
  end
@@ -4,6 +4,7 @@ require 'redis-client'
4
4
  require 'qonfig'
5
5
  require 'timeout'
6
6
  require 'securerandom'
7
+ require 'logger'
7
8
 
8
9
  # @api public
9
10
  # @since 0.1.0
@@ -11,6 +12,7 @@ module RedisQueuedLocks
11
12
  require_relative 'redis_queued_locks/version'
12
13
  require_relative 'redis_queued_locks/errors'
13
14
  require_relative 'redis_queued_locks/utilities'
15
+ require_relative 'redis_queued_locks/logging'
14
16
  require_relative 'redis_queued_locks/data'
15
17
  require_relative 'redis_queued_locks/debugger'
16
18
  require_relative 'redis_queued_locks/resource'
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.21
4
+ version: 0.0.23
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-19 00:00:00.000000000 Z
11
+ date: 2024-03-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis-client
@@ -79,6 +79,8 @@ files:
79
79
  - lib/redis_queued_locks/instrument.rb
80
80
  - lib/redis_queued_locks/instrument/active_support.rb
81
81
  - lib/redis_queued_locks/instrument/void_notifier.rb
82
+ - lib/redis_queued_locks/logging.rb
83
+ - lib/redis_queued_locks/logging/void_logger.rb
82
84
  - lib/redis_queued_locks/resource.rb
83
85
  - lib/redis_queued_locks/utilities.rb
84
86
  - lib/redis_queued_locks/version.rb