redis_queued_locks 0.0.21 → 0.0.22

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: 4360a9af038cc33ca36215d729558bab6810b9c5a4fec7f31df6ca2871b7dd80
4
+ data.tar.gz: 10b80a0f389994fde9a4815c3a5031ae85cee3c61d9c2a02e85d80146208bb99
5
5
  SHA512:
6
- metadata.gz: cf96a4535a3fa8f6c7a62797805263e7957f198997213df65c164b9261d5199526f7dbc6e2a3f5e13a912e79cc7cd718ed193b0f197586cd03f1f3f95d958fca
7
- data.tar.gz: 46726b0d18cd73d108b57b34872f1369ab63771a7d6c899aee021ab628ced537e6e0a7e255aecee259b902d682f5d13a12519dcc7033b786ef832c035a61f12a
6
+ metadata.gz: 362e7708a37f8716217e08117f072ef34d14886677379d28bef08f80c1a1a82e1309509dfc5fe4f748a3a10268bf340087c83e797ebcd72cb8172594352fb23d
7
+ data.tar.gz: 32a0943ee8c80b8ed745dff8eeea748c2289008d8fb00d3e20c7b464ef14a4d21158066e548bb86cfebc73c5d61a365289b49ae8061bc3ea0339ad7bb3c9894d
data/CHANGELOG.md CHANGED
@@ -1,8 +1,12 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.0.22] - 2024-03-21
4
+ ### Added
5
+ - Logging infrastructure. Initial implementation includes the only debugging features.
6
+
3
7
  ## [0.0.21] - 2024-03-19
4
8
  ### Changed
5
- - `RedisQueuedLocks::Acquier` refactirngs;
9
+ - Refactored `RedisQueuedLocks::Acquier`;
6
10
 
7
11
  ## [0.0.20] - 2024-03-14
8
12
  ### 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;
@@ -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
@@ -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
 
@@ -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.22
9
+ VERSION = '0.0.22'
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.22
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