Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
## [Unreleased]

## [1.16.0] - 2025-10-19
### Added
- `#claer_locks_of_acquirer` (alias: `#release_locks_of_acquirer`) - remove locks of the concrete acquirer and drop this acquirer form all lock queues;
- `#clear_locks_of_host` (alias: `#release_locks_of_host`) - remove locks of the concrete host;
- Instrumentation events:
- added `:rel_req_cnt` (the count of removed lock-requests from lock queues) to `"redis_queued_locks.release_locks_of"` instrumentation event payload;
- Added new methods to `RedisQueuedLocks::Resource`:
- `.acquirer_host` - extract host identifier from acquirer identifier;
- `.acquirer_pattern_from_host` - extract acquirer search pattern from any host identifier needed for `ZSCAN` command;
- `.extract_identity` - extract **identity** part from acquirer identifier (or from host indetifier) needed for `ZSCAN` command;
- `.extract_non_identified_part` - extract the substring from acquirer identifier (or from host identifier) excluding the **identity** part need for `ZSCAN` command;
### Changed
- the process-ractor-thread-fiber order of the acquirer identifier and the host identifier has changed:
- the order is consider the object scope priority:
- before: **process_id** -> **thread_id** -> **fiber_id** -> **ractor_id** -> **identity**
- after: **process_id** -> **ractor_id** -> **thread_id** -> **fiber_id** -> **identity**
- current patterns of acquire_identifier and host_identifier:
- **acquirer**: `"rql:acq:#{process_id}/#{ractor_id}/#{thread_id}/#{fiber_id}/#{identity}"`
- **host**: `"rql:hst:#{process_id}/#{ractor_id}/#{thread_id}/#{identity}"`

## [1.15.0] - 2025-10-17
### Changed
- `"redis_queud_locks.release_locks_of"` instrumentation event payload now includes `hst_id` and `acq_id`;
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
redis_queued_locks (1.15.0)
redis_queued_locks (1.16.0)
redis-client (~> 0.20)

