redis_queued_locks 1.20.0 → 1.22.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/.claude/project-overview.md +249 -36
- data/.claude/rules/acquirer.md +16 -14
- data/.claude/rules/arguments.md +2 -2
- data/.claude/rules/changelog.md +27 -0
- data/.claude/rules/git.md +1 -1
- data/.claude/rules/logic.md +34 -13
- data/.claude/rules/swarm.md +4 -5
- data/.claude/rules/tests.md +9 -6
- data/.claude/rules/type-checking.md +13 -14
- data/.claude/rules/visitors.md +13 -14
- data/.claude/skills/rql-code-review/SKILL.md +134 -43
- data/CHANGELOG.md +105 -125
- data/CLAUDE.md +11 -9
- data/README.md +501 -136
- data/Rakefile +11 -0
- data/lib/redis_queued_locks/acquirer/acquire_lock/dequeue_from_lock_queue/log_visitor.rb +1 -0
- data/lib/redis_queued_locks/acquirer/acquire_lock/instr_visitor.rb +68 -10
- data/lib/redis_queued_locks/acquirer/acquire_lock/log_visitor.rb +82 -15
- data/lib/redis_queued_locks/acquirer/acquire_lock/try_to_lock/log_visitor.rb +92 -14
- data/lib/redis_queued_locks/acquirer/acquire_lock/try_to_lock.rb +441 -63
- data/lib/redis_queued_locks/acquirer/acquire_lock/with_acq_timeout.rb +2 -2
- data/lib/redis_queued_locks/acquirer/acquire_lock/yield_expire/log_visitor.rb +1 -0
- data/lib/redis_queued_locks/acquirer/acquire_lock/yield_expire.rb +5 -5
- data/lib/redis_queued_locks/acquirer/acquire_lock.rb +176 -39
- data/lib/redis_queued_locks/acquirer/clear_dead_requests.rb +322 -49
- data/lib/redis_queued_locks/acquirer/extend_lock_ttl.rb +125 -13
- data/lib/redis_queued_locks/acquirer/is_locked.rb +0 -1
- data/lib/redis_queued_locks/acquirer/is_queued.rb +0 -1
- data/lib/redis_queued_locks/acquirer/keys.rb +16 -3
- data/lib/redis_queued_locks/acquirer/lock_info.rb +31 -39
- data/lib/redis_queued_locks/acquirer/lock_series_poc/instr_visitor.rb +11 -5
- data/lib/redis_queued_locks/acquirer/lock_series_poc/log_visitor.rb +20 -10
- data/lib/redis_queued_locks/acquirer/lock_series_poc.rb +105 -36
- data/lib/redis_queued_locks/acquirer/locks.rb +32 -11
- data/lib/redis_queued_locks/acquirer/queue_info.rb +30 -8
- data/lib/redis_queued_locks/acquirer/queues.rb +26 -6
- data/lib/redis_queued_locks/acquirer/reindex_locks.rb +117 -4
- data/lib/redis_queued_locks/acquirer/release_all_locks.rb +37 -6
- data/lib/redis_queued_locks/acquirer/release_lock.rb +33 -7
- data/lib/redis_queued_locks/acquirer/release_locks_of.rb +45 -6
- data/lib/redis_queued_locks/acquirer/release_read_lock.rb +22 -3
- data/lib/redis_queued_locks/client.rb +140 -56
- data/lib/redis_queued_locks/config.rb +105 -14
- data/lib/redis_queued_locks/errors.rb +34 -4
- data/lib/redis_queued_locks/instrument/sampler.rb +4 -7
- data/lib/redis_queued_locks/logging/sampler.rb +4 -8
- data/lib/redis_queued_locks/resource.rb +137 -12
- data/lib/redis_queued_locks/swarm/flush_zombies.rb +1 -0
- data/lib/redis_queued_locks/swarm/redis_client_builder.rb +8 -4
- data/lib/redis_queued_locks/swarm/swarm_element/threaded.rb +8 -6
- data/lib/redis_queued_locks/swarm/zombie_info.rb +6 -2
- data/lib/redis_queued_locks/swarm.rb +86 -5
- data/lib/redis_queued_locks/utilities.rb +1 -1
- data/lib/redis_queued_locks/version.rb +2 -2
- data/rbs_collection.lock.yaml +4 -0
- data/rbs_collection.yaml +2 -1
- data/sig/manifest.yml +1 -0
- data/sig/redis_queued_locks/acquirer/acquire_lock/dequeue_from_lock_queue/log_visitor.rbs +2 -2
- data/sig/redis_queued_locks/acquirer/acquire_lock/dequeue_from_lock_queue.rbs +2 -2
- data/sig/redis_queued_locks/acquirer/acquire_lock/instr_visitor.rbs +25 -5
- data/sig/redis_queued_locks/acquirer/acquire_lock/log_visitor.rbs +32 -12
- data/sig/redis_queued_locks/acquirer/acquire_lock/try_to_lock/log_visitor.rbs +61 -40
- data/sig/redis_queued_locks/acquirer/acquire_lock/try_to_lock.rbs +59 -11
- data/sig/redis_queued_locks/acquirer/acquire_lock/{yield_with_expire → yield_expire}/log_visitor.rbs +4 -4
- data/sig/redis_queued_locks/acquirer/acquire_lock/yield_expire.rbs +4 -4
- data/sig/redis_queued_locks/acquirer/acquire_lock.rbs +6 -4
- data/sig/redis_queued_locks/acquirer/clear_dead_requests.rbs +23 -1
- data/sig/redis_queued_locks/acquirer/extend_lock_ttl.rbs +16 -3
- data/sig/redis_queued_locks/acquirer/queues.rbs +1 -1
- data/sig/redis_queued_locks/acquirer/reindex_locks.rbs +22 -1
- data/sig/redis_queued_locks/acquirer/release_lock.rbs +3 -2
- data/sig/redis_queued_locks/acquirer/release_read_lock.rbs +1 -1
- data/sig/redis_queued_locks/client.rbs +24 -10
- data/sig/redis_queued_locks/config.rbs +54 -1
- data/sig/redis_queued_locks/errors.rbs +5 -2
- data/sig/redis_queued_locks/instrument/sampler.rbs +3 -3
- data/sig/redis_queued_locks/instrument/void_notifier.rbs +3 -1
- data/sig/redis_queued_locks/logging/sampler.rbs +3 -3
- data/sig/redis_queued_locks/resource.rbs +23 -13
- data/sig/redis_queued_locks/swarm/swarm_element/threaded.rbs +1 -1
- data/sig/redis_queued_locks/utilities.rbs +2 -1
- data/sig/redis_queued_locks.rbs +7 -0
- data/sig/vendor/redis_client.rbs +1 -0
- metadata +4 -3
- /data/sig/redis_queued_locks/{acquier.rbs → acquirer.rbs} +0 -0
data/README.md
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
**RedisQueuedLocks** (aka **RQL**, aka **RQL-lock**) - <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 its own queue separately from other queues) and processed in order of their priority (FIFO
|
|
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: requests of a higher [priority](#lock-request-prioritization) go first, requests of the same priority are processed in FIFO order. 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
|
|
|
11
|
-
Supports `Read`/`Write` locks and `Reentrant` locks.
|
|
11
|
+
Supports [lock request prioritization](#lock-request-prioritization), `Read`/`Write` locks and `Reentrant` locks.
|
|
12
12
|
|
|
13
13
|
Provides flexible invocation flow, parametrized limits (lock request ttl, lock ttl, queue ttl, lock attempts limit, fast failing, etc), **zombie locks elimination**, **[lock indexing](#lock-index)**, logging and instrumentation (and much more).
|
|
14
14
|
|
|
@@ -47,6 +47,7 @@ Provides flexible invocation flow, parametrized limits (lock request ttl, lock t
|
|
|
47
47
|
- [locks_info](#locks_info---get-list-of-locks-with-their-info)
|
|
48
48
|
- [queues_info](#queues_info---get-list-of-queues-with-their-info)
|
|
49
49
|
- [clear_dead_requests](#clear_dead_requests)
|
|
50
|
+
- [reindex_locks](#reindex_locks)
|
|
50
51
|
- [current_acquirer_id](#current_acquirer_id)
|
|
51
52
|
- [current_host_id](#current_host_id)
|
|
52
53
|
- [possible_host_ids](#possible_host_ids)
|
|
@@ -70,6 +71,7 @@ Provides flexible invocation flow, parametrized limits (lock request ttl, lock t
|
|
|
70
71
|
- [Lock Access Strategies](#lock-access-strategies)
|
|
71
72
|
- [queued](#lock-access-strategies)
|
|
72
73
|
- [random](#lock-access-strategies)
|
|
74
|
+
- [Lock Request Prioritization](#lock-request-prioritization)
|
|
73
75
|
- [Read/Write Locks](#readwrite-locks)
|
|
74
76
|
- [Deadlocks and Reentrant locks](#deadlocks-and-reentrant-locks)
|
|
75
77
|
- [Logging](#logging)
|
|
@@ -78,6 +80,7 @@ Provides flexible invocation flow, parametrized limits (lock request ttl, lock t
|
|
|
78
80
|
- [Instrumentation Configuration](#instrumentation-configuration)
|
|
79
81
|
- [Instrumentation Events](#instrumentation-events)
|
|
80
82
|
- ["redis_queued_locks.lock_obtained"](#redis_queued_lockslock_obtained)
|
|
83
|
+
- ["redis_queued_locks.lock_obtaining_failed"](#redis_queued_lockslock_obtaining_failed)
|
|
81
84
|
- ["redis_queued_locks.extendable_reentrant_lock_obtained"](#redis_queued_locksextendable_reentrant_lock_obtained)
|
|
82
85
|
- ["redis_queued_locks.reentrant_lock_obtained"](#redis_queued_locksreentrant_lock_obtained)
|
|
83
86
|
- ["redis_queued_locks.lock_hold_and_release"](#redis_queued_lockslock_hold_and_release)
|
|
@@ -88,6 +91,9 @@ Provides flexible invocation flow, parametrized limits (lock request ttl, lock t
|
|
|
88
91
|
- ["redis_queued_locks.explicit_read_lock_release"](#redis_queued_locksexplicit_read_lock_release)
|
|
89
92
|
- ["redis_queued_locks.explicit_all_locks_release"](#redis_queued_locksexplicit_all_locks_release)
|
|
90
93
|
- ["redis_queued_locks.release_locks_of"](#redis_queued_locksrelease_locks_of)
|
|
94
|
+
- ["redis_queued_locks.extend_lock_ttl"](#redis_queued_locksextend_lock_ttl)
|
|
95
|
+
- ["redis_queued_locks.clear_dead_requests"](#redis_queued_locksclear_dead_requests)
|
|
96
|
+
- ["redis_queued_locks.reindex_locks"](#redis_queued_locksreindex_locks)
|
|
91
97
|
- [Roadmap](#roadmap)
|
|
92
98
|
- [Build and Develop](#build-and-develop)
|
|
93
99
|
- [Contributing](#contributing)
|
|
@@ -120,7 +126,7 @@ Provides flexible invocation flow, parametrized limits (lock request ttl, lock t
|
|
|
120
126
|
|
|
121
127
|
<sup>\[[back to top](#table-of-contents)\]</sup>
|
|
122
128
|
|
|
123
|
-
> 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
|
|
129
|
+
> 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: requests of a higher [priority](#lock-request-prioritization) go first, requests of the same priority are processed in FIFO order. Each lock request lives some period of time (RTTL) which guarantees that the request queue will never be stacked.
|
|
124
130
|
|
|
125
131
|
> 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.
|
|
126
132
|
|
|
@@ -191,16 +197,20 @@ client = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
|
191
197
|
|
|
192
198
|
# (seconds) (supports nil)
|
|
193
199
|
# - nil means "no timeout" and you are only limited by "retry_count" config;
|
|
200
|
+
# - 0 is the same as nil (no timeout);
|
|
194
201
|
config['try_to_lock_timeout'] = 10
|
|
195
202
|
|
|
196
203
|
# (milliseconds) (default: 5_000)
|
|
197
204
|
# - lock's time to live
|
|
205
|
+
# - should be a positive Integer
|
|
198
206
|
config['default_lock_ttl'] = 5_000
|
|
199
207
|
|
|
200
208
|
# (seconds) (default: 15)
|
|
201
|
-
# - lock request timeout. after this timeout your lock request in queue will be requeued with new position (at the end of the
|
|
209
|
+
# - lock request timeout. after this timeout your lock request in queue will be requeued with new position (at the end of the
|
|
210
|
+
# lock requests of its priority);
|
|
202
211
|
# - (the enabled lock index) the lock request queue lives as long as its latest lock request (the queue TTL is refreshed with
|
|
203
212
|
# the queue ttl on each lock request): queues of dead acquirers expire by themselves;
|
|
213
|
+
# - should be a positive Integer
|
|
204
214
|
config['default_queue_ttl'] = 15
|
|
205
215
|
|
|
206
216
|
# (boolean) (default: false)
|
|
@@ -241,6 +251,30 @@ client = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
|
241
251
|
# retries, timeouts and so on). if lock is free to obtain - it will be obtained;
|
|
242
252
|
config['default_access_strategy'] = :queued
|
|
243
253
|
|
|
254
|
+
# (hash) (default: { lowest: -100, low: -50, normal: 0, high: 50, highest: 100 })
|
|
255
|
+
# - Lock request priority names: a priority name (Symbol) => its priority (Integer from `-100` to `100`);
|
|
256
|
+
# - The names can be used instead of the Integer priorities in the `:priority` attribute and in
|
|
257
|
+
# `config['default_lock_priority']`: `rql.lock('name', priority: :high) { ... }`;
|
|
258
|
+
# - Define the names of your application (keep the default names with `merge`):
|
|
259
|
+
# `config['lock_priorities'] = config['lock_priorities'].merge(payment: 90, email: -10)`;
|
|
260
|
+
# - The default value is frozen (it is shared by all clients): assign a new hash, don't change it in place;
|
|
261
|
+
# - See [Lock Request Prioritization](#lock-request-prioritization) documentation section for details;
|
|
262
|
+
config['lock_priorities'] = { lowest: -100, low: -50, normal: 0, high: 50, highest: 100 }
|
|
263
|
+
|
|
264
|
+
# (integer/symbol) (default: 0)
|
|
265
|
+
# - The default lock request priority: an Integer from `-100` (the lowest) to `100` (the highest)
|
|
266
|
+
# or a priority name of `config['lock_priorities']`;
|
|
267
|
+
# - The priority name should be a key of `config['lock_priorities']` (`RedisQueuedLocks::ConfigValidationError` otherwise):
|
|
268
|
+
# it is checked once after the configuration block (`Client.new { ... }`, `#configure { ... }`: the keys can be assigned
|
|
269
|
+
# in any order; an inconsistent pair is never applied: both keys keep their previous values, also when the block fails)
|
|
270
|
+
# and on a direct `config[...] =` assignment (before the value is applied: change the priority names and the default
|
|
271
|
+
# name together in a configuration block);
|
|
272
|
+
# - Can be customized in methods `#lock`, `#lock!`, `#lock_series` and `#lock_series!` via `:priority` attribute;
|
|
273
|
+
# - `:queued` access strategy: the lock request is placed in the lock request queue after all requests of the same
|
|
274
|
+
# or higher priority and before all requests of the lower priority (FIFO inside the same priority);
|
|
275
|
+
# - See [Lock Request Prioritization](#lock-request-prioritization) documentation section for details;
|
|
276
|
+
config['default_lock_priority'] = 0
|
|
277
|
+
|
|
244
278
|
# (symbol) (default: :wait_for_lock)
|
|
245
279
|
# - Global default conflict strategy mode;
|
|
246
280
|
# - Can be customized in methods `#lock` and `#lock!` via `:conflict_strategy` attribute (see method signatures of #lock and #lock! methods);
|
|
@@ -260,6 +294,7 @@ client = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
|
260
294
|
# - (the enabled lock index) how many locks (lock names of the lock index) will be processed at a time
|
|
261
295
|
# (the ZSCAN count of the lock index pass);
|
|
262
296
|
# - affects the performance of your Redis and Ruby Application (configure thoughtfully);
|
|
297
|
+
# - should be a positive Integer
|
|
263
298
|
config['lock_release_batch_size'] = 100
|
|
264
299
|
|
|
265
300
|
# (default: 300)
|
|
@@ -268,12 +303,14 @@ client = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
|
268
303
|
# (the ZSCAN count of the lock index pass, DEL for batch deletion);
|
|
269
304
|
# - 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);
|
|
270
305
|
# - affects the performance of your Redis (configure thoughtfully);
|
|
306
|
+
# - should be a positive Integer
|
|
271
307
|
config['clear_locks_of__lock_scan_size'] = 300
|
|
272
308
|
|
|
273
309
|
# (default: 300)
|
|
274
310
|
# - how many lock queues will be processed in one round trip in #clear_locks_of and in #clear_current_locks methods
|
|
275
311
|
# (lock queues are found by the same SCAN pass as locks or derived from the lock names of the same lock index pass);
|
|
276
312
|
# - affects the performance of your Redis (configure thoughtfully);
|
|
313
|
+
# - should be a positive Integer
|
|
277
314
|
config['clear_locks_of__queue_scan_size'] = 300
|
|
278
315
|
|
|
279
316
|
# (default: 500)
|
|
@@ -282,6 +319,7 @@ client = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
|
282
319
|
# - (the enabled lock index) how many locks (lock names of the lock index) should be processed at a time
|
|
283
320
|
# (the ZSCAN count of the lock index pass);
|
|
284
321
|
# - affects the performance of your Redis and Ruby Application (configure thoughtfully);
|
|
322
|
+
# - should be a positive Integer
|
|
285
323
|
config['key_extraction_batch_size'] = 500
|
|
286
324
|
|
|
287
325
|
# (default: 1 day)
|
|
@@ -327,15 +365,28 @@ client = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
|
327
365
|
# - should implement `debug(progname = nil, &block)` (minimal requirement) or be an instance of Ruby's `::Logger` class/subclass;
|
|
328
366
|
# - supports `SemanticLogger::Logger` (see "semantic_logger" gem)
|
|
329
367
|
# - at this moment the only debug logs are realised in following cases:
|
|
330
|
-
# - "[redis_queued_locks.start_lock_obtaining]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
331
|
-
# - "[redis_queued_locks.start_try_to_lock_cycle]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
332
|
-
# - "[redis_queued_locks.dead_score_reached__reset_acquirer_position]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
333
|
-
# - "[redis_queued_locks.lock_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "
|
|
334
|
-
# - "[redis_queued_locks.
|
|
335
|
-
# - "[redis_queued_locks.
|
|
336
|
-
# - "[redis_queued_locks.
|
|
337
|
-
# - "[redis_queued_locks.
|
|
338
|
-
# - "[redis_queued_locks.
|
|
368
|
+
# - "[redis_queued_locks.start_lock_obtaining]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
369
|
+
# - "[redis_queued_locks.start_try_to_lock_cycle]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
370
|
+
# - "[redis_queued_locks.dead_score_reached__reset_acquirer_position]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
371
|
+
# - "[redis_queued_locks.lock_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time");
|
|
372
|
+
# - "[redis_queued_locks.lock_obtaining_failed]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time", "result");
|
|
373
|
+
# - "[redis_queued_locks.extendable_reentrant_lock_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time");
|
|
374
|
+
# - "[redis_queued_locks.reentrant_lock_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time");
|
|
375
|
+
# - "[redis_queued_locks.fail_fast_or_limits_reached_or_deadlock__dequeue]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
376
|
+
# - "[redis_queued_locks.expire_lock]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
377
|
+
# - "[redis_queued_locks.decrease_lock]" (logs "lock_key", "decreased_ttl", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
378
|
+
# - "[redis_queued_locks.start_lock_series_obtaining]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
379
|
+
# - "[redis_queued_locks.lock_series_obtained]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time");
|
|
380
|
+
# - "[redis_queued_locks.expire_lock_series]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
381
|
+
# - "[redis_queued_locks.explicit_lock_release]" (logs "lock_key", "lock_key_queue", "rel_time", "at");
|
|
382
|
+
# - "[redis_queued_locks.explicit_read_lock_release]" (logs "lock_key", "acq_id", "lock_res", "rel_time", "at");
|
|
383
|
+
# - "[redis_queued_locks.explicit_all_locks_release]" (logs "at", "rel_time", "rel_key_cnt");
|
|
384
|
+
# - "[redis_queued_locks.release_locks_of]" (logs "at", "hst_id", "acq_id", "rel_time", "rel_key_cnt", "tch_queue_cnt");
|
|
385
|
+
# - "[redis_queued_locks.extend_lock_ttl]" (logs "lock_key", "rw_mode", "all_read_locks", "ext_ttl", "extended_locks_count", "ext_time", "at");
|
|
386
|
+
# - "[redis_queued_locks.clear_dead_requests]" (logs "dead_ttl", "prc_queue_cnt", "rel_time", "at");
|
|
387
|
+
# - "[redis_queued_locks.reindex_locks]" (logs "idx_key_cnt", "idx_time", "at");
|
|
388
|
+
# - "rw_mode" is the requested lock mode ('write'/'read'), the held lock mode for "expire_lock" and "decrease_lock",
|
|
389
|
+
# the mode of the series for lock series logs;
|
|
339
390
|
# - by default uses VoidLogger that does nothing;
|
|
340
391
|
config['logger'] = RedisQueuedLocks::Logging::VoidLogger
|
|
341
392
|
|
|
@@ -343,19 +394,26 @@ client = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
|
343
394
|
# - adds additional debug logs;
|
|
344
395
|
# - enables additional logs for each internal try-retry lock acquiring (a lot of logs can be generated depending on your retry configurations);
|
|
345
396
|
# - it adds following debug logs in addition to the existing:
|
|
346
|
-
# - "[redis_queued_locks.try_lock.start]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
347
|
-
# - "[redis_queued_locks.try_lock.rconn_fetched]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
348
|
-
# - "[redis_queued_locks.try_lock.same_process_conflict_detected]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
349
|
-
# - "[redis_queued_locks.try_lock.same_process_conflict_analyzed]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "spc_status");
|
|
350
|
-
# - "[redis_queued_locks.try_lock.reentrant_lock__extend_and_work_through]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "spc_status", "last_ext_ttl", "last_ext_ts");
|
|
351
|
-
# - "[redis_queued_locks.try_lock.reentrant_lock__work_through]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "spc_status", last_spc_ts);
|
|
352
|
-
# - "[redis_queued_locks.try_lock.
|
|
353
|
-
# - "[redis_queued_locks.try_lock.
|
|
354
|
-
# - "[redis_queued_locks.try_lock.
|
|
355
|
-
# - "[redis_queued_locks.try_lock.
|
|
356
|
-
# - "[redis_queued_locks.try_lock.
|
|
357
|
-
# - "[redis_queued_locks.try_lock.
|
|
358
|
-
# - "[redis_queued_locks.try_lock.
|
|
397
|
+
# - "[redis_queued_locks.try_lock.start]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
398
|
+
# - "[redis_queued_locks.try_lock.rconn_fetched]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
399
|
+
# - "[redis_queued_locks.try_lock.same_process_conflict_detected]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
400
|
+
# - "[redis_queued_locks.try_lock.same_process_conflict_analyzed]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "spc_status");
|
|
401
|
+
# - "[redis_queued_locks.try_lock.reentrant_lock__extend_and_work_through]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "spc_status", "last_ext_ttl", "last_ext_ts");
|
|
402
|
+
# - "[redis_queued_locks.try_lock.reentrant_lock__work_through]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "spc_status", "last_spc_ts");
|
|
403
|
+
# - "[redis_queued_locks.try_lock.single_process_lock_conflict__dead_lock]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "spc_status", "last_spc_ts");
|
|
404
|
+
# - "[redis_queued_locks.try_lock.single_process_lock_conflict__lock_upgrade]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "spc_status", "last_spc_ts");
|
|
405
|
+
# - "[redis_queued_locks.try_lock.acq_added_to_queue]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
406
|
+
# - "[redis_queued_locks.try_lock.remove_expired_acqs]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
407
|
+
# - "[redis_queued_locks.try_lock.dead_request_removed]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "dead_acq_id", "dead_priority");
|
|
408
|
+
# - "[redis_queued_locks.try_lock.get_first_from_queue]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "first_acq_id_in_queue", "first_priority_in_queue");
|
|
409
|
+
# - "[redis_queued_locks.try_lock.exit__queue_ttl_reached]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
410
|
+
# - "[redis_queued_locks.try_lock.exit__no_first]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "first_acq_id_in_queue", "first_priority_in_queue", "<current_lock_data>");
|
|
411
|
+
# - "[redis_queued_locks.try_lock.exit__lock_still_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "first_acq_id_in_queue", "first_priority_in_queue", "locked_by_acq_id", "<current_lock_data>");
|
|
412
|
+
# - "[redis_queued_locks.try_lock.exit__write_request_ahead]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "ahead_acq_id", "ahead_priority");
|
|
413
|
+
# - "[redis_queued_locks.try_lock.exit__read_request_ahead]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "ahead_acq_id", "ahead_priority");
|
|
414
|
+
# - "[redis_queued_locks.try_lock.exit__read_lock_still_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "first_acq_id_in_queue", "first_priority_in_queue");
|
|
415
|
+
# - "[redis_queued_locks.try_lock.obtain__free_to_acquire]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
416
|
+
# - "rw_mode" is the requested lock mode ('write'/'read');
|
|
359
417
|
config['log_lock_try'] = false
|
|
360
418
|
|
|
361
419
|
# (default: false)
|
|
@@ -366,7 +424,7 @@ client = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
|
366
424
|
|
|
367
425
|
# (default: 15)
|
|
368
426
|
# - the percent of cases that should be logged;
|
|
369
|
-
# - take an effect when <config['
|
|
427
|
+
# - take an effect when <config['log_sampling_enabled']> is true;
|
|
370
428
|
# - works in tandem with <config['log_sampling_enabled']> and <config['log_sampler']> configs;
|
|
371
429
|
config['log_sampling_percent'] = 15
|
|
372
430
|
|
|
@@ -383,11 +441,11 @@ client = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
|
383
441
|
# - enables <instrumentaion sampling>: only the configured percent of RQL cases will be instrumented;
|
|
384
442
|
# - disabled by default;
|
|
385
443
|
# - works in tandem with <config['instr_sampling_percent'] and <config['instr_sampler']>;
|
|
386
|
-
config['
|
|
444
|
+
config['instr_sampling_enabled'] = false
|
|
387
445
|
|
|
388
446
|
# (default: 15)
|
|
389
447
|
# - the percent of cases that should be instrumented;
|
|
390
|
-
# - take an effect when <config['
|
|
448
|
+
# - take an effect when <config['instr_sampling_enabled']> is true;
|
|
391
449
|
# - works in tandem with <config['instr_sampling_enabled']> and <config['instr_sampler']> configs;
|
|
392
450
|
config['instr_sampling_percent'] = 15
|
|
393
451
|
|
|
@@ -409,15 +467,18 @@ end
|
|
|
409
467
|
<sup>\[[back to top](#table-of-contents)\]</sup>
|
|
410
468
|
|
|
411
469
|
- [lock](#lock---obtain-a-lock)
|
|
412
|
-
- [lock!](#lock---
|
|
470
|
+
- [lock!](#lock---exceptional-lock-obtaining)
|
|
413
471
|
- [lock_series](#lock_series---poc-acquire-a-series-of-locks) (PoC)
|
|
414
472
|
- [lock_series!](#lock_series---poc-exceptional-lock_series) (PoC)
|
|
415
473
|
- [lock_info](#lock_info)
|
|
416
474
|
- [queue_info](#queue_info)
|
|
417
475
|
- [locked?](#locked)
|
|
418
476
|
- [queued?](#queued)
|
|
419
|
-
- [unlock](#unlock---release-a-lock)
|
|
420
|
-
- [
|
|
477
|
+
- [unlock](#unlock---release-a-lock) (aka `release_lock`)
|
|
478
|
+
- [unlock_read](#unlock_read---release-the-read-lock-of-the-current-acquirer) (aka `release_read_lock`)
|
|
479
|
+
- [clear_locks](#clear_locks---release-all-locks-and-lock-queues) (aka `release_locks`)
|
|
480
|
+
- [clear_locks_of](#clear_locks_of) (aka `release_locks_of`)
|
|
481
|
+
- [clear_current_locks](#clear_current_locks) (aka `release_current_locks`)
|
|
421
482
|
- [extend_lock_ttl](#extend_lock_ttl)
|
|
422
483
|
- [locks](#locks---get-list-of-obtained-locks)
|
|
423
484
|
- [queues](#queues---get-list-of-lock-request-queues)
|
|
@@ -430,6 +491,10 @@ end
|
|
|
430
491
|
- [current_host_id](#current_host_id)
|
|
431
492
|
- [possible_host_ids](#possible_host_ids)
|
|
432
493
|
|
|
494
|
+
The numeric options of the methods (ttls, timeouts, retries, scan and batch sizes, zombie ttl) are validated with the same
|
|
495
|
+
types and ranges as their `config` keys: `Integer` values only (`timeout: 0.5` is rejected), `RedisQueuedLocks::ArgumentError`
|
|
496
|
+
(`RedisQueuedLocks::SwarmArgumentError` for the swarm methods) otherwise.
|
|
497
|
+
|
|
433
498
|
---
|
|
434
499
|
|
|
435
500
|
#### #lock - obtain a lock
|
|
@@ -465,6 +530,7 @@ def lock(
|
|
|
465
530
|
conflict_strategy: config['default_conflict_strategy'],
|
|
466
531
|
read_write_mode: :write,
|
|
467
532
|
access_strategy: config['default_access_strategy'],
|
|
533
|
+
priority: config['default_lock_priority'],
|
|
468
534
|
identity: uniq_identity, # (attr_accessor) calculated during client instantiation via config['uniq_identifier'] proc;
|
|
469
535
|
meta: nil,
|
|
470
536
|
detailed_acq_timeout_error: config['detailed_acq_timeout_error'],
|
|
@@ -488,6 +554,7 @@ def lock(
|
|
|
488
554
|
- Lock name to be obtained.
|
|
489
555
|
- `ttl` - (optional) - [Integer]
|
|
490
556
|
- Lock's time to live (in milliseconds);
|
|
557
|
+
- should be a positive `Integer` (write and read locks);
|
|
491
558
|
- pre-configured in `config['default_lock_ttl']`;
|
|
492
559
|
- `queue_ttl` - (optional) `[Integer]`
|
|
493
560
|
- Lifetime of the acuier's lock request. In seconds.
|
|
@@ -502,9 +569,14 @@ def lock(
|
|
|
502
569
|
- pre-configured in `config['lock_index_grace_period']`;
|
|
503
570
|
- `timeout` - (optional) `[Integer,NilClass]`
|
|
504
571
|
- Time period a client should try to acquire the lock (in seconds). Nil means "without timeout".
|
|
572
|
+
- `0` is the same as `nil` (without timeout);
|
|
505
573
|
- pre-configured in `config['try_to_lock_timeout']`;
|
|
574
|
+
- the timeout can not be swallowed by `rescue` of your logger/instrumenter (see `timed` below);
|
|
506
575
|
- `timed` - (optiona) `[Boolean]`
|
|
507
576
|
- Limit the invocation time period of the passed block of code by the lock's TTL.
|
|
577
|
+
- the block is interrupted by an internal `RedisQueuedLocks::Exception` (an `::Exception`, like `Timeout::ExitException` of Ruby's `Timeout`): `rescue => e`,
|
|
578
|
+
`rescue StandardError`, `rescue Timeout::Error` and `rescue nil` inside the block can not swallow it (only `rescue Exception`),
|
|
579
|
+
`ensure` of the block still runs;
|
|
508
580
|
- pre-configured in `config['is_timed_by_default']`;
|
|
509
581
|
- `false` by default;
|
|
510
582
|
- `retry_count` - (optional) `[Integer,NilClass]`
|
|
@@ -519,7 +591,7 @@ def lock(
|
|
|
519
591
|
- `instrumenter` - (optional) `[#notify]`
|
|
520
592
|
- See RedisQueuedLocks::Instrument::ActiveSupport for example;
|
|
521
593
|
- See [Instrumentation](#instrumentation) section of docs;
|
|
522
|
-
- pre-configured in `config['
|
|
594
|
+
- pre-configured in `config['instrumenter']` with void notifier (`RedisQueuedLocks::Instrument::VoidNotifier`);
|
|
523
595
|
- `instrument` - (optional) `[NilClass,Any]`
|
|
524
596
|
- Custom instrumentation data wich will be passed to the instrumenter's payload with :instrument key;
|
|
525
597
|
- `nil` by default (means "no custom instrumentation data");
|
|
@@ -541,6 +613,19 @@ def lock(
|
|
|
541
613
|
if lock is free to obtain - it will be obtained;
|
|
542
614
|
- pre-configured in `config['default_access_strategy']`;
|
|
543
615
|
- See [Lock Access Strategies](#lock-access-strategies) documentation section for details;
|
|
616
|
+
- `priority` - (optional) - `[Integer,Symbol]`
|
|
617
|
+
- The lock request priority: an Integer from `-100` (the lowest) to `100` (the highest), `0` by default,
|
|
618
|
+
or a priority name of `config['lock_priorities']` (`:lowest`, `:low`, `:normal`, `:high`, `:highest` by default,
|
|
619
|
+
the names of your application can be configured);
|
|
620
|
+
- `:queued` access strategy: the lock request is placed in the lock request queue after all requests of the same or
|
|
621
|
+
higher priority and before all requests of the lower priority (strict priority, FIFO inside the same priority;
|
|
622
|
+
read and write lock requests too);
|
|
623
|
+
- `:random` access strategy: the lock request does not wait for its turn (it ignores the order of the lock request queues
|
|
624
|
+
and the priorities of other requests), but it is queued at the position of its priority like a `:queued` one: `:queued`
|
|
625
|
+
lock requests wait for it;
|
|
626
|
+
- the obtained lock is never taken away: the priority orders the waiting lock requests only;
|
|
627
|
+
- pre-configured in `config['default_lock_priority']`;
|
|
628
|
+
- See [Lock Request Prioritization](#lock-request-prioritization) documentation section for details;
|
|
544
629
|
- `conflict_strategy` - (optional) - `[Symbol]`
|
|
545
630
|
- The conflict strategy mode for cases when the process that obtained the lock
|
|
546
631
|
want to acquire this lock again;
|
|
@@ -603,7 +688,7 @@ def lock(
|
|
|
603
688
|
- pre-configured in `config['log_sampling_enabled']`;
|
|
604
689
|
- `log_sampling_percent` - (optional) `[Integer]`
|
|
605
690
|
- the percent of cases that should be logged;
|
|
606
|
-
- take an effect when `
|
|
691
|
+
- take an effect when `log_sampling_enabled` is true;
|
|
607
692
|
- works in tandem with `log_sampling_enabled` and `log_sampler` options;
|
|
608
693
|
- pre-configured in `config['log_sampling_percent']`;
|
|
609
694
|
- `log_sampler` - (optional) `[#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]`
|
|
@@ -625,7 +710,7 @@ def lock(
|
|
|
625
710
|
- pre-configured in `config['instr_sampling_enabled']`;
|
|
626
711
|
- `instr_sampling_percent` - (optional) `[Integer]`
|
|
627
712
|
- the percent of cases that should be instrumented;
|
|
628
|
-
- take an effect when `
|
|
713
|
+
- take an effect when `instr_sampling_enabled` is true;
|
|
629
714
|
- works in tandem with `instr_sampling_enabled` and `instr_sampler` options;
|
|
630
715
|
- pre-configured in `config['instr_sampling_percent']`;
|
|
631
716
|
- `instr_sampler` - (optional) `[#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]`
|
|
@@ -663,6 +748,7 @@ Return value:
|
|
|
663
748
|
result: {
|
|
664
749
|
lock_key: "rql:lock:my_lock",
|
|
665
750
|
acq_id: "rql:acq:26672/2280/2300/2320/70ea5dbf10ea1056",
|
|
751
|
+
hst_id: "rql:hst:26672/2280/2320/70ea5dbf10ea1056",
|
|
666
752
|
ts: 1711909612.653696,
|
|
667
753
|
ttl: 10000,
|
|
668
754
|
process: :lock_obtaining
|
|
@@ -676,7 +762,8 @@ Return value:
|
|
|
676
762
|
- `:lock_obtaining` - classic lock obtaining proces. Default behavior (`conflict_strategy: :wait_for_lock`);
|
|
677
763
|
- `:extendable_conflict_work_through` - reentrant lock acquiring process with lock's TTL extension. Suitable for `conflict_strategy: :extendable_work_through`;
|
|
678
764
|
- `:conflict_work_through` - reentrant lock acquiring process without lock's TTL extension. Suitable for `conflict_strategy: :work_through`;
|
|
679
|
-
-
|
|
765
|
+
- (`conflict_strategy: :dead_locking` does not obtain the lock already obtained by the current acquirer:
|
|
766
|
+
it fails with `{ ok: false, result: :conflict_dead_lock }`);
|
|
680
767
|
- For more details see [Deadlocks and Reentrant locks](#deadlocks-and-reentrant-locks) readme section;
|
|
681
768
|
- For successful lock obtaining:
|
|
682
769
|
```ruby
|
|
@@ -688,7 +775,7 @@ Return value:
|
|
|
688
775
|
hst_id: String, # host identifier ("process_id/thread_id/ractor_id/identity")
|
|
689
776
|
ts: Float, # time (epoch) when lock was obtained (float, Time#to_f)
|
|
690
777
|
ttl: Integer, # lock's time to live in milliseconds (integer)
|
|
691
|
-
process: Symbol # which logical process has acquired the lock (:lock_obtaining, :extendable_conflict_work_through, :conflict_work_through
|
|
778
|
+
process: Symbol # which logical process has acquired the lock (:lock_obtaining, :extendable_conflict_work_through, :conflict_work_through)
|
|
692
779
|
}
|
|
693
780
|
}
|
|
694
781
|
```
|
|
@@ -700,21 +787,21 @@ Return value:
|
|
|
700
787
|
result: {
|
|
701
788
|
lock_key: "rql:lock:my_lock",
|
|
702
789
|
acq_id: "rql:acq:26672/2280/2300/2320/70ea5dbf10ea1056",
|
|
703
|
-
|
|
790
|
+
hst_id: "rql:hst:26672/2280/2320/70ea5dbf10ea1056",
|
|
704
791
|
ts: 1711909612.653696,
|
|
705
792
|
ttl: 10000,
|
|
706
|
-
process: :lock_obtaining # for
|
|
793
|
+
process: :lock_obtaining # for reentrant locks (conflict strategies): :conflict_work_through, :extendable_conflict_work_through
|
|
707
794
|
}
|
|
708
795
|
}
|
|
709
796
|
```
|
|
710
797
|
- For failed lock obtaining:
|
|
711
798
|
```ruby
|
|
712
|
-
{ ok: false, result: :timeout_reached }
|
|
713
|
-
{ ok: false, result: :
|
|
799
|
+
{ ok: false, result: :timeout_reached } # see <timeout> option
|
|
800
|
+
{ ok: false, result: :retry_limit_reached } # see <retry_count> option
|
|
714
801
|
{ ok: false, result: :conflict_dead_lock } # see <conflict_strategy> option for details (:dead_locking strategy)
|
|
802
|
+
{ ok: false, result: :conflict_lock_upgrade } # read-to-write lock upgrade (see <read_write_mode> and <conflict_strategy> options)
|
|
715
803
|
{ ok: false, result: :fail_fast_no_try } # see <fail_fast> option
|
|
716
804
|
{ ok: false, result: :fail_fast_after_try } # see <fail_fast> option
|
|
717
|
-
{ ok: false, result: :unknown }
|
|
718
805
|
```
|
|
719
806
|
|
|
720
807
|
Examples:
|
|
@@ -775,8 +862,9 @@ rql.lock_info("my_lock")
|
|
|
775
862
|
# =>
|
|
776
863
|
{
|
|
777
864
|
"lock_key" => "rql:lock:my_lock",
|
|
865
|
+
"rw_mode" => "write",
|
|
778
866
|
"acq_id" => "rql:acq:123/456/567/678/374dd74324",
|
|
779
|
-
"hst_id" => "rql:
|
|
867
|
+
"hst_id" => "rql:hst:123/456/678/374dd74324",
|
|
780
868
|
"ts" => 123456789,
|
|
781
869
|
"ini_ttl" => 123456,
|
|
782
870
|
"rem_ttl" => 123440,
|
|
@@ -787,7 +875,7 @@ rql.lock_info("my_lock")
|
|
|
787
875
|
|
|
788
876
|
- (`:queue_ttl`) setting a short limit of time to the lock request queue position (if a process fails to acquire
|
|
789
877
|
the lock within this period of time (and before timeout/retry_count limits occurs of course) -
|
|
790
|
-
its lock request will be moved to the end of queue):
|
|
878
|
+
its lock request will be moved to the end of queue: the end of the lock requests of its priority):
|
|
791
879
|
|
|
792
880
|
```ruby
|
|
793
881
|
rql.lock("my_lock", queue_ttl: 5, timeout: 10_000, retry_count: nil)
|
|
@@ -899,6 +987,7 @@ def lock!(
|
|
|
899
987
|
instrument: nil,
|
|
900
988
|
instrumenter: config['instrumenter'],
|
|
901
989
|
access_strategy: config['default_access_strategy'],
|
|
990
|
+
priority: config['default_lock_priority'],
|
|
902
991
|
conflict_strategy: config['default_conflict_strategy'],
|
|
903
992
|
read_write_mode: :write,
|
|
904
993
|
log_sampling_enabled: config['log_sampling_enabled'],
|
|
@@ -947,6 +1036,7 @@ applied with the same value to the each lock acquirement process (for each lock
|
|
|
947
1036
|
- reentrant locks (already obtained by your outer logic, `conflict_strategy: :work_through`/`:extendable_work_through`) are kept;
|
|
948
1037
|
- a lock that is expired during your block and obtained by another acquirer is kept;
|
|
949
1038
|
- `read_write_mode:` is the same for all locks of the series (see [Read/Write Locks](#readwrite-locks));
|
|
1039
|
+
- `priority:` is the same for all locks of the series (see [Lock Request Prioritization](#lock-request-prioritization));
|
|
950
1040
|
- if you did not pass a block - all locks will be released with their forumla-based TTL described above;
|
|
951
1041
|
- **HOW WILL THIS CASE WORK IN FUTURE RELESAE**: all locks will have the same TTL (`5 seconds` for our example, not `15/10/5 seconds`);
|
|
952
1042
|
|
|
@@ -1027,9 +1117,9 @@ client.lock_seires('c', 'a', 'b') # ... `a` is still obtained
|
|
|
1027
1117
|
|
|
1028
1118
|
```shell
|
|
1029
1119
|
# NEW logs (examples)
|
|
1030
|
-
[redis_queued_locks.start_lock_series_obtaining] lock_keys => '["rql:lock:a", "rql:lock:b"]' queue_ttl => 15 acq_id => 'rql:acq:4486/1696/1704/1712/e6fd0da7991e4303' hst_id => 'rql:hst:4486/1696/1712/e6fd0da7991e4303' acs_strat => 'queued' rw_mode => 'write'
|
|
1031
|
-
[redis_queued_locks.lock_series_obtained] lock_keys => '["rql:lock:a", "rql:lock:b"]' queue_ttl => 15 acq_id => 'rql:acq:4486/1696/1704/1712/e6fd0da7991e4303' hst_id => 'rql:hst:4486/1696/1712/e6fd0da7991e4303' acs_strat => 'queued' rw_mode => 'write' acq_time => 2.55 (ms)
|
|
1032
|
-
[redis_queued_locks.expire_lock_series] lock_keys => '["rql:lock:x", "rql:lock:y", "rql:lock:z"]' queue_ttl => 15 acq_id => 'rql:acq:4486/1696/1704/1712/e6fd0da7991e4303' hst_id => 'rql:hst:4486/1696/1712/e6fd0da7991e4303' acs_strat => 'queued' rw_mode => 'write'
|
|
1120
|
+
[redis_queued_locks.start_lock_series_obtaining] lock_keys => '["rql:lock:a", "rql:lock:b"]' queue_ttl => 15 acq_id => 'rql:acq:4486/1696/1704/1712/e6fd0da7991e4303' hst_id => 'rql:hst:4486/1696/1712/e6fd0da7991e4303' acs_strat => 'queued' rw_mode => 'write' priority => 0
|
|
1121
|
+
[redis_queued_locks.lock_series_obtained] lock_keys => '["rql:lock:a", "rql:lock:b"]' queue_ttl => 15 acq_id => 'rql:acq:4486/1696/1704/1712/e6fd0da7991e4303' hst_id => 'rql:hst:4486/1696/1712/e6fd0da7991e4303' acs_strat => 'queued' rw_mode => 'write' priority => 0 acq_time => 2.55 (ms)
|
|
1122
|
+
[redis_queued_locks.expire_lock_series] lock_keys => '["rql:lock:x", "rql:lock:y", "rql:lock:z"]' queue_ttl => 15 acq_id => 'rql:acq:4486/1696/1704/1712/e6fd0da7991e4303' hst_id => 'rql:hst:4486/1696/1712/e6fd0da7991e4303' acs_strat => 'queued' rw_mode => 'write' priority => 0
|
|
1033
1123
|
```
|
|
1034
1124
|
|
|
1035
1125
|
----
|
|
@@ -1060,9 +1150,9 @@ client.lock_series!("x", "y", "z") { ...some_code... }
|
|
|
1060
1150
|
- `"acq_id"` - `string` - acquirer identifier (process_id/thread_id/fiber_id/ractor_id/identity);
|
|
1061
1151
|
- `"hst_id"` - `string` - host identifier (process_id/thread_id/ractor_id/identity);
|
|
1062
1152
|
- `"ts"` - `numeric`/`epoch` - the time when lock was obtained;
|
|
1063
|
-
- `"
|
|
1153
|
+
- `"ini_ttl"` - `integer` - (milliseconds) initial lock key ttl;
|
|
1064
1154
|
- `"rem_ttl"` - `integer` - (milliseconds) remaining lock key ttl;
|
|
1065
|
-
- `<custom metadata>`- `string`/`integer` - custom metadata passed to the `lock`/`lock!` methods via `meta:` keyword argument (see [lock](
|
|
1155
|
+
- `<custom metadata>`- `string`/`integer` - custom metadata passed to the `lock`/`lock!` methods via `meta:` keyword argument (see [lock](#lock---obtain-a-lock) method documentation);
|
|
1066
1156
|
- additional keys for **reentrant locks** and **extendable reentrant locks**:
|
|
1067
1157
|
- for any type of reentrant locks:
|
|
1068
1158
|
- `"spc_cnt"` - `integer` - how many times the lock was obtained as reentrant lock;
|
|
@@ -1082,7 +1172,7 @@ rql.lock_info("your_lock_name")
|
|
|
1082
1172
|
"lock_key" => "rql:lock:your_lock_name",
|
|
1083
1173
|
"rw_mode" => "write",
|
|
1084
1174
|
"acq_id" => "rql:acq:123/456/567/678/374dd74324",
|
|
1085
|
-
"hst_id" => "rql:
|
|
1175
|
+
"hst_id" => "rql:hst:123/456/678/374dd74324",
|
|
1086
1176
|
"ts" => 123456789.12345,
|
|
1087
1177
|
"ini_ttl" => 5_000,
|
|
1088
1178
|
"rem_ttl" => 4_999
|
|
@@ -1099,7 +1189,7 @@ rql.lock_info("your_lock_name")
|
|
|
1099
1189
|
"lock_key" => "rql:lock:your_lock_name",
|
|
1100
1190
|
"rw_mode" => "write",
|
|
1101
1191
|
"acq_id" => "rql:acq:123/456/567/678/374dd74324",
|
|
1102
|
-
"hst_id" => "rql:
|
|
1192
|
+
"hst_id" => "rql:hst:123/456/678/374dd74324",
|
|
1103
1193
|
"ts" => 123456789.12345,
|
|
1104
1194
|
"ini_ttl" => 5_000,
|
|
1105
1195
|
"rem_ttl" => 4_999,
|
|
@@ -1122,12 +1212,12 @@ rql.lock_info("your_lock_name")
|
|
|
1122
1212
|
"lock_key" => "rql:lock:your_lock_name",
|
|
1123
1213
|
"rw_mode" => "write",
|
|
1124
1214
|
"acq_id" => "rql:acq:123/456/567/678/374dd74324",
|
|
1125
|
-
"hst_id" => "rql:
|
|
1215
|
+
"hst_id" => "rql:hst:123/456/678/374dd74324",
|
|
1126
1216
|
"ts" => 123456789.12345,
|
|
1127
1217
|
"ini_ttl" => 5_000,
|
|
1128
1218
|
"rem_ttl" => 9_444,
|
|
1129
1219
|
# ==> keys for any type of reentrant lock:
|
|
1130
|
-
"
|
|
1220
|
+
"spc_cnt" => 2, # how many times the lock was obtained as reentrant lock
|
|
1131
1221
|
# ==> keys for extendable reentarnt locks with `:extendable_work_through` strategy:
|
|
1132
1222
|
"spc_ext_ttl" => 5_000, # sum of TTL of the each <extendable> reentrant lock (3_000 + 2_000)
|
|
1133
1223
|
"l_spc_ext_ini_ttl" => 2_000, # TTL of the last <extendable> reentrant lock
|
|
@@ -1209,15 +1299,18 @@ lock acquirers and their position in queue. The queue of write lock requests is
|
|
|
1209
1299
|
- get the lock queue information;
|
|
1210
1300
|
- queue represents the ordered set of lock key reqests:
|
|
1211
1301
|
- set is ordered by score in ASC manner (inside the Redis Set);
|
|
1212
|
-
- score is represented as a timestamp when the lock request was made
|
|
1302
|
+
- score is represented as a timestamp when the lock request was made shifted by the
|
|
1303
|
+
[priority](#lock-request-prioritization) band (`-priority * 2**32` seconds: the default priority `0` keeps the timestamp);
|
|
1213
1304
|
- represents the acquirer identifier and their score as an array of hashes;
|
|
1214
1305
|
- returns `nil` if lock queues (write and read) do not exist;
|
|
1215
1306
|
- lock queue data (`Hash<String,String|Array<Hash<String|Numeric>>`):
|
|
1216
1307
|
- `"lock_queue"` - `string` - lock queue key in redis (write lock requests);
|
|
1217
1308
|
- `"queue"` - `array` - an array of lock requests (array of hashes):
|
|
1218
1309
|
- `"acq_id"` - `string` - acquirer identifier (process_id/thread_id/fiber_id/ractor_id/identity by default);
|
|
1219
|
-
- `"score"` - `float`/`epoch` - time when the lock request was made (epoch)
|
|
1310
|
+
- `"score"` - `float`/`epoch` - the position of the lock request: time when the lock request was made (epoch) shifted by
|
|
1311
|
+
`-priority * 2**32` seconds (the time itself for the default priority `0`);
|
|
1220
1312
|
- `"rw_mode"` - `string` - the requested lock mode (`"write"`);
|
|
1313
|
+
- `"priority"` - `integer` - the lock request priority (from `-100` to `100`, see [Lock Request Prioritization](#lock-request-prioritization));
|
|
1221
1314
|
- `"read_lock_queue"`, `"read_queue"` - (only when read lock requests exist) the read lock queue key and read lock requests
|
|
1222
1315
|
with the same structure (`"rw_mode"` - `"read"`), see [Read/Write Locks](#readwrite-locks);
|
|
1223
1316
|
|
|
@@ -1228,15 +1321,16 @@ rql.queue_info("your_lock_name")
|
|
|
1228
1321
|
{
|
|
1229
1322
|
"lock_queue" => "rql:lock_queue:your_lock_name",
|
|
1230
1323
|
"queue" => [
|
|
1231
|
-
|
|
1232
|
-
{ "acq_id" => "rql:acq:123/567/
|
|
1233
|
-
{ "acq_id" => "rql:acq:
|
|
1324
|
+
# (a lock request of the high priority: ahead of the requests of the default priority)
|
|
1325
|
+
{ "acq_id" => "rql:acq:123/456/567/678/fa76df9cc2", "score" => -2583360655.459158, "rw_mode" => "write", "priority" => 1 },
|
|
1326
|
+
{ "acq_id" => "rql:acq:123/567/456/679/c7bfcaf4f9", "score" => 1711606640.540906, "rw_mode" => "write", "priority" => 0 },
|
|
1327
|
+
{ "acq_id" => "rql:acq:555/329/523/127/7329553b11", "score" => 1711606640.540963, "rw_mode" => "write", "priority" => 0 },
|
|
1234
1328
|
# ...etc
|
|
1235
1329
|
],
|
|
1236
1330
|
# (when read lock requests exist)
|
|
1237
1331
|
"read_lock_queue" => "rql:lock_read_queue:your_lock_name",
|
|
1238
1332
|
"read_queue" => [
|
|
1239
|
-
{ "acq_id" => "rql:acq:123/456/567/700/fa76df9cc2", "score" => 1711606640.541011, "rw_mode" => "read" },
|
|
1333
|
+
{ "acq_id" => "rql:acq:123/456/567/700/fa76df9cc2", "score" => 1711606640.541011, "rw_mode" => "read", "priority" => 0 },
|
|
1240
1334
|
# ...etc
|
|
1241
1335
|
]
|
|
1242
1336
|
}
|
|
@@ -1319,7 +1413,7 @@ rql.queued?("your_lock_name") # => true/false
|
|
|
1319
1413
|
Return:
|
|
1320
1414
|
- `[Hash<Symbol,Boolean|Hash<Symbol,Numeric|String|Symbol>>]` (`{ ok: true/false, result: Hasn }`);
|
|
1321
1415
|
- `:result` format;
|
|
1322
|
-
- `:rel_time` - `Float` - time spent to process redis commands (in
|
|
1416
|
+
- `:rel_time` - `Float` - time spent to process redis commands (in milliseconds);
|
|
1323
1417
|
- `:rel_key` - `String` - released lock key (RedisQueudLocks-internal lock key name from Redis);
|
|
1324
1418
|
- `:rel_queue` - `String` - released lock queue key (RedisQueuedLocks-internal queue key name from Redis);
|
|
1325
1419
|
- `:queue_res` - `Symbol` - `:released` (or `:nothing_to_release` if the required queue does not exist);
|
|
@@ -1334,7 +1428,7 @@ rql.unlock("your_lock_name")
|
|
|
1334
1428
|
{
|
|
1335
1429
|
ok: true,
|
|
1336
1430
|
result: {
|
|
1337
|
-
rel_time: 0.02, # time spent to lock release (in
|
|
1431
|
+
rel_time: 0.02, # time spent to lock release (in milliseconds)
|
|
1338
1432
|
rel_key: "rql:lock:your_lock_name", # released lock key
|
|
1339
1433
|
rel_queue: "rql:lock_queue:your_lock_name", # released lock key queue
|
|
1340
1434
|
queue_res: :released, # or :nothing_to_release
|
|
@@ -1424,7 +1518,7 @@ rql.unlock_read('report', acquirer_id: reader['acq_id'])
|
|
|
1424
1518
|
- pre-configured value in `config['logger']`;
|
|
1425
1519
|
- `:instrumenter` - (optional) `[#notify]`
|
|
1426
1520
|
- custom instrumenter object;
|
|
1427
|
-
- pre-configured value in `config['
|
|
1521
|
+
- pre-configured value in `config['instrumenter']`;
|
|
1428
1522
|
- `:instrument` - (optional) `[NilClass,Any]`
|
|
1429
1523
|
- custom instrumentation data wich will be passed to the instrumenter's payload with `:instrument` key;
|
|
1430
1524
|
- `:log_sampling_enabled` - (optional) `[Boolean]`
|
|
@@ -1507,7 +1601,7 @@ rql.clear_locks
|
|
|
1507
1601
|
- pre-configured value in `config['logger']`;
|
|
1508
1602
|
- `:instrumenter` - (optional) `[#notify]`
|
|
1509
1603
|
- custom instrumenter object;
|
|
1510
|
-
- pre-configured value in `config['
|
|
1604
|
+
- pre-configured value in `config['instrumenter']`;
|
|
1511
1605
|
- `:instrument` - (optional) `[NilClass,Any]`
|
|
1512
1606
|
- custom instrumentation data wich will be passed to the instrumenter's payload with `:instrument` key;
|
|
1513
1607
|
- `:log_sampling_enabled` - (optional) `[Boolean]`
|
|
@@ -1615,7 +1709,7 @@ rql.clear_current_locks
|
|
|
1615
1709
|
- pre-configured value in `config['logger']`;
|
|
1616
1710
|
- `:instrumenter` - (optional) `[#notify]`
|
|
1617
1711
|
- custom instrumenter object;
|
|
1618
|
-
- pre-configured value in `config['
|
|
1712
|
+
- pre-configured value in `config['instrumenter']`;
|
|
1619
1713
|
- `:instrument` - (optional) `[NilClass,Any]`
|
|
1620
1714
|
- custom instrumentation data wich will be passed to the instrumenter's payload with `:instrument` key;
|
|
1621
1715
|
- `:log_sampling_enabled` - (optional) `[Boolean]`
|
|
@@ -1684,6 +1778,7 @@ rql.release_locks_of(host_id: rql.current_host_id, acquirer_id: rql.current_acqu
|
|
|
1684
1778
|
- the lock name which ttl should be extended;
|
|
1685
1779
|
- `milliseconds` - (required) `[Integer]`
|
|
1686
1780
|
- how many milliseconds should be added to the lock's TTL;
|
|
1781
|
+
- should be a positive `Integer`;
|
|
1687
1782
|
- `:read_write_mode` - (optional) `[Symbol]`
|
|
1688
1783
|
- `:write` (default) - extends the write lock (the behavior described below);
|
|
1689
1784
|
- `:read` - extends the read lock of the current acquirer only (see [Read/Write Locks](#readwrite-locks)):
|
|
@@ -1743,17 +1838,10 @@ rql.release_locks_of(host_id: rql.current_host_id, acquirer_id: rql.current_acqu
|
|
|
1743
1838
|
and for the read lock of the current acquirer, the number of extended read locks for `all_read_locks: true`;
|
|
1744
1839
|
- returns `{ ok: false, result: :async_expire_or_no_lock }` when a lock not found or a lock is already expired during
|
|
1745
1840
|
some steps of invocation (see **Important** section below);
|
|
1746
|
-
- **Important
|
|
1747
|
-
- the
|
|
1748
|
-
- the
|
|
1749
|
-
|
|
1750
|
-
- (2) set new ttl that is calculated as "current pttl + additional milliseconds";
|
|
1751
|
-
- the method uses Redis'es **CAS** (check-and-set) behavior;
|
|
1752
|
-
- what can happen during these steps:
|
|
1753
|
-
- lock is expired between commands or before the first command;
|
|
1754
|
-
- lock is expired before the second command;
|
|
1755
|
-
- lock is expired AND newly acquired by another process (so you will extend the
|
|
1756
|
-
totally new lock with fresh PTTL);
|
|
1841
|
+
- **Important** (`:write` mode):
|
|
1842
|
+
- the write lock TTL is extended atomically by one Lua script (`PTTL` + `PEXPIRE` with "current pttl + additional milliseconds");
|
|
1843
|
+
- the lock owner is not checked: the current write lock is extended, so when your lock is expired and newly obtained
|
|
1844
|
+
by another acquirer, the lock of that acquirer is extended;
|
|
1757
1845
|
- use it at your own risk and consider the async nature when calling this method;
|
|
1758
1846
|
|
|
1759
1847
|
```ruby
|
|
@@ -1909,6 +1997,8 @@ rql.locks_info # or rql.locks_info(scan_size: 123)
|
|
|
1909
1997
|
"hst_id"=>"rql:hst:41478/4320/4360/848818f09d8c3420",
|
|
1910
1998
|
"ts"=>1711607112.670343,
|
|
1911
1999
|
"ini_ttl"=>15000,
|
|
2000
|
+
"lock_key"=>"rql:lock:some-lock-123",
|
|
2001
|
+
"rw_mode"=>"write",
|
|
1912
2002
|
"rem_ttl"=>13998}},
|
|
1913
2003
|
{:lock=>"rql:lock:some-lock-456",
|
|
1914
2004
|
:status=>:released,
|
|
@@ -1929,8 +2019,8 @@ rql.locks_info # or rql.locks_info(scan_size: 123)
|
|
|
1929
2019
|
- returns `Set<Hash<Symbol,Any>>` (see [#queue_info](#queue_info) and examples below for details).
|
|
1930
2020
|
- contained data: `{ queue: String, requests: Array<Hash<String,Any>> }`
|
|
1931
2021
|
- `:queue` - `String` - lock key queue in Redis;
|
|
1932
|
-
- `:requests` - `Array<Hash<String,Any>>` - lock requests in the que with their acquirer id, score
|
|
1933
|
-
(`"rw_mode"` - `"write"` for `rql:lock_queue:*`, `"read"` for read lock queues `rql:lock_read_queue:*`).
|
|
2022
|
+
- `:requests` - `Array<Hash<String,Any>>` - lock requests in the que with their acquirer id, score, requested lock mode
|
|
2023
|
+
(`"rw_mode"` - `"write"` for `rql:lock_queue:*`, `"read"` for read lock queues `rql:lock_read_queue:*`) and priority.
|
|
1934
2024
|
|
|
1935
2025
|
```ruby
|
|
1936
2026
|
rql.queues_info # or rql.qeuues_info(scan_size: 123)
|
|
@@ -1938,13 +2028,13 @@ rql.queues_info # or rql.qeuues_info(scan_size: 123)
|
|
|
1938
2028
|
=> #<Set:
|
|
1939
2029
|
{{:queue=>"rql:lock_queue:some-lock-123",
|
|
1940
2030
|
:requests=>
|
|
1941
|
-
[{"acq_id"=>"rql:acq:38529/4500/4520/4360/66093702f24a3129", "score"=>1711606640.540842, "rw_mode"=>"write"},
|
|
1942
|
-
{"acq_id"=>"rql:acq:38529/4580/4600/4360/66093702f24a3129", "score"=>1711606640.540906, "rw_mode"=>"write"},
|
|
1943
|
-
{"acq_id"=>"rql:acq:38529/4620/4640/4360/66093702f24a3129", "score"=>1711606640.5409632, "rw_mode"=>"write"}]},
|
|
2031
|
+
[{"acq_id"=>"rql:acq:38529/4500/4520/4360/66093702f24a3129", "score"=>1711606640.540842, "rw_mode"=>"write", "priority"=>0},
|
|
2032
|
+
{"acq_id"=>"rql:acq:38529/4580/4600/4360/66093702f24a3129", "score"=>1711606640.540906, "rw_mode"=>"write", "priority"=>0},
|
|
2033
|
+
{"acq_id"=>"rql:acq:38529/4620/4640/4360/66093702f24a3129", "score"=>1711606640.5409632, "rw_mode"=>"write", "priority"=>0}]},
|
|
1944
2034
|
{:queue=>"rql:lock_read_queue:some-lock-456",
|
|
1945
2035
|
:requests=>
|
|
1946
|
-
[{"acq_id"=>"rql:acq:38529/4380/4400/4360/66093702f24a3129", "score"=>1711606640.540722, "rw_mode"=>"read"},
|
|
1947
|
-
{"acq_id"=>"rql:acq:38529/4420/4440/4360/66093702f24a3129", "score"=>1711606640.5407748, "rw_mode"=>"read"}]},
|
|
2036
|
+
[{"acq_id"=>"rql:acq:38529/4380/4400/4360/66093702f24a3129", "score"=>1711606640.540722, "rw_mode"=>"read", "priority"=>0},
|
|
2037
|
+
{"acq_id"=>"rql:acq:38529/4420/4440/4360/66093702f24a3129", "score"=>1711606640.5407748, "rw_mode"=>"read", "priority"=>0}]},
|
|
1948
2038
|
...}>
|
|
1949
2039
|
```
|
|
1950
2040
|
---
|
|
@@ -1956,8 +2046,10 @@ rql.queues_info # or rql.qeuues_info(scan_size: 123)
|
|
|
1956
2046
|
In some cases your lock requests may become "dead". It means that your lock request lives in lock queue in Redis without
|
|
1957
2047
|
any processing. It can happen when your processs that are enqueeud to the lock queue is failed unexpectedly (for some reason)
|
|
1958
2048
|
before the lock acquire moment occurs and when no any other process does not need this lock anymore.
|
|
1959
|
-
For this case your lock
|
|
1960
|
-
|
|
2049
|
+
For this case your lock request will be cleared only when another lock request meets it: a lock request of the same
|
|
2050
|
+
[priority](#lock-request-prioritization) (lock acquirement drops the expired requests of its priority) or a `:queued` lock request
|
|
2051
|
+
it blocks (the head of the queue, an earlier request of the other read/write mode); the dead requests of other priorities stay
|
|
2052
|
+
in the queue.
|
|
1961
2053
|
|
|
1962
2054
|
NOTE: with the enabled [Lock Index](#lock-index) the lock request queue lives as long as its latest lock request (the queue TTL
|
|
1963
2055
|
is refreshed with the request `queue_ttl` on each lock request), so the queue of dead requests expires by itself (queues of older
|
|
@@ -1966,7 +2058,12 @@ RQL versions and queues created with the disabled lock index have no TTL: see [#
|
|
|
1966
2058
|
In order to help with these dead requests you may periodically call `#clear_dead_requests`
|
|
1967
2059
|
with corresponding `:dead_ttl` option, that is pre-configured by default via `config['dead_request_ttl']`.
|
|
1968
2060
|
Lock queues are found via the redis `SCAN` (one pass) or via the [Lock Index](#lock-index) (one `ZSCAN` pass, the keyspace is
|
|
1969
|
-
never scanned: the enabled lock index).
|
|
2061
|
+
never scanned: the enabled lock index). Each batch of lock queues takes two round trips: the lock requests are read, then the dead
|
|
2062
|
+
requests of each [priority](#lock-request-prioritization) found in the queues are dropped (no second round trip without dead requests).
|
|
2063
|
+
With `by_priority: true` the lock requests are not read: only the first lock request of each priority is read (the dead requests
|
|
2064
|
+
of a priority are found by its first lock request): a batch takes one round trip per priority of its lock queue with the most
|
|
2065
|
+
priorities + 1 (the dead requests of a priority are dropped in the round trip that reads the next priority), so the transfer does not
|
|
2066
|
+
depend on the length of the lock queues: use it for long lock queues with few priorities.
|
|
1970
2067
|
|
|
1971
2068
|
`:dead_ttl` option is required because of it is no any **fast** and **resource-free** way to understand which request
|
|
1972
2069
|
is dead now and is it really dead cuz each request queue can host their requests with
|
|
@@ -1979,14 +2076,20 @@ Accepts:
|
|
|
1979
2076
|
- `:scan_size` - (optional) `[Integer]`
|
|
1980
2077
|
- the batch of scanned keys for Redis'es SCAN command;
|
|
1981
2078
|
- (the enabled lock index) the `ZSCAN` count of the [Lock Index](#lock-index) pass and the number of locks (both lock queues
|
|
1982
|
-
of each lock) processed in one
|
|
2079
|
+
of each lock) processed in one batch;
|
|
1983
2080
|
- has a preconfigured valie in `config['lock_release_batch_size']`;
|
|
2081
|
+
- `:by_priority` - (optional) `[Boolean]`
|
|
2082
|
+
- read only the first lock request of each [priority](#lock-request-prioritization) in the lock queues instead of all lock
|
|
2083
|
+
requests: a batch of lock queues takes one round trip per priority of its lock queue with the most priorities + 1 (instead
|
|
2084
|
+
of two round trips), the transfer does not depend on the length of the lock queues (for long lock queues with few
|
|
2085
|
+
priorities);
|
|
2086
|
+
- `false` by default (all lock requests are read);
|
|
1984
2087
|
- `:logger` - (optional) `[::Logger,#debug]`
|
|
1985
2088
|
- custom logger object;
|
|
1986
2089
|
- pre-configured in `config['logger']`;
|
|
1987
2090
|
- `:instrumenter` - (optional) `[#notify]`
|
|
1988
2091
|
- custom instrumenter object;
|
|
1989
|
-
- pre-configured in `config['
|
|
2092
|
+
- pre-configured in `config['instrumenter']`;
|
|
1990
2093
|
- `:instrument` - (optional) `[NilClass,Any]`
|
|
1991
2094
|
- custom instrumentation data wich will be passed to the instrumenter's payload with :instrument key;
|
|
1992
2095
|
- `nil` by default (no additional data);
|
|
@@ -2017,7 +2120,8 @@ Accepts:
|
|
|
2017
2120
|
- makes sense when instrumentation sampling is enabled;
|
|
2018
2121
|
- `false` by default;
|
|
2019
2122
|
|
|
2020
|
-
Returns: `{ ok: true, processed_queues: Set<String> }` returns the
|
|
2123
|
+
Returns: `{ ok: true, result: { processed_queues: Set<String> } }` returns the set of processed lock request queues
|
|
2124
|
+
(write and read lock request queues);
|
|
2021
2125
|
|
|
2022
2126
|
```ruby
|
|
2023
2127
|
rql.clear_dead_requests(dead_ttl: 60 * 60 * 1000) # 1 hour in milliseconds
|
|
@@ -2025,13 +2129,19 @@ rql.clear_dead_requests(dead_ttl: 60 * 60 * 1000) # 1 hour in milliseconds
|
|
|
2025
2129
|
# =>
|
|
2026
2130
|
{
|
|
2027
2131
|
ok: true,
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2132
|
+
result: {
|
|
2133
|
+
processed_queues: #<Set: {
|
|
2134
|
+
"rql:lock_queue:some-lock-123",
|
|
2135
|
+
"rql:lock_queue:some-lock-456",
|
|
2136
|
+
"rql:lock_read_queue:some-lock-456",
|
|
2137
|
+
"rql:lock_queue:your-other-lock",
|
|
2138
|
+
...
|
|
2139
|
+
}>
|
|
2140
|
+
}
|
|
2034
2141
|
}
|
|
2142
|
+
|
|
2143
|
+
# long lock queues with few priorities: only the first lock request of each priority is read
|
|
2144
|
+
rql.clear_dead_requests(dead_ttl: 60 * 60 * 1000, by_priority: true)
|
|
2035
2145
|
```
|
|
2036
2146
|
|
|
2037
2147
|
---
|
|
@@ -2061,6 +2171,41 @@ rql.clear_dead_requests(dead_ttl: 60 * 60 * 1000) # 1 hour in milliseconds
|
|
|
2061
2171
|
- `:lock_index_grace_period` - (optional) `[Integer]`
|
|
2062
2172
|
- the lock index grace period (in milliseconds);
|
|
2063
2173
|
- pre-configured in `config['lock_index_grace_period']`;
|
|
2174
|
+
- `:logger` - (optional) `[::Logger,#debug]`
|
|
2175
|
+
- custom logger object;
|
|
2176
|
+
- pre-configured in `config['logger']`;
|
|
2177
|
+
- `:instrumenter` - (optional) `[#notify]`
|
|
2178
|
+
- custom instrumenter object;
|
|
2179
|
+
- pre-configured in `config['instrumetner']`;
|
|
2180
|
+
- `:instrument` - (optional) `[NilClass,Any]`;
|
|
2181
|
+
- custom instrumentation data wich will be passed to the instrumenter's payload with :instrument key;
|
|
2182
|
+
- `nil` by default (no additional data);
|
|
2183
|
+
- `:log_sampling_enabled` - (optional) `[Boolean]`
|
|
2184
|
+
- enables **log sampling**;
|
|
2185
|
+
- pre-configured in `config['log_sampling_enabled']`;
|
|
2186
|
+
- `:log_sampling_percent` - (optional) `[Integer]`
|
|
2187
|
+
- **log sampling**:the percent of cases that should be logged;
|
|
2188
|
+
- pre-configured in `config['log_sampling_percent']`;
|
|
2189
|
+
- `:log_sampler` - (optional) `[#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]`
|
|
2190
|
+
- **log sampling**: percent-based log sampler that decides should be RQL case logged or not;
|
|
2191
|
+
- pre-configured in `config['log_sampler']`;
|
|
2192
|
+
- `log_sample_this` - (optional) `[Boolean]`
|
|
2193
|
+
- marks the method that everything should be logged despite the enabled log sampling;
|
|
2194
|
+
- makes sense when log sampling is enabled;
|
|
2195
|
+
- `false` by default;
|
|
2196
|
+
- `:instr_sampling_enabled` - (optional) `[Boolean]`
|
|
2197
|
+
- enables **instrumentaion sampling**;
|
|
2198
|
+
- pre-configured in `config['instr_sampling_enabled']`;
|
|
2199
|
+
- `instr_sampling_percent` - (optional) `[Integer]`
|
|
2200
|
+
- the percent of cases that should be instrumented;
|
|
2201
|
+
- pre-configured in `config['instr_sampling_percent']`;
|
|
2202
|
+
- `instr_sampler` - (optional) `[#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]`
|
|
2203
|
+
- percent-based log sampler that decides should be RQL case instrumented or not;
|
|
2204
|
+
- pre-configured in `config['instr_sampler']`;
|
|
2205
|
+
- `instr_sample_this` - (optional) `[Boolean]`
|
|
2206
|
+
- marks the method that everything should be instrumneted despite the enabled instrumentation sampling;
|
|
2207
|
+
- makes sense when instrumentation sampling is enabled;
|
|
2208
|
+
- `false` by default;
|
|
2064
2209
|
- returns `{ ok: true, result: { idx_key_cnt: Integer } }` (the number of indexed keys);
|
|
2065
2210
|
|
|
2066
2211
|
```ruby
|
|
@@ -2235,6 +2380,8 @@ rql.possible_host_ids
|
|
|
2235
2380
|
# NOTE: auto-swarm your RQL client after initalization (run swarm elements and their supervisor)
|
|
2236
2381
|
config['swarm.auto_swarm'] = false
|
|
2237
2382
|
|
|
2383
|
+
# NOTE: the periods, the zombie ttl and the scan sizes should be positive Integers
|
|
2384
|
+
|
|
2238
2385
|
# supervisor configs
|
|
2239
2386
|
config['swarm.supervisor.liveness_probing_period'] = 2 # NOTE: in seconds
|
|
2240
2387
|
|
|
@@ -2514,19 +2661,121 @@ rql.possible_host_ids
|
|
|
2514
2661
|
- can be customized in methods `#lock` and `#lock!` via `:access_strategy` attribute (see method signatures of #lock and #lock! methods);
|
|
2515
2662
|
- supports different strategies:
|
|
2516
2663
|
- `:queued` (FIFO): the classic queued behavior (default), your lock will be obitaned if you are first in queue and the required lock is free;
|
|
2664
|
+
the queue is ordered by the [lock request priorities](#lock-request-prioritization) (FIFO inside the same priority);
|
|
2517
2665
|
- `:random` (RANDOM): obtain a lock without checking the positions in the queue (but with checking the limist, retries, timeouts and so on). if lock is free to obtain - it will be obtained;
|
|
2666
|
+
(the priorities of other lock requests are ignored too; the lock request itself is queued at the position of its
|
|
2667
|
+
[priority](#lock-request-prioritization) like a `:queued` one: `:queued` lock requests wait for it);
|
|
2518
2668
|
- for current implementation detalis check:
|
|
2519
2669
|
- [Configuration](#configuration) documentation: see `config['default_access_strategy']` config docs;
|
|
2520
2670
|
- [#lock](#lock---obtain-a-lock) method documentation: see `access_strategy` attribute docs;
|
|
2521
2671
|
|
|
2522
2672
|
---
|
|
2523
2673
|
|
|
2674
|
+
## Lock Request Prioritization
|
|
2675
|
+
|
|
2676
|
+
<sup>\[[back to top](#table-of-contents)\]</sup>
|
|
2677
|
+
|
|
2678
|
+
- `priority:` attribute of `#lock`, `#lock!`, `#lock_series` and `#lock_series!` (`config['default_lock_priority']` by default):
|
|
2679
|
+
an Integer from `-100` (the lowest priority) to `100` (the highest priority), `0` - (**default**) normal priority,
|
|
2680
|
+
or a priority name (Symbol) of `config['lock_priorities']`;
|
|
2681
|
+
- priority names (`config['lock_priorities']`, a priority name => its Integer priority):
|
|
2682
|
+
- default names: `:lowest` (`-100`), `:low` (`-50`), `:normal` (`0`), `:high` (`50`), `:highest` (`100`);
|
|
2683
|
+
- name the priorities of your application (keep the default names with `merge`; the default value is frozen: assign
|
|
2684
|
+
a new hash);
|
|
2685
|
+
- a name is resolved to its Integer priority on each lock acquirement: the lock request queues, `#queue_info`/`#queues_info`,
|
|
2686
|
+
logs and instrumentation have the Integer priority; an unknown name fails with `RedisQueuedLocks::ArgumentError`;
|
|
2687
|
+
- `config['default_lock_priority']` can be a priority name too: an unknown one fails the configuration with
|
|
2688
|
+
`RedisQueuedLocks::ConfigValidationError` (checked after the configuration block, the keys can be assigned in any order,
|
|
2689
|
+
and on a direct `config[...] =` assignment);
|
|
2690
|
+
- `:queued` access strategy (default): the lock request is placed in the lock request queue after all requests of the same
|
|
2691
|
+
or higher priority and before all requests of the lower priority: strict priority between priorities, FIFO inside the same
|
|
2692
|
+
priority (read and write lock requests too, see [Read/Write Locks](#readwrite-locks));
|
|
2693
|
+
|
|
2694
|
+
```ruby
|
|
2695
|
+
# a balance operation goes ahead of the email notifications that are already waiting for the lock
|
|
2696
|
+
rql.lock("user_#{user.id}", priority: 50) { process_balance }
|
|
2697
|
+
|
|
2698
|
+
# a payment goes ahead of the balance operations
|
|
2699
|
+
rql.lock("user_#{user.id}", priority: 90) { process_payment }
|
|
2700
|
+
|
|
2701
|
+
# email notifications wait for all lock requests of higher priorities
|
|
2702
|
+
rql.lock("user_#{user.id}", priority: -10) { send_email }
|
|
2703
|
+
|
|
2704
|
+
# normal priority by default
|
|
2705
|
+
rql.lock("user_#{user.id}") { update_profile } # priority: 0
|
|
2706
|
+
|
|
2707
|
+
# the default priority of the client (for example, in background workers)
|
|
2708
|
+
rql.configure { |config| config['default_lock_priority'] = -10 }
|
|
2709
|
+
|
|
2710
|
+
# priority names: the default ones (:lowest, :low, :normal, :high, :highest) and the names of your application
|
|
2711
|
+
rql.configure do |config|
|
|
2712
|
+
config['lock_priorities'] = config['lock_priorities'].merge(payment: 90, balance: 50, email: -10)
|
|
2713
|
+
end
|
|
2714
|
+
rql.lock("user_#{user.id}", priority: :payment) { process_payment } # priority: 90
|
|
2715
|
+
rql.lock("user_#{user.id}", priority: :email) { send_email } # priority: -10
|
|
2716
|
+
rql.lock("user_#{user.id}", priority: :high) { update_profile } # priority: 50
|
|
2717
|
+
```
|
|
2718
|
+
|
|
2719
|
+
- how it works:
|
|
2720
|
+
- the position of the lock request in the queue (the score) is the request time shifted by the priority band:
|
|
2721
|
+
`request time - priority * 2**32 seconds` (each priority has its own range of positions; the default priority `0` keeps
|
|
2722
|
+
the request time: the positions of RQL versions without priorities);
|
|
2723
|
+
- every lock request drops dead requests (older than `queue_ttl`) of its own priority in both lock queues (in the same round
|
|
2724
|
+
trip as before priorities); dead requests of other priorities that block it (the head of the queue and the earlier request
|
|
2725
|
+
of the other read/write mode: the priority and the request time are decoded from the position) are dropped with all dead
|
|
2726
|
+
requests of their priorities in one more round trip of the same attempt, so a dead request of any priority never blocks the
|
|
2727
|
+
queue and the cost of an attempt does not depend on the number of priorities in use;
|
|
2728
|
+
- dead requests of lower priorities never block: they are dropped by the lock requests of their priority (or lower ones)
|
|
2729
|
+
and by `#clear_dead_requests` (it drops the dead requests of every priority found in the queues);
|
|
2730
|
+
- lock requests at infinite positions (`-inf`/`+inf`: manual writes of the lock queues, RQL never writes them) have the
|
|
2731
|
+
bound priorities (`100`/`-100`) in `#queue_info`/`#queues_info` and logs; a request at `-inf` is a dead one (like before
|
|
2732
|
+
priorities): it is dropped when it blocks a lock request and by `#clear_dead_requests`; a request at `+inf` is behind all
|
|
2733
|
+
requests and is never dropped as a dead one;
|
|
2734
|
+
- `#queue_info`/`#queues_info` return the priority of each lock request (`"priority"`); the lock acquirement logs and
|
|
2735
|
+
instrumentation events (classic, reentrant and lock series ones) have the `priority` of the lock request, the `log_lock_try`
|
|
2736
|
+
logs also have the priorities of the requests ahead (`first_priority_in_queue`, `ahead_priority`, `dead_priority`;
|
|
2737
|
+
`first_priority_in_queue => nil` when the lock queue has no requests);
|
|
2738
|
+
- limitations and recommendations:
|
|
2739
|
+
- the priority orders the waiting lock requests only: the obtained lock is never taken away (no preemption);
|
|
2740
|
+
- strict priority: a continuous flow of requests of a higher priority can starve the requests of lower priorities (they
|
|
2741
|
+
reach their `timeout`/`retry_count` limits): give them longer limits;
|
|
2742
|
+
- the released lock is passed by polling: the next lock request notices the released lock on its next attempt
|
|
2743
|
+
(`retry_delay`): use a shorter `retry_delay` for the important logic;
|
|
2744
|
+
- the default `retry_count` (`3`) gives a short waiting time: the priority matters for the requests that wait longer
|
|
2745
|
+
(`retry_count: nil` with `timeout`);
|
|
2746
|
+
- requests of the same priority made within ~61 µs of each other are ordered by their acquirer ids (the precision of
|
|
2747
|
+
the shifted positions);
|
|
2748
|
+
- each lock request drops dead requests with its own `queue_ttl` (the requests of its priority and the requests that block it):
|
|
2749
|
+
a request that waits longer than the shorter `queue_ttl` of such requests is dropped by their attempts and comes back at its
|
|
2750
|
+
old position on its own attempts (its own `queue_ttl` is not reached yet), so the requests behind it with a shorter
|
|
2751
|
+
`queue_ttl` don't wait for it: it loses its place (and the advantage of its priority) to them, the lock goes to the request
|
|
2752
|
+
whose attempt finds the lock free first; use the same `queue_ttl` for the lock requests of a lock (a longer `queue_ttl` does
|
|
2753
|
+
not keep a request in the queue);
|
|
2754
|
+
- `:random` access strategy: the lock request does not wait for its turn (the order of the queues and the priorities of other
|
|
2755
|
+
requests are ignored), but it is queued at the position of its priority: `:queued` lock requests wait for it (a `:random`
|
|
2756
|
+
request of a high priority holds back the waiting `:queued` requests of lower priorities until it obtains the lock or
|
|
2757
|
+
leaves the queue);
|
|
2758
|
+
- dead lock requests are dropped by the lock requests that meet them (the requests of their priority and the requests they
|
|
2759
|
+
block): the dead requests of the priorities that no lock request uses any more (and the dead requests met only by `:random`
|
|
2760
|
+
lock requests) stay in the queues; they never block (an accepted trade-off of the head-driven cleanup: the cost of an
|
|
2761
|
+
attempt does not depend on the number of priorities); without the [Lock Index](#lock-index) (its queues expire) and the
|
|
2762
|
+
[swarm](#swarm-mode-and-zombie-locks) (it drops the requests of dead processes) call `#clear_dead_requests` periodically
|
|
2763
|
+
(e.g. from a scheduled job) to drop them (`by_priority: true` for long lock queues: only the first lock request of each
|
|
2764
|
+
priority is read);
|
|
2765
|
+
- all processes that share locks should use RQL versions with priorities before positive priorities are used (`:high`,
|
|
2766
|
+
`:highest`, any priority above `0`): older versions drop such lock requests as dead ones (the order is broken; the mutual
|
|
2767
|
+
exclusion is not affected: lock queues never take part in it); the default priority (`0`) and negative priorities
|
|
2768
|
+
(`:low`, `:lowest`) work with older versions too: older versions keep such requests behind their own ones and never drop
|
|
2769
|
+
them as dead ones (the dead ones are dropped by the upgraded processes);
|
|
2770
|
+
|
|
2771
|
+
---
|
|
2772
|
+
|
|
2524
2773
|
## Read/Write Locks
|
|
2525
2774
|
|
|
2526
2775
|
<sup>\[[back to top](#table-of-contents)\]</sup>
|
|
2527
2776
|
|
|
2528
2777
|
- `read_write_mode:` attribute of `#lock` and `#lock!`:
|
|
2529
|
-
- `:write` (default) - exclusive lock (the classic RQL lock): waits for the write lock and for all read locks;
|
|
2778
|
+
- `:write` (**default**) - exclusive lock (the classic RQL lock): waits for the write lock and for all read locks;
|
|
2530
2779
|
- `:read` - shared lock: waits for the write lock only (read locks of different acquirers work in parallel);
|
|
2531
2780
|
|
|
2532
2781
|
```ruby
|
|
@@ -2535,17 +2784,22 @@ rql.lock('report', read_write_mode: :read) { read_report }
|
|
|
2535
2784
|
|
|
2536
2785
|
# writer waits for all readers (and readers wait for the writer)
|
|
2537
2786
|
rql.lock('report', read_write_mode: :write) { rebuild_report }
|
|
2538
|
-
|
|
2787
|
+
|
|
2788
|
+
# write mode is default:
|
|
2789
|
+
rql.lock('report') { rebuild_report } # read_write_mode: :write by default
|
|
2539
2790
|
```
|
|
2540
2791
|
|
|
2541
2792
|
- lock request ordering (`access_strategy`):
|
|
2542
2793
|
- `:queued` (default) - FIFO between modes: read lock request waits for every earlier write lock request,
|
|
2543
2794
|
write lock request waits for every earlier read and write lock request (so writers are not starved by a
|
|
2544
|
-
continuous flow of readers and consecutive readers obtain the lock together);
|
|
2545
|
-
|
|
2795
|
+
continuous flow of readers and consecutive readers obtain the lock together); with
|
|
2796
|
+
[lock request priorities](#lock-request-prioritization) "earlier" means "ahead in the queue": a request of a higher
|
|
2797
|
+
priority is ahead of all requests of lower priorities in both modes (FIFO inside the same priority);
|
|
2798
|
+
- `:random` - the order of the lock queues is ignored: any free lock is obtained (a continuous flow of readers can starve
|
|
2799
|
+
writers); the lock request itself is queued like any lock request (`:queued` lock requests wait for it);
|
|
2546
2800
|
- read locks:
|
|
2547
2801
|
- each read lock has its own `ttl` and is released (after the block execution) or expired independently of other read locks;
|
|
2548
|
-
- `ttl` should be a positive `Integer
|
|
2802
|
+
- `ttl` should be a positive `Integer`;
|
|
2549
2803
|
- each read lock has its own data with the same structure as the write lock data: acquirer, host, timestamp,
|
|
2550
2804
|
initial ttl, `:meta` (with the same reserved keys) and reentrant lock data (`spc_cnt`, `spc_ext_ttl`, ...);
|
|
2551
2805
|
the read lock data lives as long as the read lock and is returned by `#lock_info`/`#locks_info` for each reader
|
|
@@ -2641,12 +2895,13 @@ rql.lock('report') { rebuild_report } # (write mode is the default mode)
|
|
|
2641
2895
|
- default logs (raised from `#lock`/`#lock!`):
|
|
2642
2896
|
|
|
2643
2897
|
```ruby
|
|
2644
|
-
"[redis_queued_locks.start_lock_obtaining]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2645
|
-
"[redis_queued_locks.start_try_to_lock_cycle]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2646
|
-
"[redis_queued_locks.dead_score_reached__reset_acquirer_position]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2647
|
-
"[redis_queued_locks.lock_obtained]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
2648
|
-
"[redis_queued_locks.
|
|
2649
|
-
"[redis_queued_locks.
|
|
2898
|
+
"[redis_queued_locks.start_lock_obtaining]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
2899
|
+
"[redis_queued_locks.start_try_to_lock_cycle]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
2900
|
+
"[redis_queued_locks.dead_score_reached__reset_acquirer_position]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
2901
|
+
"[redis_queued_locks.lock_obtained]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time");
|
|
2902
|
+
"[redis_queued_locks.lock_obtaining_failed]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time", "result");
|
|
2903
|
+
"[redis_queued_locks.extendable_reentrant_lock_obtained]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time");
|
|
2904
|
+
"[redis_queued_locks.reentrant_lock_obtained]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time");
|
|
2650
2905
|
"[redis_queued_locks.fail_fast_or_limits_reached_or_deadlock__dequeue]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2651
2906
|
"[redis_queued_locks.expire_lock]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2652
2907
|
"[redis_queued_locks.decrease_lock]" # (logs "lock_key", "decreased_ttl", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
@@ -2663,24 +2918,38 @@ rql.lock('report') { rebuild_report } # (write mode is the default mode)
|
|
|
2663
2918
|
"[redis_queued_locks.try_lock.reentrant_lock__work_through]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "spc_status", "last_spc_ts");
|
|
2664
2919
|
"[redis_queued_locks.try_lock.single_process_lock_conflict__dead_lock]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "spc_status", "last_spc_ts");
|
|
2665
2920
|
"[redis_queued_locks.try_lock.single_process_lock_conflict__lock_upgrade]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "spc_status", "last_spc_ts");
|
|
2666
|
-
"[redis_queued_locks.try_lock.acq_added_to_queue]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2921
|
+
"[redis_queued_locks.try_lock.acq_added_to_queue]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
2667
2922
|
"[redis_queued_locks.try_lock.remove_expired_acqs]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2668
|
-
"[redis_queued_locks.try_lock.
|
|
2923
|
+
"[redis_queued_locks.try_lock.dead_request_removed]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "dead_acq_id", "dead_priority");
|
|
2924
|
+
"[redis_queued_locks.try_lock.get_first_from_queue]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "first_acq_id_in_queue", "first_priority_in_queue");
|
|
2669
2925
|
"[redis_queued_locks.try_lock.exit__queue_ttl_reached]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2670
|
-
"[redis_queued_locks.try_lock.exit__no_first]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "first_acq_id_in_queue", "<current_lock_data>");
|
|
2671
|
-
"[redis_queued_locks.try_lock.exit__lock_still_obtained]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "first_acq_id_in_queue", "locked_by_acq_id", "<current_lock_data>");
|
|
2672
|
-
"[redis_queued_locks.try_lock.exit__write_request_ahead]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "ahead_acq_id");
|
|
2673
|
-
"[redis_queued_locks.try_lock.exit__read_request_ahead]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "ahead_acq_id");
|
|
2674
|
-
"[redis_queued_locks.try_lock.exit__read_lock_still_obtained]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "first_acq_id_in_queue");
|
|
2926
|
+
"[redis_queued_locks.try_lock.exit__no_first]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "first_acq_id_in_queue", "first_priority_in_queue", "<current_lock_data>");
|
|
2927
|
+
"[redis_queued_locks.try_lock.exit__lock_still_obtained]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "first_acq_id_in_queue", "first_priority_in_queue", "locked_by_acq_id", "<current_lock_data>");
|
|
2928
|
+
"[redis_queued_locks.try_lock.exit__write_request_ahead]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "ahead_acq_id", "ahead_priority");
|
|
2929
|
+
"[redis_queued_locks.try_lock.exit__read_request_ahead]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "ahead_acq_id", "ahead_priority");
|
|
2930
|
+
"[redis_queued_locks.try_lock.exit__read_lock_still_obtained]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "first_acq_id_in_queue", "first_priority_in_queue");
|
|
2675
2931
|
"[redis_queued_locks.try_lock.obtain__free_to_acquire]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2676
2932
|
```
|
|
2677
2933
|
|
|
2678
2934
|
- lock series logs (raised from `#lock_series`/`#lock_series!`, PoC; `rw_mode` is the mode of the series):
|
|
2679
2935
|
|
|
2680
2936
|
```ruby
|
|
2681
|
-
"[redis_queued_locks.start_lock_series_obtaining]" # (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2682
|
-
"[redis_queued_locks.lock_series_obtained]" # (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
2683
|
-
"[redis_queued_locks.expire_lock_series]" # (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2937
|
+
"[redis_queued_locks.start_lock_series_obtaining]" # (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
2938
|
+
"[redis_queued_locks.lock_series_obtained]" # (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time");
|
|
2939
|
+
"[redis_queued_locks.expire_lock_series]" # (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
2940
|
+
```
|
|
2941
|
+
|
|
2942
|
+
- operation logs (one log line per operation that changes Redis keys, with the time of the whole operation; the same data as
|
|
2943
|
+
the instrumentation event of the operation without `instrument`):
|
|
2944
|
+
|
|
2945
|
+
```ruby
|
|
2946
|
+
"[redis_queued_locks.explicit_lock_release]" # (logs "lock_key", "lock_key_queue", "rel_time", "at");
|
|
2947
|
+
"[redis_queued_locks.explicit_read_lock_release]" # (logs "lock_key", "acq_id", "lock_res", "rel_time", "at");
|
|
2948
|
+
"[redis_queued_locks.explicit_all_locks_release]" # (logs "at", "rel_time", "rel_key_cnt");
|
|
2949
|
+
"[redis_queued_locks.release_locks_of]" # (logs "at", "hst_id", "acq_id", "rel_time", "rel_key_cnt", "tch_queue_cnt");
|
|
2950
|
+
"[redis_queued_locks.extend_lock_ttl]" # (logs "lock_key", "rw_mode", "all_read_locks", "ext_ttl", "extended_locks_count", "ext_time", "at");
|
|
2951
|
+
"[redis_queued_locks.clear_dead_requests]" # (logs "dead_ttl", "prc_queue_cnt", "rel_time", "at");
|
|
2952
|
+
"[redis_queued_locks.reindex_locks]" # (logs "idx_key_cnt", "idx_time", "at");
|
|
2684
2953
|
```
|
|
2685
2954
|
|
|
2686
2955
|
- read/write try-lock logs (see [Read/Write Locks](#readwrite-locks)):
|
|
@@ -2698,7 +2967,7 @@ rql.lock('report') { rebuild_report } # (write mode is the default mode)
|
|
|
2698
2967
|
<sup>\[[back to top](#table-of-contents)\]</sup>
|
|
2699
2968
|
|
|
2700
2969
|
**NOTICE**: logging can be sampled via:
|
|
2701
|
-
- `config['
|
|
2970
|
+
- `config['log_sampling_enabled'] = true` (**false** by default);
|
|
2702
2971
|
- `config['log_sampler'] = RedisQueuedLocks::Logging::Sampler` (used by default);
|
|
2703
2972
|
- see **RedisQueuedLocks::Logging::Sampler** implementation in source code for customization details;
|
|
2704
2973
|
|
|
@@ -2708,18 +2977,26 @@ rql.lock('report') { rebuild_report } # (write mode is the default mode)
|
|
|
2708
2977
|
# - should implement `debug(progname = nil, &block)` (minimal requirement) or be an instance of Ruby's `::Logger` class/subclass;
|
|
2709
2978
|
# - supports `SemanticLogger::Logger` (see "semantic_logger" gem)
|
|
2710
2979
|
# - at this moment the only debug logs are realised in following cases:
|
|
2711
|
-
# - "[redis_queued_locks.start_lock_obtaining]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2712
|
-
# - "[redis_queued_locks.start_try_to_lock_cycle]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2713
|
-
# - "[redis_queued_locks.dead_score_reached__reset_acquirer_position]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2714
|
-
# - "[redis_queued_locks.lock_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
2715
|
-
# - "[redis_queued_locks.
|
|
2716
|
-
# - "[redis_queued_locks.
|
|
2980
|
+
# - "[redis_queued_locks.start_lock_obtaining]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
2981
|
+
# - "[redis_queued_locks.start_try_to_lock_cycle]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
2982
|
+
# - "[redis_queued_locks.dead_score_reached__reset_acquirer_position]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
2983
|
+
# - "[redis_queued_locks.lock_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time");
|
|
2984
|
+
# - "[redis_queued_locks.lock_obtaining_failed]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time", "result");
|
|
2985
|
+
# - "[redis_queued_locks.extendable_reentrant_lock_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time");
|
|
2986
|
+
# - "[redis_queued_locks.reentrant_lock_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time");
|
|
2717
2987
|
# - "[redis_queued_locks.fail_fast_or_limits_reached_or_deadlock__dequeue]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2718
2988
|
# - "[redis_queued_locks.expire_lock]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2719
2989
|
# - "[redis_queued_locks.decrease_lock]" (logs "lock_key", "decreased_ttl", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2720
|
-
# - "[redis_queued_locks.start_lock_series_obtaining]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2721
|
-
# - "[redis_queued_locks.lock_series_obtained]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
2722
|
-
# - "[redis_queued_locks.expire_lock_series]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2990
|
+
# - "[redis_queued_locks.start_lock_series_obtaining]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
2991
|
+
# - "[redis_queued_locks.lock_series_obtained]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time");
|
|
2992
|
+
# - "[redis_queued_locks.expire_lock_series]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
2993
|
+
# - "[redis_queued_locks.explicit_lock_release]" (logs "lock_key", "lock_key_queue", "rel_time", "at");
|
|
2994
|
+
# - "[redis_queued_locks.explicit_read_lock_release]" (logs "lock_key", "acq_id", "lock_res", "rel_time", "at");
|
|
2995
|
+
# - "[redis_queued_locks.explicit_all_locks_release]" (logs "at", "rel_time", "rel_key_cnt");
|
|
2996
|
+
# - "[redis_queued_locks.release_locks_of]" (logs "at", "hst_id", "acq_id", "rel_time", "rel_key_cnt", "tch_queue_cnt");
|
|
2997
|
+
# - "[redis_queued_locks.extend_lock_ttl]" (logs "lock_key", "rw_mode", "all_read_locks", "ext_ttl", "extended_locks_count", "ext_time", "at");
|
|
2998
|
+
# - "[redis_queued_locks.clear_dead_requests]" (logs "dead_ttl", "prc_queue_cnt", "rel_time", "at");
|
|
2999
|
+
# - "[redis_queued_locks.reindex_locks]" (logs "idx_key_cnt", "idx_time", "at");
|
|
2723
3000
|
# - "rw_mode" is the requested lock mode ('write'/'read'), the held lock mode for "expire_lock" and "decrease_lock",
|
|
2724
3001
|
# the mode of the series for lock series logs;
|
|
2725
3002
|
# - by default uses VoidLogger that does nothing;
|
|
@@ -2737,15 +3014,16 @@ config['logger'] = RedisQueuedLocks::Logging::VoidLogger
|
|
|
2737
3014
|
# - "[redis_queued_locks.try_lock.reentrant_lock__work_through]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "spc_status", "last_spc_ts");
|
|
2738
3015
|
# - "[redis_queued_locks.try_lock.single_process_lock_conflict__dead_lock]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "spc_status", "last_spc_ts");
|
|
2739
3016
|
# - "[redis_queued_locks.try_lock.single_process_lock_conflict__lock_upgrade]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "spc_status", "last_spc_ts");
|
|
2740
|
-
# - "[redis_queued_locks.try_lock.acq_added_to_queue]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
3017
|
+
# - "[redis_queued_locks.try_lock.acq_added_to_queue]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
2741
3018
|
# - "[redis_queued_locks.try_lock.remove_expired_acqs]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2742
|
-
# - "[redis_queued_locks.try_lock.
|
|
3019
|
+
# - "[redis_queued_locks.try_lock.dead_request_removed]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "dead_acq_id", "dead_priority");
|
|
3020
|
+
# - "[redis_queued_locks.try_lock.get_first_from_queue]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "first_acq_id_in_queue", "first_priority_in_queue");
|
|
2743
3021
|
# - "[redis_queued_locks.try_lock.exit__queue_ttl_reached]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2744
|
-
# - "[redis_queued_locks.try_lock.exit__no_first]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "first_acq_id_in_queue", "<current_lock_data>");
|
|
2745
|
-
# - "[redis_queued_locks.try_lock.exit__lock_still_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "first_acq_id_in_queue", "locked_by_acq_id", "<current_lock_data>");
|
|
2746
|
-
# - "[redis_queued_locks.try_lock.exit__write_request_ahead]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "ahead_acq_id");
|
|
2747
|
-
# - "[redis_queued_locks.try_lock.exit__read_request_ahead]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "ahead_acq_id");
|
|
2748
|
-
# - "[redis_queued_locks.try_lock.exit__read_lock_still_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "first_acq_id_in_queue");
|
|
3022
|
+
# - "[redis_queued_locks.try_lock.exit__no_first]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "first_acq_id_in_queue", "first_priority_in_queue", "<current_lock_data>");
|
|
3023
|
+
# - "[redis_queued_locks.try_lock.exit__lock_still_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "first_acq_id_in_queue", "first_priority_in_queue", "locked_by_acq_id", "<current_lock_data>");
|
|
3024
|
+
# - "[redis_queued_locks.try_lock.exit__write_request_ahead]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "ahead_acq_id", "ahead_priority");
|
|
3025
|
+
# - "[redis_queued_locks.try_lock.exit__read_request_ahead]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "ahead_acq_id", "ahead_priority");
|
|
3026
|
+
# - "[redis_queued_locks.try_lock.exit__read_lock_still_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "first_acq_id_in_queue", "first_priority_in_queue");
|
|
2749
3027
|
# - "[redis_queued_locks.try_lock.obtain__free_to_acquire]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2750
3028
|
# - "rw_mode" is the requested lock mode ('write'/'read');
|
|
2751
3029
|
config['log_lock_try'] = false
|
|
@@ -2758,7 +3036,7 @@ config['log_sampling_enabled'] = false
|
|
|
2758
3036
|
|
|
2759
3037
|
# (default: 15)
|
|
2760
3038
|
# - the percent of cases that should be logged;
|
|
2761
|
-
# - take an effect when <config['
|
|
3039
|
+
# - take an effect when <config['log_sampling_enabled']> is true;
|
|
2762
3040
|
# - works in tandem with <config['log_sampling_enabled']> and <config['log_sampler']> configs;
|
|
2763
3041
|
config['log_sampling_percent'] = 15
|
|
2764
3042
|
|
|
@@ -2825,7 +3103,7 @@ config['instr_sampling_enabled'] = false
|
|
|
2825
3103
|
|
|
2826
3104
|
# (default: 15)
|
|
2827
3105
|
# - the percent of cases that should be instrumented;
|
|
2828
|
-
# - take an effect when <config['
|
|
3106
|
+
# - take an effect when <config['instr_sampling_enabled']> is true;
|
|
2829
3107
|
# - works in tandem with <config['instr_sampling_enabled']> and <config['instr_sampler']> configs;
|
|
2830
3108
|
config['instr_sampling_percent'] = 15
|
|
2831
3109
|
|
|
@@ -2848,6 +3126,7 @@ config['instr_sampler'] = RedisQueuedLocks::Instrument::Sampler
|
|
|
2848
3126
|
List of instrumentation events
|
|
2849
3127
|
|
|
2850
3128
|
- ["redis_queued_locks.lock_obtained"](#redis_queued_lockslock_obtained)
|
|
3129
|
+
- ["redis_queued_locks.lock_obtaining_failed"](#redis_queued_lockslock_obtaining_failed)
|
|
2851
3130
|
- ["redis_queued_locks.extendable_reentrant_lock_obtained"](#redis_queued_locksextendable_reentrant_lock_obtained)
|
|
2852
3131
|
- ["redis_queued_locks.reentrant_lock_obtained"](#redis_queued_locksreentrant_lock_obtained)
|
|
2853
3132
|
- ["redis_queued_locks.lock_hold_and_release"](#redis_queued_lockslock_hold_and_release)
|
|
@@ -2858,6 +3137,17 @@ List of instrumentation events
|
|
|
2858
3137
|
- ["redis_queued_locks.explicit_read_lock_release"](#redis_queued_locksexplicit_read_lock_release)
|
|
2859
3138
|
- ["redis_queued_locks.explicit_all_locks_release"](#redis_queued_locksexplicit_all_locks_release)
|
|
2860
3139
|
- ["redis_queued_locks.release_locks_of"](#redis_queued_locksrelease_locks_of)
|
|
3140
|
+
- ["redis_queued_locks.extend_lock_ttl"](#redis_queued_locksextend_lock_ttl)
|
|
3141
|
+
- ["redis_queued_locks.clear_dead_requests"](#redis_queued_locksclear_dead_requests)
|
|
3142
|
+
- ["redis_queued_locks.reindex_locks"](#redis_queued_locksreindex_locks)
|
|
3143
|
+
|
|
3144
|
+
What is instrumented (each event has a log line with the same name, see [Logging](#logging)):
|
|
3145
|
+
- the lock acquirement (`#lock`/`#lock!`, `#lock_series`/`#lock_series!`): each meaningful outcome (the lock is obtained,
|
|
3146
|
+
not obtained, held and released);
|
|
3147
|
+
- each operation that changes Redis keys (`#unlock`, `#unlock_read`, `#clear_locks`, `#clear_locks_of`/`#clear_current_locks`,
|
|
3148
|
+
`#extend_lock_ttl`, `#clear_dead_requests`, `#reindex_locks`): one event with the time of the whole operation;
|
|
3149
|
+
- lock information queries (`#lock_info`, `#locks`, `#queues`, `#keys`, ...) are not instrumented;
|
|
3150
|
+
- the swarm (`#probe_hosts`, `#flush_zombies` and the swarm elements) is not instrumented yet;
|
|
2861
3151
|
|
|
2862
3152
|
Detalized event semantics and payload structure:
|
|
2863
3153
|
|
|
@@ -2871,10 +3161,29 @@ Detalized event semantics and payload structure:
|
|
|
2871
3161
|
- `:hst_id` - `string` - lock's host identifier;
|
|
2872
3162
|
- `:lock_key` - `string` - lock name;
|
|
2873
3163
|
- `:rw_mode` - `symbol` - requested lock mode: `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
3164
|
+
- `:priority` - `integer` - lock request priority: from `-100` to `100` (a priority name is reported as its integer priority, see [Lock Request Prioritization](#lock-request-prioritization));
|
|
2874
3165
|
- `:ts` - `numeric`/`epoch` - the time when the lock was obtaiend;
|
|
2875
3166
|
- `:acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
|
|
2876
3167
|
- `:instrument` - `nil`/`Any` - custom data passed to the `#lock`/`#lock!` method as `:instrument` attribute;
|
|
2877
3168
|
|
|
3169
|
+
##### `"redis_queued_locks.lock_obtaining_failed"`
|
|
3170
|
+
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
3171
|
+
- a moment when the lock acquirement is failed (the lock is not obtained);
|
|
3172
|
+
- raised from `#lock`/`#lock!` (with `raise_errors: true` - before the error is raised) and from `#lock_series`/`#lock_series!`
|
|
3173
|
+
(for the lock of the series that is not obtained);
|
|
3174
|
+
- payload:
|
|
3175
|
+
- `:lock_key` - `string` - lock name;
|
|
3176
|
+
- `:rw_mode` - `symbol` - requested lock mode: `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
3177
|
+
- `:priority` - `integer` - lock request priority: from `-100` to `100` (a priority name is reported as its integer priority, see [Lock Request Prioritization](#lock-request-prioritization));
|
|
3178
|
+
- `:ttl` - `integer`/`milliseconds` - requested lock ttl;
|
|
3179
|
+
- `:acq_id` - `string` - lock acquirer identifier;
|
|
3180
|
+
- `:hst_id` - `string` - lock's host identifier;
|
|
3181
|
+
- `:ts` - `numeric`/`epoch` - the time when the lock acquirement is failed;
|
|
3182
|
+
- `:acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
|
|
3183
|
+
- `:result` - `symbol` - the failure result: `:timeout_reached`, `:retry_limit_reached`, `:fail_fast_no_try`,
|
|
3184
|
+
`:fail_fast_after_try`, `:conflict_dead_lock`, `:conflict_lock_upgrade` (see [lock](#lock---obtain-a-lock));
|
|
3185
|
+
- `:instrument` - `nil`/`Any` - custom data passed to the `#lock`/`#lock!` method as `:instrument` attribute;
|
|
3186
|
+
|
|
2878
3187
|
##### `"redis_queued_locks.extendable_reentrant_lock_obtained"`
|
|
2879
3188
|
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
2880
3189
|
- an event signalizes about the "extendable reentrant lock" obtaining is happened;
|
|
@@ -2883,6 +3192,7 @@ Detalized event semantics and payload structure:
|
|
|
2883
3192
|
- `:lock_key` - `string` - lock name;
|
|
2884
3193
|
- `:rw_mode` - `symbol` - requested lock mode: `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
2885
3194
|
a read lock request under the write lock of the same acquirer works through the write lock (`:rw_mode` is `:read`);
|
|
3195
|
+
- `:priority` - `integer` - requested lock request priority: from `-100` to `100` (a priority name is reported as its integer priority; a reentrant lock request is not queued, see [Lock Request Prioritization](#lock-request-prioritization));
|
|
2886
3196
|
- `:ttl` - `integer`/`milliseconds` - last lock ttl by reentrant locking;
|
|
2887
3197
|
- `:acq_id` - `string` - lock acquirer identifier;
|
|
2888
3198
|
- `:hst_id` - `string` - lock's host identifier;
|
|
@@ -2898,6 +3208,7 @@ Detalized event semantics and payload structure:
|
|
|
2898
3208
|
- `:lock_key` - `string` - lock name;
|
|
2899
3209
|
- `:rw_mode` - `symbol` - requested lock mode: `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
2900
3210
|
a read lock request under the write lock of the same acquirer works through the write lock (`:rw_mode` is `:read`);
|
|
3211
|
+
- `:priority` - `integer` - requested lock request priority: from `-100` to `100` (a priority name is reported as its integer priority; a reentrant lock request is not queued, see [Lock Request Prioritization](#lock-request-prioritization));
|
|
2901
3212
|
- `:ttl` - `integer`/`milliseconds` - last lock ttl by reentrant locking;
|
|
2902
3213
|
- `:acq_id` - `string` - lock acquirer identifier;
|
|
2903
3214
|
- `:hst_id` - `string` - lock's host identifier;
|
|
@@ -2916,6 +3227,7 @@ Detalized event semantics and payload structure:
|
|
|
2916
3227
|
- `:hst_id` - `string` - lock's host identifier;
|
|
2917
3228
|
- `:lock_key` - `string` - lock name;
|
|
2918
3229
|
- `:rw_mode` - `symbol` - requested lock mode: `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
3230
|
+
- `:priority` - `integer` - lock request priority: from `-100` to `100` (a priority name is reported as its integer priority, see [Lock Request Prioritization](#lock-request-prioritization));
|
|
2919
3231
|
- `:ts` - `numeric`/`epoch` - the time when lock was obtained;
|
|
2920
3232
|
- `:acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
|
|
2921
3233
|
- `:instrument` - `nil`/`Any` - custom data passed to the `#lock`/`#lock!` method as `:instrument` attribute;
|
|
@@ -2934,6 +3246,7 @@ Detalized event semantics and payload structure:
|
|
|
2934
3246
|
- `:lock_key` - `string` - lock name;
|
|
2935
3247
|
- `:rw_mode` - `symbol` - requested lock mode: `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
2936
3248
|
a read lock request under the write lock of the same acquirer works through the write lock (`:rw_mode` is `:read`);
|
|
3249
|
+
- `:priority` - `integer` - requested lock request priority: from `-100` to `100` (a priority name is reported as its integer priority; a reentrant lock request is not queued, see [Lock Request Prioritization](#lock-request-prioritization));
|
|
2937
3250
|
- `:acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
|
|
2938
3251
|
- `:instrument` - `nil`/`Any` - custom data passed to the `#lock`/`#lock!` method as `:instrument` attribute;
|
|
2939
3252
|
|
|
@@ -2944,6 +3257,7 @@ Detalized event semantics and payload structure:
|
|
|
2944
3257
|
- payload:
|
|
2945
3258
|
- `:lock_keys` - `array<string>` - lock names of the series;
|
|
2946
3259
|
- `:rw_mode` - `symbol` - the mode of the series (`read_write_mode`): `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
3260
|
+
- `:priority` - `integer` - the lock request priority of the series (`priority`): from `-100` to `100` (a priority name is reported as its integer priority, see [Lock Request Prioritization](#lock-request-prioritization));
|
|
2947
3261
|
- `:ttl` - `integer`/`milliseconds` - the `ttl` option of the series (each lock of the series gets its own ttl,
|
|
2948
3262
|
see [lock_series](#lock_series---poc-acquire-a-series-of-locks));
|
|
2949
3263
|
- `:acq_id` - `string` - lock acquirer identifier;
|
|
@@ -2959,6 +3273,7 @@ Detalized event semantics and payload structure:
|
|
|
2959
3273
|
- payload:
|
|
2960
3274
|
- `:lock_keys` - `array<string>` - lock names of the series;
|
|
2961
3275
|
- `:rw_mode` - `symbol` - the mode of the series (`read_write_mode`): `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
3276
|
+
- `:priority` - `integer` - the lock request priority of the series (`priority`): from `-100` to `100` (a priority name is reported as its integer priority, see [Lock Request Prioritization](#lock-request-prioritization));
|
|
2962
3277
|
- `:hold_time` - `float`/`milliseconds` - the series hold time;
|
|
2963
3278
|
- `:ttl` - `integer`/`milliseconds` - the `ttl` option of the series;
|
|
2964
3279
|
- `:acq_id` - `string` - lock acquirer identifier;
|
|
@@ -2977,6 +3292,7 @@ Detalized event semantics and payload structure:
|
|
|
2977
3292
|
- `:rel_time` - `float`/`milliseconds` - time spent on lock releasing;
|
|
2978
3293
|
- `:lock_key` - `string` - released lock (lock name);
|
|
2979
3294
|
- `:lock_key_queue` - `string` - released lock queue (the queue of write lock requests);
|
|
3295
|
+
- `:instrument` - `nil`/`Any` - custom data passed to the `#unlock` method as `:instrument` attribute;
|
|
2980
3296
|
|
|
2981
3297
|
##### `"redis_queued_locks.explicit_read_lock_release"`
|
|
2982
3298
|
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
@@ -2999,6 +3315,7 @@ Detalized event semantics and payload structure:
|
|
|
2999
3315
|
- `:at` - `float`/`epoch` - the time when the operation has ended;
|
|
3000
3316
|
- `:rel_key_cnt` - `integer` - released redis keys count (write locks, readers registries, read lock data,
|
|
3001
3317
|
write and read lock request queues);
|
|
3318
|
+
- `:instrument` - `nil`/`Any` - custom data passed to the `#clear_locks` method as `:instrument` attribute;
|
|
3002
3319
|
|
|
3003
3320
|
##### `"redis_queued_locks.release_locks_of"`
|
|
3004
3321
|
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
@@ -3011,6 +3328,42 @@ Detalized event semantics and payload structure:
|
|
|
3011
3328
|
- `:hst_id` - `string` - refused host identifier;
|
|
3012
3329
|
- `:rel_key_cnt` - `integer` - released locks count (write locks and read locks);
|
|
3013
3330
|
- `:tch_queue_cnt` - `:integer` - the number of queues (write and read lock request queues) from which the concrete host/acquirer was removed;
|
|
3331
|
+
- `:instrument` - `nil`/`Any` - custom data passed to the `#clear_locks_of`/`#clear_current_locks` method as `:instrument` attribute;
|
|
3332
|
+
|
|
3333
|
+
##### `"redis_queued_locks.extend_lock_ttl"`
|
|
3334
|
+
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
3335
|
+
- an event signalizes about the lock TTL extension (successful or not);
|
|
3336
|
+
- raised from `#extend_lock_ttl`;
|
|
3337
|
+
- payload:
|
|
3338
|
+
- `:lock_key` - `string` - lock key (`rql:lock:<lock_name>`);
|
|
3339
|
+
- `:rw_mode` - `symbol` - `:write` (the write lock) or `:read` (the read lock of the acquirer or all read locks);
|
|
3340
|
+
- `:all_read_locks` - `boolean` - all read locks of the lock are extended (`:read` mode);
|
|
3341
|
+
- `:ext_ttl` - `integer`/`milliseconds` - the extension (`milliseconds` argument);
|
|
3342
|
+
- `:extended_locks_count` - `integer` - the number of extended locks (`0` when nothing is extended);
|
|
3343
|
+
- `:ext_time` - `float`/`milliseconds` - time spent on the extension;
|
|
3344
|
+
- `:at` - `float`/`epoch` - the time when the operation has ended;
|
|
3345
|
+
- `:instrument` - `nil`/`Any` - custom data passed to the `#extend_lock_ttl` method as `:instrument` attribute;
|
|
3346
|
+
|
|
3347
|
+
##### `"redis_queued_locks.clear_dead_requests"`
|
|
3348
|
+
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
3349
|
+
- an event signalizes about the dropped dead lock requests;
|
|
3350
|
+
- raised from `#clear_dead_requests`;
|
|
3351
|
+
- payload:
|
|
3352
|
+
- `:dead_ttl` - `integer`/`milliseconds` - lock requests older than `dead_ttl` are dropped;
|
|
3353
|
+
- `:prc_queue_cnt` - `integer` - the number of processed lock request queues (write and read);
|
|
3354
|
+
- `:rel_time` - `float`/`milliseconds` - time spent on the operation;
|
|
3355
|
+
- `:at` - `float`/`epoch` - the time when the operation has ended;
|
|
3356
|
+
- `:instrument` - `nil`/`Any` - custom data passed to the `#clear_dead_requests` method as `:instrument` attribute;
|
|
3357
|
+
|
|
3358
|
+
##### `"redis_queued_locks.reindex_locks"`
|
|
3359
|
+
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
3360
|
+
- an event signalizes about the lock keys added to the [Lock Index](#lock-index);
|
|
3361
|
+
- raised from `#reindex_locks`;
|
|
3362
|
+
- payload:
|
|
3363
|
+
- `:idx_key_cnt` - `integer` - the number of indexed keys;
|
|
3364
|
+
- `:idx_time` - `float`/`milliseconds` - time spent on the operation;
|
|
3365
|
+
- `:at` - `float`/`epoch` - the time when the operation has ended;
|
|
3366
|
+
- `:instrument` - `nil`/`Any` - custom data passed to the `#reindex_locks` method as `:instrument` attribute;
|
|
3014
3367
|
|
|
3015
3368
|
---
|
|
3016
3369
|
|
|
@@ -3028,7 +3381,6 @@ Detalized event semantics and payload structure:
|
|
|
3028
3381
|
- isolated timeouts which are independent of internal Ruby's timeout implementation (where all timeouts are hostend inside
|
|
3029
3382
|
the global "timeout request" queue and managed by a single global "timeout wathcer" thread). it should prevent any logic and timeout intersections,
|
|
3030
3383
|
some GVL-related things and problem situations when the global watcher thread is "dead";
|
|
3031
|
-
- lock request prioritization;
|
|
3032
3384
|
- **strict redlock algorithm support** (support for many `RedisClient` instances that are fully independent (distributed redis instances));
|
|
3033
3385
|
- #lock_series` - acquire a series of locks:
|
|
3034
3386
|
- (NOTE: "Proof of Concept" implementation is ready to use);
|
|
@@ -3046,6 +3398,7 @@ Detalized event semantics and payload structure:
|
|
|
3046
3398
|
Mutex (instantiated inside the main Ractor) from the our non-main Ractor (swar-mode works under the non-main ractor) and this process fails\
|
|
3047
3399
|
on non-sharable memory error);
|
|
3048
3400
|
- **Minor**:
|
|
3401
|
+
- `*_status` aliases for `*_info` methods;
|
|
3049
3402
|
- an ability to return all insturmentation metrics from the `lock` invocation (and after block `yield`ing);
|
|
3050
3403
|
- think about a PG-like `stats`-data/table, that should be helpful for lock history acquirement analyzation :thinking: (suitable for cases when we want
|
|
3051
3404
|
to check if the lock was acquired by some different parallel process durign the some period of time in the past or not);
|
|
@@ -3094,6 +3447,17 @@ Detalized event semantics and payload structure:
|
|
|
3094
3447
|
bundle exec rake rspec
|
|
3095
3448
|
```
|
|
3096
3449
|
|
|
3450
|
+
- Tests in parallel processes (`RSpec`, ~6x faster): each process (worker) works with its own pair of Redis databases
|
|
3451
|
+
(the specs use db 0 and db 1), so `WORKERS=n` flushes db `0..2n-1` of the local Redis (the default number of workers:
|
|
3452
|
+
the Redis `databases` / 2, up to the CPU count); the examples are distributed by their durations of the previous runs
|
|
3453
|
+
(`tmp/rspec_parallel/durations.json`), the output of each worker is in `tmp/rspec_parallel/worker-<n>.log`, the coverage
|
|
3454
|
+
report of the whole run is collated into `coverage/` after all workers:
|
|
3455
|
+
```shell
|
|
3456
|
+
bundle exec rake rspec:parallel
|
|
3457
|
+
WORKERS=4 bundle exec rake rspec:parallel
|
|
3458
|
+
bundle exec rake "rspec:parallel[spec/redis_queued_locks_spec.rb]" # spec files/filters
|
|
3459
|
+
```
|
|
3460
|
+
|
|
3097
3461
|
- Linting (`Rubocop`+`RBS`):
|
|
3098
3462
|
```shell
|
|
3099
3463
|
bundle exec rake rubocop
|
|
@@ -3106,7 +3470,8 @@ bundle exec rake steep:check
|
|
|
3106
3470
|
|
|
3107
3471
|
- `Runtime` TypeChecking (`RBS`):
|
|
3108
3472
|
```shell
|
|
3109
|
-
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
|
|
3473
|
+
bundle exec rbs collection install && RBS_TEST_RAISE=true RUBYOPT='-rbundler/setup -rrbs/test/setup' RBS_TEST_OPT='-I sig' RBS_TEST_LOGLEVEL=error RBS_TEST_TARGET='RedisQueuedLocks::*' RBS_TEST_SKIP='::RedisQueuedLocks::Swarm::FlushZombies,::RedisQueuedLocks::Swarm::SwarmElement::Isolated,::RedisQueuedLocks::Swarm::RedisClientBuilder' bundle exec rspec
|
|
3474
|
+
# or in parallel processes: the same environment with `bundle exec rake rspec:parallel` instead of `bundle exec rspec`
|
|
3110
3475
|
```
|
|
3111
3476
|
|
|
3112
3477
|
---
|