redis_queued_locks 1.13.0 → 1.14.0

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: 306e86a36b29ee294af7e0ee9eda0c3815e1b646733abf3f30e25c4f0d3f0394
4
- data.tar.gz: 1f4b4d1b762f4c27aab1bfae415c2f183593ab5a2772b07e4789d44aa0b5ea1c
3
+ metadata.gz: 7924e5055147fe7c2e0ff65e59ca7e82dc0ae81ce2c880cced9c914307fb1821
4
+ data.tar.gz: e6921c3d151ae3d227ad130989562f96266954c47c8358ecd82f76afc922c362
5
5
  SHA512:
6
- metadata.gz: f48e55fece224be62a769d2eaba83fe206b3275fed1849dc000dfd75d51794a1f023af43c4a0647809fc6ee6fb8d80258e2686d9b1f59fa3ff3a0b20651db89b
7
- data.tar.gz: 338a6748a479158d2429c57b6afab1d10682b94e1751560c595c4d9dadd0a607b1d3ea45aeb5612b003a0a88d208780e7dcdb474f05418ac817a46bb9bd16de1
6
+ metadata.gz: e0b1deaefb5638a96392c759a903661ad3175e45b128e605f0939889c56974c6f6ea2f887884a883fcd8843aec5423bd384a7c8d24644a42ff4e0c6737e0340e
7
+ data.tar.gz: ffaea4bd5b9c57802b99c754e2defb6e16ad9cff3e82855b4a8e6e3f93538ba648dbc84f3cd85200017f47bbc9f4d26a7bbb5d910776c79b19adbc02e5a002ed
data/.rubocop.yml CHANGED
@@ -13,7 +13,6 @@ AllCops:
13
13
  - spec/**/*.rb
14
14
  - sig/**/*.rbs
15
15
  - Gemfile
16
- - gemfiles/ruby3.3.gemfile
17
16
  - Rakefile
18
17
  - redis_queued_locks.gemspec
19
18
  - bin/console
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.4.3
1
+ 3.4.5
data/CHANGELOG.md CHANGED
@@ -1,4 +1,17 @@
1
1
  ## [Unreleased]
2
+
3
+ ## [1.14.0] - 2025-10-17
4
+ ### Added
5
+ - `#clear_locks_of`/`#release_locks_of`
6
+ - `#clear_current_locks`/`#release_current_locks`
7
+ - aliases for `RedisQueuedLocks::Client#current_acquirer_id`: `current_acq_id`, `acq_id`;
8
+ - aliases for `RedisQueuedLocks::Client#current_host_id`: `current_hst_id`, `hst_id`;
9
+ ### Changed
10
+ - bumped ruby development version to 3.4.5;
11
+ - bumped development dependencies;
12
+ - (**internal**): `Process.clock_gettime` explicit invocations is moved to the `RedisQueuedLocks::Utils`;
13
+
14
+ ## [1.13.0] - 2025-06-07
2
15
  ### Changed
3
16
  - Updated development dependencies (`armitage-rubocop`, `rbs`, `steep`);
4
17
  - Bumped development Ruby version (from `3.3.6` to `3.4.3`);
@@ -24,7 +37,7 @@
24
37
  - Test coverage (via `simplecov` with `html` and `lcov` formats). `minimum_coverage` config is temporary disabled (and the CI step is not configured yet) cuz we need to refactor tests in first;
25
38
  - CI:
26
39
  - `rspec-retry` is temporary added until the tests are fully refactored;
27
- - typecheck steps: `TypeCheck (Static)` (based on `steep` checks) and `TypeCheckl (Runtime)` (based on `RBS` runtime type checking/testing);
40
+ - typecheck steps: `TypeCheck (Static)` (based on `steep` checks) and `TypeCheck (Runtime)` (based on `RBS` runtime type checking/testing);
28
41
  - Support for `ActiveSupport::BroadcastLogger` logger instances;
29
42
 
30
43
  ## [1.12.1]
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
- # RedisQueuedLocks · [![Gem Version](https://badge.fury.io/rb/redis_queued_locks.svg)](https://badge.fury.io/rb/redis_queued_locks)
1
+ # RedisQueuedLocks · ![Gem Version](https://img.shields.io/gem/v/redis_queued_locks)
2
2
 
3
- [![Tests (RSpec)](https://github.com/0exp/redis_queued_locks/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/0exp/redis_queued_locks/actions) [![Lint (Rubocop/RBS)](https://github.com/0exp/redis_queued_locks/actions/workflows/lint.yml/badge.svg?branch=master)](https://github.com/0exp/redis_queued_locks/actions) [![TypeCheck (Runtime) [RBS]](https://github.com/0exp/redis_queued_locks/actions/workflows/typecheck-runtime.yml/badge.svg?branch=master)](https://github.com/0exp/redis_queued_locks/actions) [![TypeCheck (Static) [Steep]](https://github.com/0exp/redis_queued_locks/actions/workflows/typecheck-static.yml/badge.svg?branch=master)](https://github.com/0exp/redis_queued_locks/actions)
3
+ [![Tests (RSpec)](https://github.com/0exp/redis_queued_locks/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/0exp/redis_queued_locks/actions) [![Lint (Rubocop)](https://github.com/0exp/redis_queued_locks/actions/workflows/lint.yml/badge.svg?branch=master)](https://github.com/0exp/redis_queued_locks/actions) [![TypeCheck (Runtime/RBS)](https://github.com/0exp/redis_queued_locks/actions/workflows/typecheck-runtime.yml/badge.svg?branch=master)](https://github.com/0exp/redis_queued_locks/actions) [![TypeCheck (Static/Steep)](https://github.com/0exp/redis_queued_locks/actions/workflows/typecheck-static.yml/badge.svg?branch=master)](https://github.com/0exp/redis_queued_locks/actions)
4
4
 
5
- <a href="https://redis.io/docs/manual/patterns/distributed-locks/">Distributed locks</a> with "prioritized lock acquisition queue" capabilities based on the Redis Database.
5
+ <a href="https://redis.io/docs/latest/develop/clients/patterns/distributed-locks/">Distributed locks</a> with "prioritized lock acquisition queue" capabilities based on the Redis Database.
6
6
 
7
- Each lock request is put into the request queue (each lock is hosted by it's own queue separately from other queues) and processed in order of their priority (FIFO). Each lock request lives some period of time (RTTL) (with requeue capabilities) which guarantees the request queue will never be stacked.
7
+ Each lock request is put into the request queue (each lock is hosted by its own queue separately from other queues) and processed in order of their priority (FIFO). Each lock request lives some period of time (RTTL) (with requeue capabilities) which guarantees the request queue will never be stacked.
8
8
 
9
9
  In addition to the classic `queued` (FIFO) strategy RQL supports `random` (RANDOM) lock obtaining strategy when any acquirer from the lock queue can obtain the lock regardless the position in the queue.
10
10
 
@@ -27,8 +27,10 @@ Provides flexible invocation flow, parametrized limits (lock request ttl, lock t
27
27
  - [queue_info](#queue_info)
28
28
  - [locked?](#locked)
29
29
  - [queued?](#queued)
30
- - [unlock](#unlock---release-a-lock)
31
- - [clear_locks](#clear_locks---release-all-locks-and-lock-queues)
30
+ - [unlock](#unlock---release-a-lock) (aka `release_lock`)
31
+ - [clear_locks](#clear_locks---release-all-locks-and-lock-queues) (aka `release_locks`)
32
+ - [clear_locks_of](#clear_locks_of) (aka `release_locks_of`)
33
+ - [clear_current_locks](#clear_current_locks) (aka `release_current_locks`)
32
34
  - [extend_lock_ttl](#extend_lock_ttl)
33
35
  - [locks](#locks---get-list-of-obtained-locks)
34
36
  - [queues](#queues---get-list-of-lock-request-queues)
@@ -62,7 +64,16 @@ Provides flexible invocation flow, parametrized limits (lock request ttl, lock t
62
64
  - [Instrumentation](#instrumentation)
63
65
  - [Instrumentation Configuration](#instrumentation-configuration)
64
66
  - [Instrumentation Events](#instrumentation-events)
67
+ - ["redis_queued_locks.lock_obtained"](#redis_queued_lockslock_hold_and_release)
68
+ - ["redis_queued_locks.extendable_reentrant_lock_obtained"](#redis_queued_locksextendable_reentrant_lock_obtained)
69
+ - ["redis_queued_locks.reentrant_lock_obtained"](#redis_queued_locksreentrant_lock_obtained)
70
+ - ["redis_queued_locks.lock_hold_and_release"](#redis_queued_lockslock_hold_and_release)
71
+ - ["redis_queued_locks.reentrant_lock_hold_completes"](#redis_queued_locksreentrant_lock_hold_completes)
72
+ - ["redis_queued_locks.explicit_lock_release"](#redis_queued_locksexplicit_lock_release)
73
+ - ["redis_queued_locks.explicit_all_locks_release"](#redis_queued_locksexplicit_all_locks_release)
74
+ - ["redis_queued_locks.release_locks_of"](#redis_queued_locksrelease_locks_of)
65
75
  - [Roadmap](#roadmap)
76
+ - [Build and Develop](#build-and-develop)
66
77
  - [Contributing](#contributing)
67
78
  - [License](#license)
68
79
  - [Authors](#authors)
@@ -151,7 +162,7 @@ rq_lock_client.lock("some-lock") { puts "Hello, lock!" }
151
162
  ```ruby
152
163
  redis_client = RedisClient.config.new_pool # NOTE: provide your own RedisClient instance
153
164
 
154
- clinet = RedisQueuedLocks::Client.new(redis_client) do |config|
165
+ client = RedisQueuedLocks::Client.new(redis_client) do |config|
155
166
  # (default: 3) (supports nil)
156
167
  # - nil means "infinite retries" and you are only limited by the "try_to_lock_timeout" config;
157
168
  config['retry_count'] = 3
@@ -223,7 +234,7 @@ clinet = RedisQueuedLocks::Client.new(redis_client) do |config|
223
234
  # - `:extendable_work_through` - continue working under the lock <with> lock's TTL extension;
224
235
  # - `:wait_for_lock` - (default) - work in classic way (with timeouts, retry delays, retry limits, etc - in classic way :));
225
236
  # - `:dead_locking` - fail with deadlock exception;
226
- # - See "Dead locks and Reentrant Locks" documentation section in REDME.md for details;
237
+ # - See "Dead locks and Reentrant Locks" documentation section in README.md for details;
227
238
  config['default_conflict_strategy'] = :wait_for_lock
228
239
 
229
240
  # (default: 100)
@@ -231,10 +242,21 @@ clinet = RedisQueuedLocks::Client.new(redis_client) do |config|
231
242
  # - affects the performance of your Redis and Ruby Application (configure thoughtfully);
232
243
  config['lock_release_batch_size'] = 100
233
244
 
245
+ # (default: 300)
246
+ # - how many items will be released at a time in #clear_locks_of and in #clear_current_locks methods (uses SCAN for batch extraction and DEL for batch deletion);
247
+ # - affects the ruby's memory (cuz this batch will be stored in Set object in first, and then will be splatted to the DEL redis method invocation) (configure thoughtfully);
248
+ # - affects the performance of your Redis (configure thoughtfully);
249
+ config['clear_locks_of__lock_scan_size'] = 300
250
+
251
+ # (default: 300)
252
+ # - how many queues will be SCAN'ned at a time in #clear_locks_of and in #clear_current_locks methods (uses SCAN);
253
+ # - affects the performance of your Redis (configure thoughtfully);
254
+ config['clear_locks_of__queue_scan_size'] = 300
255
+
234
256
  # (default: 500)
235
257
  # - how many items should be extracted from redis during the #locks, #queues, #keys
236
258
  # #locks_info, and #queues_info operations (uses SCAN);
237
- # - affects the performance of your Redis and Ruby Application (configure thoughtfully;)
259
+ # - affects the performance of your Redis and Ruby Application (configure thoughtfully);
238
260
  config['key_extraction_batch_size'] = 500
239
261
 
240
262
  # (default: 1 day)
@@ -735,7 +757,7 @@ rql.lock("my_lock", queue_ttl: 5, timeout: 10_000, retry_count: nil)
735
757
  ```
736
758
 
737
759
  - obtain a lock in `:random` way (with `:random` strategy): in `:random` strategy
738
- any acquirer from the lcok queue can obtain the lock regardless of the position in the lock queue;
760
+ any acquirer from the lock queue can obtain the lock regardless of the position in the lock queue;
739
761
 
740
762
  ```ruby
741
763
  # Current Process (process#1)
@@ -1120,6 +1142,197 @@ rql.clear_locks
1120
1142
 
1121
1143
  ---
1122
1144
 
1145
+ #### #clear_locks_of
1146
+
1147
+ <sup>\[[back to top](#usage)\]</sup>
1148
+
1149
+ - release all locks of the passed acquirer/host and remove this acquirer/host from all queues (locks will be released first)
1150
+ - this is a cleanup helper intended for operational and debugging scenarios (for example: your
1151
+ current puma request thread is killed by Rack::Timeout and you need to cleanup all zombie RQL
1152
+ locks and lock reuqests obtained during the request processing);
1153
+ - acquirer/host dentifiers can be extracted via:
1154
+ - `#current_host_id`
1155
+ - `#current_acquirer_id`
1156
+ - `#possible_host_ids`
1157
+ - from lock data (extracted via `#lock_info`, `#locks_info`, `#queue_info`, `#queues_info`);
1158
+ - produces `"redis_queued_locks.release_locks_of"` instrumentation event;
1159
+ - accepts:
1160
+ - `:host_id` - (required) `[String]`
1161
+ - host indentifier whose locks we want to release and which we want to remove from all locks queues;
1162
+ - `:acquirer_id` - (required) `[String]`
1163
+ - acquirer indentifier whose locks we want to release and which we want to remove from all locks queues;
1164
+ - `:lock_scan_size` - (optional) `[Integer]`
1165
+ - how many items will be released at a time (uses `SCAN `for batch extraction and `DEL` for batch deletion);
1166
+ - pre-configuerd value in `config['clear_locks_of__lock_scan_size']`;
1167
+ - `:queue_scan_size` - (optional) `[Integer]`
1168
+ - how many queues will be SCAN'ned at a time (uses `SCAN)`;
1169
+ - pre-configuerd value in `config['clear_locks_of__queue_scan_size']`;
1170
+ - `:logger` - (optional) `[::Logger,#debug]`
1171
+ - custom logger object;
1172
+ - pre-configured value in `config['logger']`;
1173
+ - `:instrumenter` - (optional) `[#notify]`
1174
+ - custom instrumenter object;
1175
+ - pre-configured value in `config['isntrumenter']`;
1176
+ - `:instrument` - (optional) `[NilClass,Any]`
1177
+ - custom instrumentation data wich will be passed to the instrumenter's payload with `:instrument` key;
1178
+ - `:log_sampling_enabled` - (optional) `[Boolean]`
1179
+ - enables **log sampling**;
1180
+ - pre-configured in `config['log_sampling_enabled']`;
1181
+ - `:log_sampling_percent` - (optional) `[Integer]`
1182
+ - **log sampling**:the percent of cases that should be logged;
1183
+ - pre-configured in `config['log_sampling_percent']`;
1184
+ - `:log_sampler` - (optional) `[#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]`
1185
+ - **log sampling**: percent-based log sampler that decides should be RQL case logged or not;
1186
+ - pre-configured in `config['log_sampler']`;
1187
+ - `log_sample_this` - (optional) `[Boolean]`
1188
+ - marks the method that everything should be logged despite the enabled log sampling;
1189
+ - makes sense when log sampling is enabled;
1190
+ - `false` by default;
1191
+ - `:instr_sampling_enabled` - (optional) `[Boolean]`
1192
+ - enables **instrumentaion sampling**;
1193
+ - pre-configured in `config['instr_sampling_enabled']`;
1194
+ - `instr_sampling_percent` - (optional) `[Integer]`
1195
+ - the percent of cases that should be instrumented;
1196
+ - pre-configured in `config['instr_sampling_percent']`;
1197
+ - `instr_sampler` - (optional) `[#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]`
1198
+ - percent-based log sampler that decides should be RQL case instrumented or not;
1199
+ - pre-configured in `config['instr_sampler']`;
1200
+ - `instr_sample_this` - (optional) `[Boolean]`
1201
+ - marks the method that everything should be instrumneted despite the enabled instrumentation sampling;
1202
+ - makes sense when instrumentation sampling is enabled;
1203
+ - `false` by default;
1204
+ - returns:
1205
+ - `[Hash<Symbol,Numeric>]` - Format: `{ ok: true, result: Hash<Symbol,Numeric> }`;
1206
+ - result data:
1207
+ - `:rel_time` - `Numeric` - time spent;
1208
+ - `:rel_key_cnt` - `Integer` - the count of released locks;
1209
+ - `:tch_queue_cnt` - `Integer` - the count of touched (modified) queues (the number of queues from which the acquirer/host was removed);
1210
+
1211
+ ```ruby
1212
+ rql.clear_locks_of(
1213
+ host_id: "rql:hst:41478/4320/4360/848818f09d8c3420",
1214
+ acquirer_id: "rql:acq:41478/4320/4340/4360/848818f09d8c3420"
1215
+ )
1216
+ # -- or (alias) --
1217
+ rql.release_locks_of(
1218
+ host_id: "rql:hst:41478/4320/4360/848818f09d8c3420",
1219
+ acquirer_id: "rql:acq:41478/4320/4340/4360/848818f09d8c3420"
1220
+ )
1221
+
1222
+ # -> result
1223
+ {
1224
+ ok: true,
1225
+ result: {
1226
+ rel_key_cnt: 123,
1227
+ tch_queue_cnt: 2,
1228
+ rel_time: 0.1
1229
+ }
1230
+ }
1231
+ ```
1232
+
1233
+ ```ruby
1234
+ # clear locks and queues of the current acquirer:
1235
+ rql.clear_locks_of(
1236
+ host_id: rql.current_host_id,
1237
+ acquirer_id: rql.current_acquirer_id
1238
+ )
1239
+
1240
+ # equivalent:
1241
+ rql.clear_current_locks
1242
+
1243
+ # -> result
1244
+ {
1245
+ ok: true,
1246
+ result: {
1247
+ rel_key_cnt: 43,
1248
+ tch_queue_cnt: 3,
1249
+ rel_time: 0.2
1250
+ }
1251
+ }
1252
+ ```
1253
+
1254
+ ---
1255
+
1256
+ #### #clear_current_locks
1257
+
1258
+ <sup>\[[back to top](#usage)\]</sup>
1259
+
1260
+ - release all locks of the current acquirer/host and remove the current acquirer/host from all queues (locks will be released first);
1261
+ - this is a cleanup helper intended for operational and debugging scenarios (for example: your
1262
+ current puma request thread is killed by Rack::Timeout and you need to cleanup all zombie RQL
1263
+ locks and lock reuqests obtained during the request processing);
1264
+ - representes an equivalent invocation:
1265
+ ```ruby
1266
+ rql.clear_locks_of(host_id: rql.current_host_id, acquirer_id: rql.current_acquirer_id)
1267
+ ```
1268
+ - produces `"redis_queued_locks.release_locks_of"` instrumentation event;
1269
+ - `:lock_scan_size` - (optional) `[Integer]`
1270
+ - how many items will be released at a time (uses `SCAN `for batch extraction and `DEL` for batch deletion);
1271
+ - pre-configuerd value in `config['clear_locks_of__lock_scan_size']`;
1272
+ - `:queue_scan_size` - (optional) `[Integer]`
1273
+ - how many queues will be SCAN'ned at a time (uses `SCAN)`;
1274
+ - pre-configuerd value in `config['clear_locks_of__queue_scan_size']`;
1275
+ - `:logger` - (optional) `[::Logger,#debug]`
1276
+ - custom logger object;
1277
+ - pre-configured value in `config['logger']`;
1278
+ - `:instrumenter` - (optional) `[#notify]`
1279
+ - custom instrumenter object;
1280
+ - pre-configured value in `config['isntrumenter']`;
1281
+ - `:instrument` - (optional) `[NilClass,Any]`
1282
+ - custom instrumentation data wich will be passed to the instrumenter's payload with `:instrument` key;
1283
+ - `:log_sampling_enabled` - (optional) `[Boolean]`
1284
+ - enables **log sampling**;
1285
+ - pre-configured in `config['log_sampling_enabled']`;
1286
+ - `:log_sampling_percent` - (optional) `[Integer]`
1287
+ - **log sampling**:the percent of cases that should be logged;
1288
+ - pre-configured in `config['log_sampling_percent']`;
1289
+ - `:log_sampler` - (optional) `[#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]`
1290
+ - **log sampling**: percent-based log sampler that decides should be RQL case logged or not;
1291
+ - pre-configured in `config['log_sampler']`;
1292
+ - `log_sample_this` - (optional) `[Boolean]`
1293
+ - marks the method that everything should be logged despite the enabled log sampling;
1294
+ - makes sense when log sampling is enabled;
1295
+ - `false` by default;
1296
+ - `:instr_sampling_enabled` - (optional) `[Boolean]`
1297
+ - enables **instrumentaion sampling**;
1298
+ - pre-configured in `config['instr_sampling_enabled']`;
1299
+ - `instr_sampling_percent` - (optional) `[Integer]`
1300
+ - the percent of cases that should be instrumented;
1301
+ - pre-configured in `config['instr_sampling_percent']`;
1302
+ - `instr_sampler` - (optional) `[#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]`
1303
+ - percent-based log sampler that decides should be RQL case instrumented or not;
1304
+ - pre-configured in `config['instr_sampler']`;
1305
+ - `instr_sample_this` - (optional) `[Boolean]`
1306
+ - marks the method that everything should be instrumneted despite the enabled instrumentation sampling;
1307
+ - makes sense when instrumentation sampling is enabled;
1308
+ - `false` by default;
1309
+ - returns:
1310
+ - `[Hash<Symbol,Numeric>]` - Format: `{ ok: true, result: Hash<Symbol,Numeric> }`;
1311
+ - result data:
1312
+ - `:rel_time` - `Numeric` - time spent;
1313
+ - `:rel_key_cnt` - `Integer` - the count of released locks;
1314
+ - `:tch_queue_cnt` - `Integer` - the count of touched (modified) queues (the number of queues from which the acquirer/host was removed);
1315
+
1316
+ ```ruby
1317
+ rql.clear_current_locks
1318
+ # -- or (alias) --
1319
+ rql.release_current_locks
1320
+ # equivalent:
1321
+ rql.release_locks_of(host_id: rql.current_host_id, acquirer_id: rql.current_acquirer_id)
1322
+
1323
+ # -> result
1324
+ {
1325
+ ok: true,
1326
+ result: {
1327
+ rel_key_cnt: 43,
1328
+ tch_queue_cnt: 3,
1329
+ rel_time: 0.2
1330
+ }
1331
+ }
1332
+ ```
1333
+
1334
+ ---
1335
+
1123
1336
  #### #extend_lock_ttl
1124
1337
 
1125
1338
  <sup>\[[back to top](#usage)\]</sup>
@@ -1320,7 +1533,7 @@ rql.locks_info # or rql.locks_info(scan_size: 123)
1320
1533
  :status=>:alive,
1321
1534
  :info=>{
1322
1535
  "acq_id"=>"rql:acq:41478/4320/4340/4360/848818f09d8c3420",
1323
- "hst_id"=>"rql:hst:41478/4320/4360/848818f09d8c3420"
1536
+ "hst_id"=>"rql:hst:41478/4320/4360/848818f09d8c3420",
1324
1537
  "ts"=>1711607112.670343,
1325
1538
  "ini_ttl"=>15000,
1326
1539
  "rem_ttl"=>13998}},
@@ -1445,6 +1658,7 @@ rql.clear_dead_requests(dead_ttl: 60 * 60 * 1000) # 1 hour in milliseconds
1445
1658
 
1446
1659
  <sup>\[[back to top](#usage)\]</sup>
1447
1660
 
1661
+ - (aliases: `#current_acq_id`, `#acq_id`);
1448
1662
  - get the current acquirer identifier in RQL notation that you can use for debugging purposes during the lock analyzation;
1449
1663
  - acquirer identifier format:
1450
1664
  ```ruby
@@ -1482,6 +1696,7 @@ rql.current_acquirer_id
1482
1696
 
1483
1697
  <sup>\[[back to top](#usage)\]</sup>
1484
1698
 
1699
+ - (aliases: `#current_hst_id`, `#hst_id`);
1485
1700
  - get a current host identifier in RQL notation that you can use for debugging purposes during the lock analyzis;
1486
1701
  - the host is a ruby worker (a combination of process/thread/ractor/identity) that is alive and can obtain locks;
1487
1702
  - the host is limited to `process`/`thread`/`ractor` (without `fiber`) combination cuz we have no abilities to extract
@@ -1907,17 +2122,16 @@ config['log_sampler'] = RedisQueuedLocks::Logging::Sampler
1907
2122
 
1908
2123
  An instrumentation layer is incapsulated in `instrumenter` object stored in [config](#configuration) (`RedisQueuedLocks::Client#config['instrumenter']`).
1909
2124
 
1910
- Instrumentation can be sampled. See [Instrumentation Configuration](#instrumentation-configuration) section for details.
2125
+ Instrumentation can be **sampled**. See [Instrumentation Configuration](#instrumentation-configuration) section for details.
1911
2126
 
1912
2127
  Instrumenter object should provide `notify(event, payload)` method with the following signarue:
1913
2128
 
1914
2129
  - `event` - `string`;
1915
2130
  - `payload` - `hash<Symbol,Any>`;
1916
2131
 
1917
- `redis_queued_locks` provides two instrumenters:
2132
+ **RQL** provides two instrumenters:
1918
2133
 
1919
- - `RedisQueuedLocks::Instrument::ActiveSupport` - **ActiveSupport::Notifications** instrumenter
1920
- that instrument events via **ActiveSupport::Notifications** API;
2134
+ - `RedisQueuedLocks::Instrument::ActiveSupport` - **ActiveSupport::Notifications** instrumenter that instrument events via **ActiveSupport::Notifications** API;
1921
2135
  - `RedisQueuedLocks::Instrument::VoidNotifier` - instrumenter that does nothing;
1922
2136
 
1923
2137
  By default `RedisQueuedLocks::Client` is configured with the void notifier (which means "instrumentation is disabled").
@@ -1972,95 +2186,113 @@ config['instr_sampler'] = RedisQueuedLocks::Instrument::Sampler
1972
2186
 
1973
2187
  List of instrumentation events
1974
2188
 
1975
- - `redis_queued_locks.lock_obtained`;
1976
- - `redis_queued_locks.extendable_reentrant_lock_obtained`;
1977
- - `redis_queued_locks.reentrant_lock_obtained`;
1978
- - `redis_queued_locks.lock_hold_and_release`;
1979
- - `redis_queued_locks.reentrant_lock_hold_completes`;
1980
- - `redis_queued_locks.explicit_lock_release`;
1981
- - `redis_queued_locks.explicit_all_locks_release`;
2189
+ - ["redis_queued_locks.lock_obtained"](#redis_queued_lockslock_hold_and_release)
2190
+ - ["redis_queued_locks.extendable_reentrant_lock_obtained"](#redis_queued_locksextendable_reentrant_lock_obtained)
2191
+ - ["redis_queued_locks.reentrant_lock_obtained"](#redis_queued_locksreentrant_lock_obtained)
2192
+ - ["redis_queued_locks.lock_hold_and_release"](#redis_queued_lockslock_hold_and_release)
2193
+ - ["redis_queued_locks.reentrant_lock_hold_completes"](#redis_queued_locksreentrant_lock_hold_completes)
2194
+ - ["redis_queued_locks.explicit_lock_release"](#redis_queued_locksexplicit_lock_release)
2195
+ - ["redis_queued_locks.explicit_all_locks_release"](#redis_queued_locksexplicit_all_locks_release)
2196
+ - ["redis_queued_locks.release_locks_of"](#redis_queued_locksrelease_locks_of)
1982
2197
 
1983
2198
  Detalized event semantics and payload structure:
1984
2199
 
1985
- - `"redis_queued_locks.lock_obtained"`
1986
- - a moment when the lock was obtained;
1987
- - raised from `#lock`/`#lock!`;
1988
- - payload:
1989
- - `:ttl` - `integer`/`milliseconds` - lock ttl;
1990
- - `:acq_id` - `string` - lock acquirer identifier;
1991
- - `:hst_id` - `string` - lock's host identifier;
1992
- - `:lock_key` - `string` - lock name;
1993
- - `:ts` - `numeric`/`epoch` - the time when the lock was obtaiend;
1994
- - `:acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
1995
- - `:instrument` - `nil`/`Any` - custom data passed to the `#lock`/`#lock!` method as `:instrument` attribute;
1996
-
1997
- - `"redis_queued_locks.extendable_reentrant_lock_obtained"`
1998
- - an event signalizes about the "extendable reentrant lock" obtaining is happened;
1999
- - raised from `#lock`/`#lock!` when the lock was obtained as reentrant lock;
2000
- - payload:
2001
- - `:lock_key` - `string` - lock name;
2002
- - `:ttl` - `integer`/`milliseconds` - last lock ttl by reentrant locking;
2003
- - `:acq_id` - `string` - lock acquirer identifier;
2004
- - `:hst_id` - `string` - lock's host identifier;
2005
- - `:ts` - `numeric`/`epoch` - the time when the lock was obtaiend as extendable reentrant lock;
2006
- - `:acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
2007
- - `:instrument` - `nil`/`Any` - custom data passed to the `#lock`/`#lock!` method as `:instrument` attribute;
2008
-
2009
- - `"redis_queued_locks.reentrant_lock_obtained"`
2010
- - an event signalizes about the "reentrant lock" obtaining is happened (without TTL extension);
2011
- - raised from `#lock`/`#lock!` when the lock was obtained as reentrant lock;
2012
- - payload:
2013
- - `:lock_key` - `string` - lock name;
2014
- - `:ttl` - `integer`/`milliseconds` - last lock ttl by reentrant locking;
2015
- - `:acq_id` - `string` - lock acquirer identifier;
2016
- - `:hst_id` - `string` - lock's host identifier;
2017
- - `:ts` - `numeric`/`epoch` - the time when the lock was obtaiend as reentrant lock;
2018
- - `:acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
2019
- - `:instrument` - `nil`/`Any` - custom data passed to the `#lock`/`#lock!` method as `:instrument` attribute;
2020
-
2021
- - `"redis_queued_locks.lock_hold_and_release"`
2022
- - an event signalizes about the "hold+and+release" process is finished;
2023
- - raised from `#lock`/`#lock!` when invoked with a block of code;
2024
- - payload:
2025
- - `:hold_time` - `float`/`milliseconds` - lock hold time;
2026
- - `:ttl` - `integer`/`milliseconds` - lock ttl;
2027
- - `:acq_id` - `string` - lock acquirer identifier;
2028
- - `:hst_id` - `string` - lock's host identifier;
2029
- - `:lock_key` - `string` - lock name;
2030
- - `:ts` - `numeric`/`epoch` - the time when lock was obtained;
2031
- - `:acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
2032
- - `:instrument` - `nil`/`Any` - custom data passed to the `#lock`/`#lock!` method as `:instrument` attribute;
2033
-
2034
- - `"redis_queued_locks.reentrant_lock_hold_completes"`
2035
- - an event signalizes about the "reentrant lock hold" is complete (both extendable and non-extendable);
2036
- - lock re-entering can happen many times and this event happens for each of them separately;
2037
- - raised from `#lock`/`#lock!` when the lock was obtained as reentrant lock;
2038
- - payload:
2039
- - `:hold_time` - `float`/`milliseconds` - lock hold time;
2040
- - `:ttl` - `integer`/`milliseconds` - last lock ttl by reentrant locking;
2041
- - `:acq_id` - `string` - lock acquirer identifier;
2042
- - `:hst_id` - `string` - lock's host identifier;
2043
- - `:ts` - `numeric`/`epoch` - the time when the lock was obtaiend as reentrant lock;
2044
- - `:lock_key` - `string` - lock name;
2045
- - `:acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
2046
- - `:instrument` - `nil`/`Any` - custom data passed to the `#lock`/`#lock!` method as `:instrument` attribute;
2047
-
2048
- - `"redis_queued_locks.explicit_lock_release"`
2049
- - an event signalizes about the explicit lock release (invoked via `RedisQueuedLock#unlock`);
2050
- - raised from `#unlock`;
2051
- - payload:
2052
- - `:at` - `float`/`epoch` - the time when the lock was released;
2053
- - `:rel_time` - `float`/`milliseconds` - time spent on lock releasing;
2054
- - `:lock_key` - `string` - released lock (lock name);
2055
- - `:lock_key_queue` - `string` - released lock queue (lock queue name);
2056
-
2057
- - `"redis_queued_locks.explicit_all_locks_release"`
2058
- - an event signalizes about the explicit all locks release (invoked via `RedisQueuedLock#clear_locks`);
2059
- - raised from `#clear_locks`;
2060
- - payload:
2061
- - `:rel_time` - `float`/`milliseconds` - time spent on "realese all locks" operation;
2062
- - `:at` - `float`/`epoch` - the time when the operation has ended;
2063
- - `:rel_keys` - `integer` - released redis keys count (`released queue keys` + `released lock keys`);
2200
+ ##### `"redis_queued_locks.lock_obtained"`
2201
+ - <sup>\[[back to the list](#instrumentation-events)\]</sup>
2202
+ - a moment when the lock was obtained;
2203
+ - raised from `#lock`/`#lock!`;
2204
+ - payload:
2205
+ - `:ttl` - `integer`/`milliseconds` - lock ttl;
2206
+ - `:acq_id` - `string` - lock acquirer identifier;
2207
+ - `:hst_id` - `string` - lock's host identifier;
2208
+ - `:lock_key` - `string` - lock name;
2209
+ - `:ts` - `numeric`/`epoch` - the time when the lock was obtaiend;
2210
+ - `:acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
2211
+ - `:instrument` - `nil`/`Any` - custom data passed to the `#lock`/`#lock!` method as `:instrument` attribute;
2212
+
2213
+ ##### `"redis_queued_locks.extendable_reentrant_lock_obtained"`
2214
+ - <sup>\[[back to the list](#instrumentation-events)\]</sup>
2215
+ - an event signalizes about the "extendable reentrant lock" obtaining is happened;
2216
+ - raised from `#lock`/`#lock!` when the lock was obtained as reentrant lock;
2217
+ - payload:
2218
+ - `:lock_key` - `string` - lock name;
2219
+ - `:ttl` - `integer`/`milliseconds` - last lock ttl by reentrant locking;
2220
+ - `:acq_id` - `string` - lock acquirer identifier;
2221
+ - `:hst_id` - `string` - lock's host identifier;
2222
+ - `:ts` - `numeric`/`epoch` - the time when the lock was obtaiend as extendable reentrant lock;
2223
+ - `:acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
2224
+ - `:instrument` - `nil`/`Any` - custom data passed to the `#lock`/`#lock!` method as `:instrument` attribute;
2225
+
2226
+ ##### `"redis_queued_locks.reentrant_lock_obtained"`
2227
+ - <sup>\[[back to the list](#instrumentation-events)\]</sup>
2228
+ - an event signalizes about the "reentrant lock" obtaining is happened (without TTL extension);
2229
+ - raised from `#lock`/`#lock!` when the lock was obtained as reentrant lock;
2230
+ - payload:
2231
+ - `:lock_key` - `string` - lock name;
2232
+ - `:ttl` - `integer`/`milliseconds` - last lock ttl by reentrant locking;
2233
+ - `:acq_id` - `string` - lock acquirer identifier;
2234
+ - `:hst_id` - `string` - lock's host identifier;
2235
+ - `:ts` - `numeric`/`epoch` - the time when the lock was obtaiend as reentrant lock;
2236
+ - `:acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
2237
+ - `:instrument` - `nil`/`Any` - custom data passed to the `#lock`/`#lock!` method as `:instrument` attribute;
2238
+
2239
+ ##### `"redis_queued_locks.lock_hold_and_release"`
2240
+ - <sup>\[[back to the list](#instrumentation-events)\]</sup>
2241
+ - an event signalizes about the "hold+and+release" process is finished;
2242
+ - raised from `#lock`/`#lock!` when invoked with a block of code;
2243
+ - payload:
2244
+ - `:hold_time` - `float`/`milliseconds` - lock hold time;
2245
+ - `:ttl` - `integer`/`milliseconds` - lock ttl;
2246
+ - `:acq_id` - `string` - lock acquirer identifier;
2247
+ - `:hst_id` - `string` - lock's host identifier;
2248
+ - `:lock_key` - `string` - lock name;
2249
+ - `:ts` - `numeric`/`epoch` - the time when lock was obtained;
2250
+ - `:acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
2251
+ - `:instrument` - `nil`/`Any` - custom data passed to the `#lock`/`#lock!` method as `:instrument` attribute;
2252
+
2253
+ ##### `"redis_queued_locks.reentrant_lock_hold_completes"`
2254
+ - <sup>\[[back to the list](#instrumentation-events)\]</sup>
2255
+ - an event signalizes about the "reentrant lock hold" is complete (both extendable and non-extendable);
2256
+ - lock re-entering can happen many times and this event happens for each of them separately;
2257
+ - raised from `#lock`/`#lock!` when the lock was obtained as reentrant lock;
2258
+ - payload:
2259
+ - `:hold_time` - `float`/`milliseconds` - lock hold time;
2260
+ - `:ttl` - `integer`/`milliseconds` - last lock ttl by reentrant locking;
2261
+ - `:acq_id` - `string` - lock acquirer identifier;
2262
+ - `:hst_id` - `string` - lock's host identifier;
2263
+ - `:ts` - `numeric`/`epoch` - the time when the lock was obtaiend as reentrant lock;
2264
+ - `:lock_key` - `string` - lock name;
2265
+ - `:acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
2266
+ - `:instrument` - `nil`/`Any` - custom data passed to the `#lock`/`#lock!` method as `:instrument` attribute;
2267
+
2268
+ ##### `"redis_queued_locks.explicit_lock_release"`
2269
+ - <sup>\[[back to the list](#instrumentation-events)\]</sup>
2270
+ - an event signalizes about the explicit lock release (invoked via `RedisQueuedLock#unlock`);
2271
+ - raised from `#unlock`;
2272
+ - payload:
2273
+ - `:at` - `float`/`epoch` - the time when the lock was released;
2274
+ - `:rel_time` - `float`/`milliseconds` - time spent on lock releasing;
2275
+ - `:lock_key` - `string` - released lock (lock name);
2276
+ - `:lock_key_queue` - `string` - released lock queue (lock queue name);
2277
+
2278
+ ##### `"redis_queued_locks.explicit_all_locks_release"`
2279
+ - <sup>\[[back to the list](#instrumentation-events)\]</sup>
2280
+ - an event signalizes about the explicit all locks release (invoked via `RedisQueuedLock#clear_locks`);
2281
+ - raised from `#clear_locks`;
2282
+ - payload:
2283
+ - `:rel_time` - `float`/`milliseconds` - time spent on "realese all locks" operation;
2284
+ - `:at` - `float`/`epoch` - the time when the operation has ended;
2285
+ - `:rel_keys` - `integer` - released redis keys count (`released queue keys` + `released lock keys`);
2286
+
2287
+ ##### `"redis_queued_locks.release_locks_of"`
2288
+ - <sup>\[[back to the list](#instrumentation-events)\]</sup>
2289
+ - an event signalizes about the released locks of the cocnrete host and acquirer;
2290
+ - raised from `#clear_locks_of` and `#clear_current_locks` (`#release_locks_of` and `#release_current_locks` respectively);
2291
+ - payload:
2292
+ - `:rel_time` - `float`/`milliseconds` - time spent on "release locks of" operation;
2293
+ - `:at` - `float`/`epoch` - the time when the opertaion has ended;
2294
+ - `:rel_key_cnt` - `integer` - released locks count;
2295
+ - `:tch_queue_cnt` - `:integer` - the number of queues from which the cocnrete host/acquirer was removed;
2064
2296
 
2065
2297
  ---
2066
2298
 
@@ -2094,6 +2326,9 @@ Detalized event semantics and payload structure:
2094
2326
  - `write` - waits - `write`;
2095
2327
  - **write** mode is a default behavior for all RQL locks;
2096
2328
  - **Minor**:
2329
+ - add `hst_id` to all methods that works with queues info;
2330
+ - try to return the `fiber object id` to the lock host identifier (we cant use fiber object id cuz `ObjectSpace` has no access to the fiber object space after the any ractor object initialization)
2331
+ - named RQL's threads (`Thread#name`) and RQL's ractors (`Ractor#name`) in order to have an ability to find and work with RQL's threads and ractors outside of RQL logic (stop threads before process forking, for example);
2097
2332
  - `#lock`/`#lock!` - `timeout:` option: support for granular periods (it supports only `seconds` at the moment, but we need `milliseconds`);
2098
2333
  - Add `after timeout hook` ability for timeout errors with an ability to access the binding context (if it possibly) of the thread where it failed;
2099
2334
  - Add `ignore timeout errors` ability (for debug purposes);
@@ -2120,7 +2355,31 @@ Detalized event semantics and payload structure:
2120
2355
  - `unlock_on:`-option in lock/lock! logic in order to support auto-unlocking on any exception rasaied under the invoked block (invoked under the lock);
2121
2356
  - **Research**: support for `Valkey` database backend (https://github.com/valkey-io/valkey) (https://valkey.io/);
2122
2357
  - **Research**: support for `Garnet` database backend (https://microsoft.github.io/) (https://github.com/microsoft/garnet);
2123
- - add a library-level exception, when RQL-related key (required for it's logic) has incompatible type (means: some other program uses our key with their own type and logic and RQL can't work properly);
2358
+ - add a library-level exception, when RQL-related key in Redis (required for it's logic) has incompatible type (means: some other program uses our key with their own type and logic and RQL can't work properly);
2359
+ ---
2360
+
2361
+ ## Build and Develop
2362
+
2363
+ - Tests (`RSpec`):
2364
+ ```shell
2365
+ bundle exec rake rspec
2366
+ ```
2367
+
2368
+ - Linting (`Rubocop`+`RBS`):
2369
+ ```shell
2370
+ bundle exec rake rubocop
2371
+ ```
2372
+
2373
+ - `Static` TypeChecking (`Steep`):
2374
+ ```shell
2375
+ bundle exec rake steep:check
2376
+ ```
2377
+
2378
+ - `Runtime` TypeChecking (`RBS`):
2379
+ ```shell
2380
+ bundle exec rbs collection install && RBS_TEST_RAISE=true RUBYOPT='-rrbs/test/setup' RBS_TEST_OPT='-I sig' RBS_TEST_LOGLEVEL=error RBS_TEST_TARGET='RedisQueuedLocks::*' bundle exec rspec --failure-exit-code=0
2381
+ ```
2382
+
2124
2383
  ---
2125
2384
 
2126
2385
  ## Contributing