diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7560b16..b43f404 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -45,7 +45,7 @@ jobs: - name: (Linter) Rubocop run: bundle exec rake rubocop - name: (TypeCheck) Steep - run: bundle exec steep check --jobs 10 + run: bundle exec rbs collection install && bundle exec steep check --jobs 10 continue-on-error: true - name: (Test) RSpec run: bundle exec rake rspec diff --git a/.gitignore b/.gitignore index ef31ccd..b447771 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ /tmp/ .rspec_status *.gem +/.gem_rbs_collection/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f52e66..7d40bdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,5 @@ ## [Unreleased] - -## Changed +### Changed - Updated development dependencies (`armitage-rubocop`); - CI is splitted to "mainstream ruby version" and "previous actually maintaned ruby versions"; - `Acquier` -> `Acquirer`, `Acquierment` -> `Acquirement` (typos): @@ -9,9 +8,17 @@ - [**Breaking**] Logs: all `acquier` text parts of each log message type were renamed to `acquirer`; - [**Breaking**] Instrumentation: all `acquier` text parts of each event name were renamed to `acquirer`; - [**Breaking**] Exceptions: all `Acquierment` exception constant name parts were renamed to `Acquirement`; -## Added -- Type signatures (`RBS`, see the `sig` directory) + `Steep` integration (see `Steepfile` for details); -- CI: added `TypeCheck` step; +- [**Breaking**] `RedisQueuedLocks::Data` is not used (**temporary**) as result type of some APIs. + This type is inherited from Hash and can not be used as a record type in `steep`/`rbs` (working on it); +### Added +- **Type Checking**: + - the code is fully typed; + - (steep + rbs) officialy supported `RBS` with a `Steep` integration that works + in **strict mode** (see `sig` directory, `Steepfile` config, `sig/manifest.yml` and `rbs_collection.yml` for dependencies); + - Added `TypeCheck` step to CI/CD; +- Test coverage checks (via `simplecov` with `html` and `lcov` formats). + Temporary locked on `~95%` and without CI integration cuz we need to refactor tests in first; +- CI: `rspec-retry` is temporary added until the tests are fully refactored; ## [1.12.1] ### Changed diff --git a/Gemfile b/Gemfile index 54f9dd4..9f4fe58 100644 --- a/Gemfile +++ b/Gemfile @@ -4,11 +4,14 @@ source 'https://rubygems.org' gemspec -gem 'activesupport' -gem 'armitage-rubocop' -gem 'bundler' -gem 'pry' -gem 'rake' -gem 'rbs' -gem 'rspec' -gem 'steep' +gem 'activesupport', require: false +gem 'armitage-rubocop', require: false +gem 'bundler', require: false +gem 'pry', require: false +gem 'rake', require: false +gem 'rbs', require: false +gem 'rspec', require: false +gem 'rspec-retry', require: false # NOTE: temporary decision for non-refactored tests +gem 'simplecov', require: false +gem 'simplecov-lcov', require: false +gem 'steep', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 36c2e2d..bb37a9d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -39,6 +39,7 @@ GEM connection_pool (2.4.1) csv (3.3.0) diff-lcs (1.5.1) + docile (1.4.1) drb (2.2.1) ffi (1.17.0) ffi (1.17.0-aarch64-linux-gnu) @@ -59,7 +60,7 @@ GEM listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) - logger (1.6.1) + logger (1.6.2) method_source (1.1.0) minitest (5.25.2) parallel (1.26.3) @@ -81,7 +82,7 @@ GEM logger redis-client (0.22.2) connection_pool - regexp_parser (2.9.2) + regexp_parser (2.9.3) rspec (3.13.0) rspec-core (~> 3.13.0) rspec-expectations (~> 3.13.0) @@ -94,6 +95,8 @@ GEM rspec-mocks (3.13.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) + rspec-retry (0.6.2) + rspec-core (> 3.3) rspec-support (3.13.1) rubocop (1.68.0) json (~> 2.3) @@ -105,7 +108,7 @@ GEM rubocop-ast (>= 1.32.2, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.36.1) + rubocop-ast (1.36.2) parser (>= 3.3.1.0) rubocop-capybara (2.21.0) rubocop (~> 1.41) @@ -127,7 +130,14 @@ GEM rubocop (~> 1.61) rubocop-rspec (~> 3, >= 3.0.1) ruby-progressbar (1.13.0) - securerandom (0.3.2) + securerandom (0.4.0) + simplecov (0.22.0) + docile (~> 1.1) + simplecov-html (~> 0.11) + simplecov_json_formatter (~> 0.1) + simplecov-html (0.13.1) + simplecov-lcov (0.8.0) + simplecov_json_formatter (0.1.4) steep (1.8.3) activesupport (>= 5.1) concurrent-ruby (>= 1.1.10) @@ -173,6 +183,9 @@ DEPENDENCIES rbs redis_queued_locks! rspec + rspec-retry + simplecov + simplecov-lcov steep BUNDLED WITH diff --git a/github_ci/ruby3.1.gemfile b/github_ci/ruby3.1.gemfile index 9a91f94..d7e9dc3 100644 --- a/github_ci/ruby3.1.gemfile +++ b/github_ci/ruby3.1.gemfile @@ -11,4 +11,7 @@ gem 'pry' gem 'rake' gem 'rbs' gem 'rspec' +gem 'rspec-retry' # NOTE: temporary decision for non-refactored tests +gem 'simplecov' +gem 'simplecov-lcov' gem 'steep' diff --git a/github_ci/ruby3.1.gemfile.lock b/github_ci/ruby3.1.gemfile.lock index dd61588..f0cea48 100644 --- a/github_ci/ruby3.1.gemfile.lock +++ b/github_ci/ruby3.1.gemfile.lock @@ -37,6 +37,7 @@ GEM connection_pool (2.4.1) csv (3.3.0) diff-lcs (1.5.1) + docile (1.4.1) drb (2.2.1) ffi (1.17.0) ffi (1.17.0-aarch64-linux-gnu) @@ -93,6 +94,8 @@ GEM rspec-mocks (3.13.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) + rspec-retry (0.6.2) + rspec-core (> 3.3) rspec-support (3.13.1) rubocop (1.59.0) json (~> 2.3) @@ -127,6 +130,13 @@ GEM rubocop-factory_bot (~> 2.22) ruby-progressbar (1.13.0) securerandom (0.3.2) + simplecov (0.22.0) + docile (~> 1.1) + simplecov-html (~> 0.11) + simplecov_json_formatter (~> 0.1) + simplecov-html (0.13.1) + simplecov-lcov (0.8.0) + simplecov_json_formatter (0.1.4) steep (1.8.3) activesupport (>= 5.1) concurrent-ruby (>= 1.1.10) @@ -171,6 +181,9 @@ DEPENDENCIES rbs redis_queued_locks! rspec + rspec-retry + simplecov + simplecov-lcov steep BUNDLED WITH diff --git a/github_ci/ruby3.2.gemfile b/github_ci/ruby3.2.gemfile index 9a91f94..d7e9dc3 100644 --- a/github_ci/ruby3.2.gemfile +++ b/github_ci/ruby3.2.gemfile @@ -11,4 +11,7 @@ gem 'pry' gem 'rake' gem 'rbs' gem 'rspec' +gem 'rspec-retry' # NOTE: temporary decision for non-refactored tests +gem 'simplecov' +gem 'simplecov-lcov' gem 'steep' diff --git a/github_ci/ruby3.2.gemfile.lock b/github_ci/ruby3.2.gemfile.lock index dd61588..f0cea48 100644 --- a/github_ci/ruby3.2.gemfile.lock +++ b/github_ci/ruby3.2.gemfile.lock @@ -37,6 +37,7 @@ GEM connection_pool (2.4.1) csv (3.3.0) diff-lcs (1.5.1) + docile (1.4.1) drb (2.2.1) ffi (1.17.0) ffi (1.17.0-aarch64-linux-gnu) @@ -93,6 +94,8 @@ GEM rspec-mocks (3.13.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) + rspec-retry (0.6.2) + rspec-core (> 3.3) rspec-support (3.13.1) rubocop (1.59.0) json (~> 2.3) @@ -127,6 +130,13 @@ GEM rubocop-factory_bot (~> 2.22) ruby-progressbar (1.13.0) securerandom (0.3.2) + simplecov (0.22.0) + docile (~> 1.1) + simplecov-html (~> 0.11) + simplecov_json_formatter (~> 0.1) + simplecov-html (0.13.1) + simplecov-lcov (0.8.0) + simplecov_json_formatter (0.1.4) steep (1.8.3) activesupport (>= 5.1) concurrent-ruby (>= 1.1.10) @@ -171,6 +181,9 @@ DEPENDENCIES rbs redis_queued_locks! rspec + rspec-retry + simplecov + simplecov-lcov steep BUNDLED WITH diff --git a/lib/redis_queued_locks/acquirer/acquire_lock.rb b/lib/redis_queued_locks/acquirer/acquire_lock.rb index 3f7a5e9..3925a78 100644 --- a/lib/redis_queued_locks/acquirer/acquire_lock.rb +++ b/lib/redis_queued_locks/acquirer/acquire_lock.rb @@ -45,7 +45,7 @@ class << self # Lock's time to live (in milliseconds). Nil means "without timeout". # @option queue_ttl [Integer] # Lifetime of the acuier's lock request. In seconds. - # @option timeout [Integer] + # @option timeout [Integer,NilClass] # Time period whe should try to acquire the lock (in seconds). # @option timed [Boolean] # Limit the invocation time period of the passed block of code by the lock's TTL. @@ -148,7 +148,7 @@ class << self # - makes sense when instrumentation sampling is enabled; # @param [Block] # A block of code that should be executed after the successfully acquired lock. - # @return [RedisQueuedLocks::Data,Hash,yield] + # @return [Hash,yield] # - Format: { ok: true/false, result: Any } # - If block is given the result of block's yeld will be returned. # @@ -256,7 +256,7 @@ def acquire_lock( ) # Step X: intermediate result observer - # @type var acq_process: ::Hash[::Symbol,untyped] + # @type var acq_process: Hash[Symbol,untyped] acq_process = { lock_info: {}, should_try: true, @@ -319,6 +319,8 @@ def acquire_lock( ) end + # NOTE: (steep ignorance) pattern matching is not supported in steep + # steep:ignore:start try_to_lock( redis, logger, @@ -337,8 +339,10 @@ def acquire_lock( log_sampled, instr_sampled ) => { ok:, result: } + # steep:ignore:end + # @type var ok: bool - # @type var result: ::Symbol|::Hash[::Symbol,untyped] + # @type var result: Symbol|Hash[Symbol,untyped] acq_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond) acq_time = ((acq_end_time - acq_start_time) / 1_000.0).ceil(2) @@ -349,7 +353,7 @@ def acquire_lock( # Step 2.1: analyze an acquirement attempt if ok - # @type var result: ::Hash[::Symbol,untyped] + # @type var result: Hash[Symbol,untyped] # Step X: (instrumentation) if acq_process[:result][:process] == :extendable_conflict_work_through @@ -402,7 +406,7 @@ def acquire_lock( acq_process[:acq_time] = acq_time acq_process[:acq_end_time] = acq_end_time else - # @type var result: ::Symbol + # @type var result: Symbol # Step 2.2: failed to acquire. anylize each case and act in accordance if acq_process[:result] == :fail_fast_no_try # Step 2.2.a: fail without try @@ -545,7 +549,9 @@ def acquire_lock( end end else - RedisQueuedLocks::Data[ok: true, result: acq_process[:lock_info]] # steep:ignore + # rubocop:disable Layout/LineLength + { ok: true, result: acq_process[:lock_info] } #: { ok: bool, result: Hash[Symbol,untyped] } + # rubocop:enable Layout/LineLength end else if acq_process[:result] != :retry_limit_reached && @@ -559,7 +565,7 @@ def acquire_lock( acq_process[:result] = :timeout_reached end # Step 3.b: lock is not acquired (acquirer is dequeued by timeout callback) - RedisQueuedLocks::Data[ok: false, result: acq_process[:result]] # steep:ignore + { ok: false, result: acq_process[:result] } #: { ok: bool, result: Symbol } end end end diff --git a/lib/redis_queued_locks/acquirer/acquire_lock/delay_execution.rb b/lib/redis_queued_locks/acquirer/acquire_lock/delay_execution.rb index 6e60cb9..fd7b914 100644 --- a/lib/redis_queued_locks/acquirer/acquire_lock/delay_execution.rb +++ b/lib/redis_queued_locks/acquirer/acquire_lock/delay_execution.rb @@ -7,7 +7,7 @@ module RedisQueuedLocks::Acquirer::AcquireLock::DelayExecution # # @param retry_delay [Integer] In milliseconds # @param retry_jitter [Integer] In milliseconds - # @return [void] + # @return [Integer] Slept seconds # # @api private # @since 1.0.0 diff --git a/lib/redis_queued_locks/acquirer/acquire_lock/dequeue_from_lock_queue.rb b/lib/redis_queued_locks/acquirer/acquire_lock/dequeue_from_lock_queue.rb index 8544363..8d183b6 100644 --- a/lib/redis_queued_locks/acquirer/acquire_lock/dequeue_from_lock_queue.rb +++ b/lib/redis_queued_locks/acquirer/acquire_lock/dequeue_from_lock_queue.rb @@ -15,7 +15,7 @@ module RedisQueuedLocks::Acquirer::AcquireLock::DequeueFromLockQueue # @param access_strategy [Symbol] # @param log_sampled [Boolean] # @param instr_sampled [Boolean] - # @return [Hash] Format: { ok: true/false, result: Any } + # @return [Hash] Format: { ok: true/false, result: Integer } # # @api private # @since 1.7.0 @@ -32,11 +32,14 @@ def dequeue_from_lock_queue( log_sampled, instr_sampled ) + # @type var result: Integer result = redis.call('ZREM', lock_key_queue, acquirer_id) + LogVisitor.dequeue_from_lock_queue( logger, log_sampled, lock_key, queue_ttl, acquirer_id, host_id, access_strategy ) - RedisQueuedLocks::Data[ok: true, result: result] # steep:ignore + + { ok: true, result: result } end end diff --git a/lib/redis_queued_locks/acquirer/acquire_lock/try_to_lock.rb b/lib/redis_queued_locks/acquirer/acquire_lock/try_to_lock.rb index cf2eb20..42a0432 100644 --- a/lib/redis_queued_locks/acquirer/acquire_lock/try_to_lock.rb +++ b/lib/redis_queued_locks/acquirer/acquire_lock/try_to_lock.rb @@ -57,11 +57,11 @@ def try_to_lock( instr_sampled ) # Step X: intermediate invocation results - # @type var inter_result: ::Symbol? + # @type var inter_result: Symbol? inter_result = nil - # @type var timestamp: ::Float? + # @type var timestamp: Float? timestamp = nil - # @type var spc_processed_timestamp: ::Float? + # @type var spc_processed_timestamp: Float? spc_processed_timestamp = nil LogVisitor.start( @@ -155,8 +155,8 @@ def try_to_lock( ) inter_result = :extendable_conflict_work_through - # @type var sp_conflict_status: ::Symbol - # @type var spc_processed_timestamp: ::Float + # @type var sp_conflict_status: Symbol + # @type var spc_processed_timestamp: Float LogVisitor.reentrant_lock__extend_and_work_through( logger, log_sampled, log_lock_try, lock_key, queue_ttl, acquirer_id, host_id, access_strategy, @@ -182,8 +182,8 @@ def try_to_lock( 'l_spc_ts', (spc_processed_timestamp = Time.now.to_f) ) - # @type var sp_conflict_status: ::Symbol - # @type var spc_processed_timestamp: ::Float + # @type var sp_conflict_status: Symbol + # @type var spc_processed_timestamp: Float LogVisitor.reentrant_lock__work_through( logger, log_sampled, log_lock_try, lock_key, queue_ttl, acquirer_id, host_id, access_strategy, @@ -194,8 +194,8 @@ def try_to_lock( inter_result = :conflict_dead_lock spc_processed_timestamp = Time.now.to_f - # @type var sp_conflict_status: ::Symbol - # @type var spc_processed_timestamp: ::Float + # @type var sp_conflict_status: Symbol + # @type var spc_processed_timestamp: Float LogVisitor.single_process_lock_conflict__dead_lock( logger, log_sampled, log_lock_try, lock_key, queue_ttl, acquirer_id, host_id, access_strategy, @@ -323,75 +323,74 @@ def try_to_lock( # 3. HINCRBY (increased spc count) (OK for != nil) # 4. HSET (store the last spc time and ttl data) (OK for == 2 or != nil) if result[0] != nil && result[1] != nil && result[2] != nil && result[3] != nil - # steep:ignore:start - RedisQueuedLocks::Data[ok: true, result: { - process: :extendable_conflict_work_through, - lock_key: lock_key, - acq_id: acquirer_id, - hst_id: host_id, - ts: spc_processed_timestamp, - ttl: ttl - }] - # steep:ignore:end + { + ok: true, + result: { + process: :extendable_conflict_work_through, + lock_key: lock_key, + acq_id: acquirer_id, + hst_id: host_id, + ts: spc_processed_timestamp, + ttl: ttl + } + } elsif result[0] != nil # NOTE: that is enough to the fact that the lock is extended but # TODO: add detalized overview (log? some in-line code clarifications?) of the result - - # steep:ignore:start - RedisQueuedLocks::Data[ok: true, result: { - process: :extendable_conflict_work_through, - lock_key: lock_key, - acq_id: acquirer_id, - hst_id: host_id, - ts: spc_processed_timestamp, - ttl: ttl - }] - # steep:ignore:end + { + ok: true, + result: { + process: :extendable_conflict_work_through, + lock_key: lock_key, + acq_id: acquirer_id, + hst_id: host_id, + ts: spc_processed_timestamp, + ttl: ttl + } + } else # NOTE: unknown behaviour :thinking: - RedisQueuedLocks::Data[ok: false, result: :unknown] # steep:ignore + { ok: false, result: :unknown } end elsif result == nil || (result.is_a?(::Array) && result.empty?) # NOTE: the lock key was changed durign an SPC logic execution - - # steep:ignore:start - RedisQueuedLocks::Data[ok: false, result: :lock_is_acquired_during_acquire_race] - # steep:ignore:end + { ok: false, result: :lock_is_acquired_during_acquire_race } else # NOTE: unknown behaviour :thinking:. this part is not reachable at the moment. - RedisQueuedLocks::Data[ok: false, result: :unknown] # steep:ignore + { ok: false, result: :unknown } end when inter_result == :conflict_work_through # Step 7.same_process_conflict.B: # - conflict_work_through case => yield without lock realesing/extending - - # steep:ignore:start - RedisQueuedLocks::Data[ok: true, result: { - process: :conflict_work_through, - lock_key: lock_key, - acq_id: acquirer_id, - hst_id: host_id, - ts: spc_processed_timestamp, - ttl: ttl - }] - # steep:ignore:end + { + ok: true, + result: { + process: :conflict_work_through, + lock_key: lock_key, + acq_id: acquirer_id, + hst_id: host_id, + ts: spc_processed_timestamp, + ttl: ttl + } + } when inter_result == :conflict_dead_lock # Step 7.same_process_conflict.C: # - deadlock. should fail in acquirement logic; - RedisQueuedLocks::Data[ok: false, result: inter_result] # steep:ignore + { ok: false, result: :conflict_dead_lock } when fail_fast && inter_result == :fail_fast_no_try # Step 7.a: lock is still acquired and we should exit from the logic as soon as possible - RedisQueuedLocks::Data[ok: false, result: inter_result] # steep:ignore + { ok: false, result: :fail_fast_no_try } when inter_result == :dead_score_reached - RedisQueuedLocks::Data[ok: false, result: inter_result] # steep:ignore - when inter_result == :lock_is_still_acquired || inter_result == :acquirer_is_not_first_in_queue + { ok: false, result: :dead_score_reached } + when inter_result == :lock_is_still_acquired # Step 7.b: lock is still acquired by another process => failed to acquire - RedisQueuedLocks::Data[ok: false, result: inter_result] # steep:ignore + { ok: false, result: :lock_is_still_acquired } + when inter_result == :acquirer_is_not_first_in_queue + # Step 7.c: lock is still acquired by another process => failed to acquire + { ok: false, result: :acquirer_is_not_first_in_queue } when result == nil || (result.is_a?(::Array) && result.empty?) - # Step 7.c: lock is already acquired durign the acquire race => failed to acquire - # steep:ignore:start - RedisQueuedLocks::Data[ok: false, result: :lock_is_acquired_during_acquire_race] - # steep:ignore:end + # Step 7.d: lock is already acquired durign the acquire race => failed to acquire + { ok: false, result: :lock_is_acquired_during_acquire_race } when result.is_a?(::Array) && result.size == 3 # NOTE: 3 is a count of redis lock commands # TODO: # => (!) analyze the command result and do actions with the depending on it; @@ -403,19 +402,20 @@ def try_to_lock( # 3. pexpire should return 1 (expiration time is successfully applied) # Step 7.d: locked! :) let's go! => successfully acquired - # steep:ignore:start - RedisQueuedLocks::Data[ok: true, result: { - process: :lock_obtaining, - lock_key: lock_key, - acq_id: acquirer_id, - hst_id: host_id, - ts: timestamp, - ttl: ttl - }] - # steep:ignore:end + { + ok: true, + result: { + process: :lock_obtaining, + lock_key: lock_key, + acq_id: acquirer_id, + hst_id: host_id, + ts: timestamp, + ttl: ttl + } + } else # Ste 7.3: unknown behaviour :thinking: - RedisQueuedLocks::Data[ok: false, result: :unknown] # steep:ignore + { ok: false, result: :unknown } end # rubocop:enable Lint/DuplicateBranch end diff --git a/lib/redis_queued_locks/acquirer/acquire_lock/with_acq_timeout.rb b/lib/redis_queued_locks/acquirer/acquire_lock/with_acq_timeout.rb index b2758d4..37f257e 100644 --- a/lib/redis_queued_locks/acquirer/acquire_lock/with_acq_timeout.rb +++ b/lib/redis_queued_locks/acquirer/acquire_lock/with_acq_timeout.rb @@ -39,7 +39,7 @@ def with_acq_timeout( ::Timeout.timeout(timeout, RedisQueuedLocks::LockAcquirementIntermediateTimeoutError, &block) rescue RedisQueuedLocks::LockAcquirementIntermediateTimeoutError if on_timeout != nil - # @type var on_timeout: ::Proc + # @type var on_timeout: Proc on_timeout.call end diff --git a/lib/redis_queued_locks/acquirer/acquire_lock/yield_expire.rb b/lib/redis_queued_locks/acquirer/acquire_lock/yield_expire.rb index 428bc83..9f46dc4 100644 --- a/lib/redis_queued_locks/acquirer/acquire_lock/yield_expire.rb +++ b/lib/redis_queued_locks/acquirer/acquire_lock/yield_expire.rb @@ -63,17 +63,17 @@ def yield_expire( if timed && ttl != nil # NOTE: # - steep is ignored cuz steep can not recognize `::Integer - ::Integer|::Float` - # operation here for some mystical reason (it tryes to fined overloadd `-` - # method for integer and fails on it); + # operation here for some mystical reason (it tryes to find overloadd `-` method for + # integer and fails on it); # - so we need to ignore steep here at all and manually set the type of each # variable for the correct following variable type recognitions; # steep:ignore:start - # @type var ttl: ::Integer - # @type var ttl_shift: ::Float|::Integer - # @type var timeout: ::Float + # @type var ttl: Integer + # @type var ttl_shift: Float|Integer + # @type var timeout: Float timeout = ((ttl - ttl_shift) / 1_000.0).yield_self do |time| - # @type var time: ::Float + # @type var time: Float # NOTE: time in cuz Ruby's Timeout requires (time < 0) ? 0.0 : time end @@ -93,9 +93,9 @@ def yield_expire( redis.call('EXPIRE', lock_key, '0') elsif should_decrease finish_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :millisecond) - # @type var initial_time: ::Integer + # @type var initial_time: Integer spent_time = (finish_time - initial_time) - # @type var ttl: ::Integer + # @type var ttl: Integer decreased_ttl = ttl - spent_time - RedisQueuedLocks::Resource::REDIS_TIMESHIFT_ERROR if decreased_ttl > 0 diff --git a/lib/redis_queued_locks/acquirer/clear_dead_requests.rb b/lib/redis_queued_locks/acquirer/clear_dead_requests.rb index 29b1e02..8bdd81d 100644 --- a/lib/redis_queued_locks/acquirer/clear_dead_requests.rb +++ b/lib/redis_queued_locks/acquirer/clear_dead_requests.rb @@ -41,9 +41,9 @@ def clear_dead_requests( ) dead_score = RedisQueuedLocks::Resource.acquirer_dead_score(dead_ttl / 1_000.0) - # @type var result: ::Set[::String] + # @type var result: Set[String] result = Set.new.tap do |processed_queues| - # @type var processed_queues: ::Set[::String] + # @type var processed_queues: Set[String] redis_client.with do |rconn| each_lock_queue(rconn, scan_size) do |lock_queue| rconn.call('ZREMRANGEBYSCORE', lock_queue, '-inf', dead_score) @@ -52,7 +52,7 @@ def clear_dead_requests( end end - RedisQueuedLocks::Data[ok: true, result: { processed_queues: result }] # steep:ignore + { ok: true, result: { processed_queues: result } } end private diff --git a/lib/redis_queued_locks/acquirer/extend_lock_ttl.rb b/lib/redis_queued_locks/acquirer/extend_lock_ttl.rb index ad058a1..5b2fce4 100644 --- a/lib/redis_queued_locks/acquirer/extend_lock_ttl.rb +++ b/lib/redis_queued_locks/acquirer/extend_lock_ttl.rb @@ -54,11 +54,11 @@ def extend_lock_ttl( result = redis_client.call('EVAL', EXTEND_LOCK_PTTL, 1, lock_key, milliseconds) # TODO: upload scripts to the redis - # @type var result: ::Integer + # @type var result: Integer if result == 1 - RedisQueuedLocks::Data[ok: true, result: :ttl_extended] # steep:ignore + { ok: true, result: :ttl_extended } else - RedisQueuedLocks::Data[ok: false, result: :async_expire_or_no_lock] # steep:ignore + { ok: false, result: :async_expire_or_no_lock } end end end diff --git a/lib/redis_queued_locks/acquirer/keys.rb b/lib/redis_queued_locks/acquirer/keys.rb index d71764a..15a8f9c 100644 --- a/lib/redis_queued_locks/acquirer/keys.rb +++ b/lib/redis_queued_locks/acquirer/keys.rb @@ -12,7 +12,7 @@ class << self # @since 1.0.0 def keys(redis_client, scan_size:) Set.new.tap do |keyset| - # @type var keyset: ::Set[::String] + # @type var keyset: Set[String] redis_client.scan( 'MATCH', RedisQueuedLocks::Resource::KEY_PATTERN, diff --git a/lib/redis_queued_locks/acquirer/lock_info.rb b/lib/redis_queued_locks/acquirer/lock_info.rb index d708f7f..c57f95b 100644 --- a/lib/redis_queued_locks/acquirer/lock_info.rb +++ b/lib/redis_queued_locks/acquirer/lock_info.rb @@ -49,7 +49,7 @@ def lock_info(redis_client, lock_name) # - result[1] (PTTL) (Integer) # => (without any mutation, integer is atomic) - # @type var result: [::Hash[::String,::String|::Float|::Integer],::Integer] + # @type var result: [Hash[String,String|Float|Integer],Integer] hget_cmd_res = result[0] pttl_cmd_res = result[1] diff --git a/lib/redis_queued_locks/acquirer/locks.rb b/lib/redis_queued_locks/acquirer/locks.rb index 240b455..dc4fe39 100644 --- a/lib/redis_queued_locks/acquirer/locks.rb +++ b/lib/redis_queued_locks/acquirer/locks.rb @@ -51,21 +51,21 @@ def extract_locks_info(redis_client, lock_keys) # TODO: refactor with RedisQueuedLocks::Acquier::LockInfo Set.new.tap do |seeded_locks| # rubocop:disable Layout/LineLength - # @type var seeded_locks: ::Set[{ lock: ::String, status: :released|:alive, info: ::Hash[::String,untyped] }] + # @type var seeded_locks: Set[{ lock: String, status: :released|:alive, info: Hash[String,untyped] }] # rubocop:enable Layout/LineLength # Step X: iterate each lock and extract their info lock_keys.each do |lock_key| # Step 1: extract lock info from redis - # @type var lock_info: ::Hash[::String,::String|::Float|::Integer] + # @type var lock_info: Hash[String,String|Float|Integer] lock_info = redis_client.pipelined do |pipeline| pipeline.call('HGETALL', lock_key) pipeline.call('PTTL', lock_key) end.yield_self do |result| # Step 2: format the result # Step 2.X: lock is released if result == nil - {} #: ::Hash[::String,::String|::Float|::Integer] + {} #: Hash[String,String|Float|Integer] else # NOTE: the result of MULTI-command is an array of results of each internal command # - result[0] (HGETALL) (Hash) @@ -73,13 +73,13 @@ def extract_locks_info(redis_client, lock_keys) # - result[1] (PTTL) (Integer) # => (without any mutation, integer is atomic) - # @type var result: [::Hash[::String,::String|::Float|::Integer],::Integer] + # @type var result: [Hash[String,String|Float|Integer],Integer] hget_cmd_res = result[0] # NOTE: HGETALL result (hash) pttl_cmd_res = result[1] # NOTE: PTTL result (integer) # Step 2.Y: lock is released if hget_cmd_res == {} || pttl_cmd_res == -2 # NOTE: key does not exist - {} #: ::Hash[::String,::String|::Float|::Integer] + {} #: Hash[String,String|Float|Integer] else # Step 2.Z: lock is alive => format received info + add additional rem_ttl info hget_cmd_res.tap do |lock_data| diff --git a/lib/redis_queued_locks/acquirer/queue_info.rb b/lib/redis_queued_locks/acquirer/queue_info.rb index d4c1f5d..a72bc5c 100644 --- a/lib/redis_queued_locks/acquirer/queue_info.rb +++ b/lib/redis_queued_locks/acquirer/queue_info.rb @@ -33,7 +33,7 @@ def queue_info(redis_client, lock_name) pipeline.call('ZRANGE', lock_key_queue, '0', '-1', 'WITHSCORES') end - # @type var result: [::Integer,::Array[[::String,::Integer|::Float]]] + # @type var result: [Integer,Array[[String,Integer|Float]]] exists_cmd_res = result[0] zrange_cmd_res = result[1] diff --git a/lib/redis_queued_locks/acquirer/queues.rb b/lib/redis_queued_locks/acquirer/queues.rb index 84dde2d..f30c68c 100644 --- a/lib/redis_queued_locks/acquirer/queues.rb +++ b/lib/redis_queued_locks/acquirer/queues.rb @@ -28,14 +28,14 @@ def queues(redis_client, scan_size:, with_info:) # @since 1.0.0 def scan_queues(redis_client, scan_size) Set.new.tap do |lock_queues| - # @type var lock_queues: ::Set[::String] + # @type var lock_queues: Set[String] redis_client.scan( 'MATCH', RedisQueuedLocks::Resource::LOCK_QUEUE_PATTERN, count: scan_size ) do |lock_queue| # TODO: reduce unnecessary iterations - # @type var lock_queue: ::String + # @type var lock_queue: String lock_queues.add(lock_queue) end end @@ -51,14 +51,14 @@ def extract_queues_info(redis_client, lock_queues) # TODO: refactor with RedisQueuedLocks::Acquier::QueueInfo Set.new.tap do |seeded_queues| # Step X: iterate over each lock queue and extract their info - # @type var seeded_queues: ::Set[::Hash[::Symbol,untyped]] + # @type var seeded_queues: Set[Hash[Symbol,untyped]] lock_queues.each do |lock_queue| # Step 1: extract lock queue info from reids queue_info = redis_client.pipelined do |pipeline| pipeline.call('EXISTS', lock_queue) pipeline.call('ZRANGE', lock_queue, '0', '-1', 'WITHSCORES') end.yield_self do |result| # Step 2: format the result - # @type var result: [::Integer, ::Array[[::String,::Float]]] + # @type var result: [Integer, Array[[String,Float]]] exists_cmd_res = result[0] zrange_cmd_res = result[1] @@ -67,11 +67,11 @@ def extract_queues_info(redis_client, lock_queues) zrange_cmd_res.map { |val| { 'acq_id' => val[0], 'score' => val[1] } } else # Step 2.Y: lock queue did not exist during the pipeline invocation - [] #: ::Array[::Hash[::String,::String|::Float]] + [] #: Array[Hash[String,String|Float]] end end - # @type var queue_info: ::Array[::Hash[::String,::String|::Float]] + # @type var queue_info: Array[Hash[String,String|Float]] # Step 3: push the lock queue info to the result store seeded_queues << { diff --git a/lib/redis_queued_locks/acquirer/release_all_locks.rb b/lib/redis_queued_locks/acquirer/release_all_locks.rb index 8258c22..bf04b3b 100644 --- a/lib/redis_queued_locks/acquirer/release_all_locks.rb +++ b/lib/redis_queued_locks/acquirer/release_all_locks.rb @@ -65,7 +65,7 @@ class << self # - marks the method that everything should be instrumneted # despite the enabled instrumentation sampling; # - makes sense when instrumentation sampling is enabled; - # @return [RedisQueuedLocks::Data,Hash] + # @return [Hash] # Format: { ok: true, result: Hash } # # @api private @@ -90,7 +90,7 @@ def release_all_locks( fully_release_all_locks(redis, batch_size) => { ok:, result: } # steep:ignore # @type var ok: bool - # @type var result: ::RedisQueuedLocks::Data + # @type var result: Hash[Symbol,Integer] time_at = Time.now.to_f rel_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond) @@ -104,21 +104,17 @@ def release_all_locks( ) run_non_critical do - # steep:ignore:start instrumenter.notify('redis_queued_locks.explicit_all_locks_release', { at: time_at, rel_time: rel_time, rel_key_cnt: result[:rel_key_cnt] }) - # steep:ignore:end end if instr_sampled - # steep:ignore:start - RedisQueuedLocks::Data[ + { ok: true, result: { rel_key_cnt: result[:rel_key_cnt], rel_time: rel_time } - ] - # steep:ignore:end + } end private @@ -127,7 +123,7 @@ def release_all_locks( # # @param redis [RedisClient] # @param batch_size [Integer] - # @return [RedisQueuedLocks::Data,Hash>] + # @return [Hash>] # - Exmaple: { ok: true, result: { rel_key_cnt: 12345 } } # # @api private @@ -157,7 +153,7 @@ def fully_release_all_locks(redis, batch_size) end end - RedisQueuedLocks::Data[ok: true, result: { rel_key_cnt: result.sum }] # steep:ignore + { ok: true, result: { rel_key_cnt: result.sum } } end end end diff --git a/lib/redis_queued_locks/acquirer/release_lock.rb b/lib/redis_queued_locks/acquirer/release_lock.rb index 3e08687..628be92 100644 --- a/lib/redis_queued_locks/acquirer/release_lock.rb +++ b/lib/redis_queued_locks/acquirer/release_lock.rb @@ -65,7 +65,7 @@ class << self # - marks the method that everything should be instrumneted # despite the enabled instrumentation sampling; # - makes sense when instrumentation sampling is enabled; - # @return [RedisQueuedLocks::Data,Hash>] + # @return [Hash>] # Format: { ok: true/false, result: Hash } # # @api private @@ -93,7 +93,7 @@ def release_lock( fully_release_lock(redis, lock_key, lock_key_queue) => { ok:, result: } # steep:ignore # @type var ok: bool - # @type var result: ::RedisQueuedLocks::Data + # @type var result: Hash[Symbol,Symbol] time_at = Time.now.to_f rel_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond) @@ -115,8 +115,7 @@ def release_lock( }) end if instr_sampled - # steep:ignore:start - RedisQueuedLocks::Data[ + { ok: true, result: { rel_time: rel_time, @@ -125,8 +124,7 @@ def release_lock( queue_res: result[:queue], lock_res: result[:lock] } - ] - # steep:ignore:end + } end # rubocop:enable Metrics/MethodLength @@ -137,7 +135,7 @@ def release_lock( # @param redis [RedisClient] # @param lock_key [String] # @param lock_key_queue [String] - # @return [RedisQueuedLocks::Data,Hash>] + # @return [Hash>] # Format: { # ok: true/false, # result: { @@ -149,6 +147,7 @@ def release_lock( # @api private # @since 1.0.0 def fully_release_lock(redis, lock_key, lock_key_queue) + # @type var result: [Integer,Integer] result = redis.with do |rconn| rconn.multi do |transact| transact.call('ZREMRANGEBYSCORE', lock_key_queue, '-inf', '+inf') @@ -156,17 +155,13 @@ def fully_release_lock(redis, lock_key, lock_key_queue) end end - # @type var result: [::Integer,::Integer] - - # steep:ignore:start - RedisQueuedLocks::Data[ + { ok: true, result: { queue: (result[0] != 0) ? :released : :nothing_to_release, lock: (result[1] != 0) ? :released : :nothing_to_release } - ] - # steep:ignore:end + } end end end diff --git a/lib/redis_queued_locks/client.rb b/lib/redis_queued_locks/client.rb index 4b955ea..cbe8ee1 100644 --- a/lib/redis_queued_locks/client.rb +++ b/lib/redis_queued_locks/client.rb @@ -400,7 +400,7 @@ def zombies_info( # - makes sense when instrumentation sampling is enabled; # @param block [Block] # A block of code that should be executed after the successfully acquired lock. - # @return [RedisQueuedLocks::Data,Hash,yield] + # @return [Hash,yield] # - Format: { ok: true/false, result: Symbol/Hash }. # - If block is given the result of block's yeld will be returned. # @@ -556,7 +556,7 @@ def lock!( # @option instr_sampling_percent [Integer] # @option instr_sampler [#sampling_happened?,Module] # @option instr_sample_this [Boolean] - # @return [RedisQueuedLocks::Data, Hash] + # @return [Hash] # Format: { # ok: true/false, # result: { @@ -795,7 +795,7 @@ def extend_lock_ttl( # @option instr_sampling_percent [Integer] # @option instr_sampler [#sampling_happened?,Module] # @option instr_sample_this [Boolean] - # @return [RedisQueuedLocks::Data,Hash>] + # @return [Hash>] # Example: { ok: true, result { rel_key_cnt: 100, rel_time: 0.01 } } # # @api public diff --git a/lib/redis_queued_locks/resource.rb b/lib/redis_queued_locks/resource.rb index 3e6329c..7382b6b 100644 --- a/lib/redis_queued_locks/resource.rb +++ b/lib/redis_queued_locks/resource.rb @@ -189,7 +189,7 @@ def possible_host_identifiers(identity) # @type var current_process_id: Integer current_process_id = get_process_id - # @type var current_threads: Array[::Thread] + # @type var current_threads: Array[Thread] current_threads = ::Thread.list # @type var current_ractor_id: Integer current_ractor_id = get_ractor_id diff --git a/lib/redis_queued_locks/swarm.rb b/lib/redis_queued_locks/swarm.rb index d231cdf..e9c5336 100644 --- a/lib/redis_queued_locks/swarm.rb +++ b/lib/redis_queued_locks/swarm.rb @@ -83,16 +83,7 @@ def swarm_info(zombie_ttl: rql_client.config[:swarm][:flush_zombies][:zombie_ttl ) end - # @return [ - # RedisQueuedLocks::Data[ - # ok: , - # result: { - # host_id1 => score1 , - # host_id2 => score2 , - # etc... - # } - # ] - # ] + # @return [Hash, - # deleted_zombie_hosts: >, - # deleted_zombie_acquirers: >, - # deleted_zombie_locks: > - # ] - # ] + # @return [Hash] # # @api public # @since 1.9.0 @@ -176,11 +160,6 @@ def zombie_hosts(zombie_ttl: rql_client.config[:swarm][:flush_zombies][:zombie_t # @option zombie_ttl [Integer] # @option lock_sacn_size [Integer] # @return [Hash>] - # Format: { - # zombie_hosts: >, - # zombie_acquirers: >, - # zombie_locks: > - # } # # @api public # @since 1.9.0 @@ -195,7 +174,7 @@ def zombies_info( ) end - # @return [NilClass,Hash] + # @return [Hash] Example: { ok: true, result: :swarming } # # @api public # @since 1.9.0 @@ -222,11 +201,11 @@ def swarm! # NOTE: need to give a little timespot to initialize ractor objects and their main loops; sleep(0.1) - RedisQueuedLocks::Data[ok: true, result: :swarming] + { ok: true, result: :swarming } end end - # @return [NilClass,Hash] + # @return [Hash] Example: { ok: true, result: :terminating } # # @api public # @since 1.9.0 @@ -239,7 +218,7 @@ def deswarm! # NOTE: need to give a little timespot to stop ractor objects and their main loops; sleep(0.1) - RedisQueuedLocks::Data[ok: true, result: :terminating] + { ok: true, result: :terminating } end end end diff --git a/lib/redis_queued_locks/swarm/acquirers.rb b/lib/redis_queued_locks/swarm/acquirers.rb index 8b9110e..718b376 100644 --- a/lib/redis_queued_locks/swarm/acquirers.rb +++ b/lib/redis_queued_locks/swarm/acquirers.rb @@ -5,34 +5,33 @@ module RedisQueuedLocks::Swarm::Acquirers class << self # Returns the list of swarm acquirers stored as HASH. - # Format: - # { - # => { - # zombie: , - # last_probe_time: