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 +4 -4
- data/.rubocop.yml +0 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +14 -1
- data/README.md +361 -102
- data/Rakefile +1 -1
- data/Steepfile +0 -1
- data/lib/redis_queued_locks/acquirer/acquire_lock.rb +7 -7
- data/lib/redis_queued_locks/acquirer/release_all_locks.rb +3 -3
- data/lib/redis_queued_locks/acquirer/release_lock.rb +3 -3
- data/lib/redis_queued_locks/acquirer/release_locks_of.rb +211 -0
- data/lib/redis_queued_locks/acquirer.rb +1 -0
- data/lib/redis_queued_locks/client.rb +155 -2
- data/lib/redis_queued_locks/config/dsl.rb +9 -9
- data/lib/redis_queued_locks/config.rb +15 -10
- data/lib/redis_queued_locks/errors.rb +3 -3
- data/lib/redis_queued_locks/utilities.rb +9 -0
- data/lib/redis_queued_locks/version.rb +2 -2
- data/lib/redis_queued_locks.rb +0 -1
- data/rbs_collection.lock.yaml +1 -13
- data/rbs_collection.yaml +2 -1
- data/sig/manifest.yml +0 -1
- data/sig/redis_queued_locks/acquirer/acquire_lock.rbs +1 -0
- data/sig/redis_queued_locks/acquirer/release_locks_of.rbs +46 -0
- data/sig/redis_queued_locks/client.rbs +40 -0
- data/sig/redis_queued_locks/utilities.rbs +1 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7924e5055147fe7c2e0ff65e59ca7e82dc0ae81ce2c880cced9c914307fb1821
|
|
4
|
+
data.tar.gz: e6921c3d151ae3d227ad130989562f96266954c47c8358ecd82f76afc922c362
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e0b1deaefb5638a96392c759a903661ad3175e45b128e605f0939889c56974c6f6ea2f887884a883fcd8843aec5423bd384a7c8d24644a42ff4e0c6737e0340e
|
|
7
|
+
data.tar.gz: ffaea4bd5b9c57802b99c754e2defb6e16ad9cff3e82855b4a8e6e3f93538ba648dbc84f3cd85200017f47bbc9f4d26a7bbb5d910776c79b19adbc02e5a002ed
|
data/.rubocop.yml
CHANGED
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.4.
|
|
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 `
|
|
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 ·
|
|
1
|
+
# RedisQueuedLocks · 
|
|
2
2
|
|
|
3
|
-
[](https://github.com/0exp/redis_queued_locks/actions) [](https://github.com/0exp/redis_queued_locks/actions) [](https://github.com/0exp/redis_queued_locks/actions) [](https://github.com/0exp/redis_queued_locks/actions) [](https://github.com/0exp/redis_queued_locks/actions)
|
|
4
4
|
|
|
5
|
-
<a href="https://redis.io/docs/
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
-
|
|
1976
|
-
-
|
|
1977
|
-
-
|
|
1978
|
-
-
|
|
1979
|
-
-
|
|
1980
|
-
-
|
|
1981
|
-
-
|
|
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
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
- `
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
- `
|
|
2022
|
-
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
- `
|
|
2035
|
-
-
|
|
2036
|
-
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
- `
|
|
2049
|
-
-
|
|
2050
|
-
-
|
|
2051
|
-
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
-
|
|
2058
|
-
-
|
|
2059
|
-
-
|
|
2060
|
-
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
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
|