GEM
Expand Down
51 changes: 49 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ Provides flexible invocation flow, parametrized limits (lock request ttl, lock t
- [clear_locks](#clear_locks---release-all-locks-and-lock-queues) (aka `release_locks`)
- [clear_locks_of](#clear_locks_of) (aka `release_locks_of`)
- [clear_current_locks](#clear_current_locks) (aka `release_current_locks`)
- [clear_locks_of_acquirer](#clear_locks_of_acquirer) (aka `release_locks_of_acquirer`)
- [clear_locks_of_host](#clear_locks_of) (aka `release_locks_of_host`)
- [extend_lock_ttl](#extend_lock_ttl)
- [locks](#locks---get-list-of-obtained-locks)
- [queues](#queues---get-list-of-lock-request-queues)
Expand Down Expand Up @@ -72,6 +74,8 @@ Provides flexible invocation flow, parametrized limits (lock request ttl, lock t
- ["redis_queued_locks.explicit_lock_release"](#redis_queued_locksexplicit_lock_release)
- ["redis_queued_locks.explicit_all_locks_release"](#redis_queued_locksexplicit_all_locks_release)
- ["redis_queued_locks.release_locks_of"](#redis_queued_locksrelease_locks_of)
- ["redis_queued_locks.release_locks_of_acquirer"](#redis_queued_locksrelease_locks_of_acquirer)
- ["redis_queued_locks.release_locks_of_host"](#redis_queued_locksrelease_locks_of_host)
- [Roadmap](#roadmap)
- [Build and Develop](#build-and-develop)
- [Contributing](#contributing)
Expand Down Expand Up @@ -253,6 +257,10 @@ client = RedisQueuedLocks::Client.new(redis_client) do |config|
# - affects the performance of your Redis (configure thoughtfully);
config['clear_locks_of__queue_scan_size'] = 300

# (default: 100)
# - ?
config['clear_locks_of_host__queue_cleanup_cursor_count'] = 100

# (default: 500)
# - how many items should be extracted from redis during the #locks, #queues, #keys
# #locks_info, and #queues_info operations (uses SCAN);
Expand Down Expand Up @@ -1333,6 +1341,19 @@ rql.release_locks_of(host_id: rql.current_host_id, acquirer_id: rql.current_acqu

---

#### clear_locks_of_acquirer

<sup>\[[back to top](#usage)\]</sup>

---


#### clear_locks_of_host

<sup>\[[back to top](#usage)\]</sup>

---

#### #extend_lock_ttl

<sup>\[[back to top](#usage)\]</sup>
Expand Down Expand Up @@ -2194,6 +2215,8 @@ List of instrumentation events
- ["redis_queued_locks.explicit_lock_release"](#redis_queued_locksexplicit_lock_release)
- ["redis_queued_locks.explicit_all_locks_release"](#redis_queued_locksexplicit_all_locks_release)
- ["redis_queued_locks.release_locks_of"](#redis_queued_locksrelease_locks_of)
- ["redis_queued_locks.release_locks_of_acquirer"](#redis_queued_locksrelease_locks_of_acquirer)
- ["redis_queued_locks.release_locks_of_host"](#redis_queued_locksrelease_locks_of_host)

Detalized event semantics and payload structure:

Expand Down Expand Up @@ -2286,16 +2309,41 @@ Detalized event semantics and payload structure:

##### `"redis_queued_locks.release_locks_of"`
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
- an event signalizes about the released locks (and removement from lock queues) of the concrete host and acquirer;
- an event signalizes about the released locks (and removement of all lock requests from lock queues) of the concrete host and acquirer;
- raised from `#clear_locks_of` and `#clear_current_locks` (`#release_locks_of` and `#release_current_locks` respectively);
- payload:
- `:rel_time` - `float`/`milliseconds` - time spent on "release locks of" operation;
- `:at` - `float`/`epoch` - the time when the opertaion has ended;
- `:acq_id` - `string` - refused acquirer identifier;
- `:hst_id` - `string` - refused host identifier;
- `:rel_key_cnt` - `integer` - released locks count;
- `:rel_req_cnt` - `integer` - the count of removed lock requests from all related lock-queues;
- `:tch_queue_cnt` - `:integer` - the number of queues from which the concrete host/acquirer was removed;

#### `"redis_queued_locks.release_locks_of_acquirer"`
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
- an event signalizes about the released locks (and removement of all lock requests from lock queues) of the concrete acquirer;
- raised from `#clear_locks_of_acquirer` (and `#release_locks_of_acquirer` respectively);
- payload:
- `:rel_time` - `float`/`milliseconds` - time spent on "release locks of" operation;
- `:at` - `float`/`epoch` - the time when the opertaion has ended;
- `:acq_id` - `string` - refused acquirer identifier;
- `:rel_key_cnt` - `integer` - released locks count;
- `:rel_req_cnt` - `integer` - the count of removed lock requests from all related lock-queues;
- `:tch_queue_cnt` - `:integer` - the number of queues from which the concrete acquirer was removed;

#### `"redis_queued_locks.release_locks_of_host"`
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
- an event signalizes about the released locks (and removement of all lock requests from lock queues) of the concrete host;
- raised from `#clear_locks_of_host` (and `#release_locks_of_host` respectively);
- payload:
- `:rel_time` - `float`/`milliseconds` - time spent on "release locks of" operation;
- `:at` - `float`/`epoch` - the time when the opertaion has ended;
- `:hst_id` - `string` - refused host identifier;
- `:rel_key_cnt` - `integer` - released locks count;
- `:rel_req_cnt` - `integer` - the count of removed lock requests from all related lock-queues;
- `:tch_queue_cnt` - `:integer` - the number of queues from which the concrete host was removed;

---

## Roadmap
Expand All @@ -2318,7 +2366,6 @@ Detalized event semantics and payload structure:
```ruby
rql.lock_series('lock_a', 'lock_b', 'lock_c') { puts 'locked' }
```
- an ability to release all locks and all requests of the concrete acquirer id or host id (or both in validation-orianted combination);
- detailed lock informotion inside the error object in cases of exceptions (at the moment we have this info inside the error message only that hard to analyze in work);
- a convenient way to mark any `lock` invocation as "non-instrumentable" / "non-loggable" (as an alternative to `VoidNotifier` and to `VoidLogger`);
- `Read`/`Write` semantics: you can mark your locks as `read` or `write` lock in order to simulate `read`/`write` lock behavior:
Expand Down
2 changes: 1 addition & 1 deletion github_ci/ruby3.3.gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: ..
specs:
redis_queued_locks (1.15.0)
redis_queued_locks (1.16.0)
redis-client (~> 0.20)

GEM
Expand Down
6 changes: 3 additions & 3 deletions lib/redis_queued_locks/acquirer/acquire_lock.rb
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ class << self
#
# @api private
# @since 1.0.0
# @version 1.14.0
# @version 1.16.0
def acquire_lock(
redis,
lock_name,
Expand Down Expand Up @@ -232,15 +232,15 @@ def acquire_lock(
# Step 1: prepare lock requirements (generate lock name, calc lock ttl, etc).
acquirer_id = RedisQueuedLocks::Resource.acquirer_identifier(
process_id,
ractor_id,
thread_id,
fiber_id,
ractor_id,
identity
)
host_id = RedisQueuedLocks::Resource.host_identifier(
process_id,
thread_id,
ractor_id,
thread_id,
identity
)
lock_ttl = ttl
Expand Down
15 changes: 12 additions & 3 deletions lib/redis_queued_locks/acquirer/release_locks_of.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

# @api private
# @since 1.14.0
# @version 1.16.0
# rubocop:disable Metrics/ModuleLength
module RedisQueuedLocks::Acquirer::ReleaseLocksOf
# @since 1.14.0
extend RedisQueuedLocks::Utilities
Expand Down Expand Up @@ -77,14 +79,15 @@ class << self
# ok: true,
# result: {
# rel_key_cnt: Integer,
# rel_req_cnt: Integer,
# tch_queue_cnt: Integer,
# rel_time: Numeric
# }
# }
#
# @api private
# @since 1.14.0
# @version 1.15.0
# @version 1.16.0
# rubocop:disable Metrics/MethodLength
def release_locks_of(
refused_host_id,
Expand Down Expand Up @@ -137,6 +140,7 @@ def release_locks_of(
acq_id: refused_acquirer_id,
rel_time: rel_time,
rel_key_cnt: result[:rel_key_cnt],
rel_req_cnt: result[:rel_req_cnt],
tch_queue_cnt: result[:tch_queue_cnt]
})
end if instr_sampled
Expand All @@ -145,6 +149,7 @@ def release_locks_of(
ok: true,
result: {
rel_key_cnt: result[:rel_key_cnt],
rel_req_cnt: result[:rel_req_cnt],
tch_queue_cnt: result[:tch_queue_cnt],
rel_time: rel_time
}
Expand All @@ -160,10 +165,11 @@ def release_locks_of(
# @param lock_scan_size [Integer]
# @param queue_scan_size [Integer]
# @return [Hash<Symbol,Boolean|Hash<Symbol,Integer>>]
# - Example: { ok: true, result: { rel_key_cnt: 12345, tch_queue_cnt: 321 } }
# Example: { ok: true, result: { rel_key_cnt: 12345, tch_queue_cnt: 321, rel_req_cnt: 123 } }
#
# @api private
# @since 1.14.0
# @version 1.16.0
# rubocop:disable Metrics/MethodLength
def fully_release_locks_of(
refused_host_id,
Expand All @@ -174,6 +180,7 @@ def fully_release_locks_of(
)
# TODO: some indexing approach isntead of <scan>
rel_key_cnt = 0
rel_req_cnt = 0
tch_queue_cnt = 0

redis.with do |rconn|
Expand Down Expand Up @@ -210,12 +217,14 @@ def fully_release_locks_of(
count: queue_scan_size
) do |lock_queue|
res = rconn.call('ZREM', lock_queue, refused_acquirer_id)
rel_req_cnt += res
tch_queue_cnt += 1 if res != 0
end
end

{ ok: true, result: { rel_key_cnt:, tch_queue_cnt: } }
{ ok: true, result: { rel_key_cnt:, tch_queue_cnt:, rel_req_cnt: } }
end
end
# rubocop:enable Metrics/MethodLength
end
# rubocop:enable Metrics/ModuleLength
Loading