redis_queued_locks 1.20.0 → 1.21.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 +35 -13
- data/.claude/rules/acquirer.md +10 -9
- data/.claude/rules/logic.md +28 -5
- data/.claude/rules/swarm.md +2 -2
- data/.claude/rules/tests.md +6 -3
- data/.claude/rules/visitors.md +9 -10
- data/.claude/skills/rql-code-review/SKILL.md +24 -10
- data/CHANGELOG.md +126 -0
- data/CLAUDE.md +2 -1
- data/README.md +245 -70
- 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 +42 -0
- data/lib/redis_queued_locks/acquirer/acquire_lock/log_visitor.rb +45 -0
- data/lib/redis_queued_locks/acquirer/acquire_lock/try_to_lock/log_visitor.rb +1 -0
- data/lib/redis_queued_locks/acquirer/acquire_lock/try_to_lock.rb +12 -1
- 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 +111 -24
- data/lib/redis_queued_locks/acquirer/clear_dead_requests.rb +86 -8
- data/lib/redis_queued_locks/acquirer/extend_lock_ttl.rb +122 -12
- 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 +10 -1
- data/lib/redis_queued_locks/acquirer/lock_info.rb +31 -39
- data/lib/redis_queued_locks/acquirer/lock_series_poc/instr_visitor.rb +3 -2
- data/lib/redis_queued_locks/acquirer/lock_series_poc/log_visitor.rb +5 -4
- data/lib/redis_queued_locks/acquirer/lock_series_poc.rb +55 -15
- data/lib/redis_queued_locks/acquirer/locks.rb +26 -9
- data/lib/redis_queued_locks/acquirer/queue_info.rb +0 -1
- data/lib/redis_queued_locks/acquirer/queues.rb +10 -1
- data/lib/redis_queued_locks/acquirer/reindex_locks.rb +117 -4
- data/lib/redis_queued_locks/acquirer/release_all_locks.rb +31 -4
- data/lib/redis_queued_locks/acquirer/release_lock.rb +33 -7
- data/lib/redis_queued_locks/acquirer/release_locks_of.rb +39 -4
- data/lib/redis_queued_locks/acquirer/release_read_lock.rb +22 -3
- data/lib/redis_queued_locks/client.rb +82 -40
- data/lib/redis_queued_locks/config.rb +12 -13
- 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 +14 -4
- 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 +3 -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/sig/redis_queued_locks/acquirer/acquire_lock/instr_visitor.rbs +14 -0
- data/sig/redis_queued_locks/acquirer/acquire_lock/log_visitor.rbs +13 -0
- data/sig/redis_queued_locks/acquirer/acquire_lock/yield_expire.rbs +2 -2
- data/sig/redis_queued_locks/acquirer/acquire_lock.rbs +1 -1
- data/sig/redis_queued_locks/acquirer/clear_dead_requests.rbs +9 -0
- data/sig/redis_queued_locks/acquirer/extend_lock_ttl.rbs +15 -2
- 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 +14 -3
- data/sig/redis_queued_locks/errors.rbs +5 -2
- data/sig/redis_queued_locks/instrument/sampler.rbs +0 -2
- data/sig/redis_queued_locks/logging/sampler.rbs +0 -2
- data/sig/redis_queued_locks/resource.rbs +1 -1
- data/sig/vendor/redis_client.rbs +1 -0
- metadata +1 -1
data/README.md
CHANGED
|
@@ -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)
|
|
@@ -78,6 +79,7 @@ Provides flexible invocation flow, parametrized limits (lock request ttl, lock t
|
|
|
78
79
|
- [Instrumentation Configuration](#instrumentation-configuration)
|
|
79
80
|
- [Instrumentation Events](#instrumentation-events)
|
|
80
81
|
- ["redis_queued_locks.lock_obtained"](#redis_queued_lockslock_obtained)
|
|
82
|
+
- ["redis_queued_locks.lock_obtaining_failed"](#redis_queued_lockslock_obtaining_failed)
|
|
81
83
|
- ["redis_queued_locks.extendable_reentrant_lock_obtained"](#redis_queued_locksextendable_reentrant_lock_obtained)
|
|
82
84
|
- ["redis_queued_locks.reentrant_lock_obtained"](#redis_queued_locksreentrant_lock_obtained)
|
|
83
85
|
- ["redis_queued_locks.lock_hold_and_release"](#redis_queued_lockslock_hold_and_release)
|
|
@@ -88,6 +90,9 @@ Provides flexible invocation flow, parametrized limits (lock request ttl, lock t
|
|
|
88
90
|
- ["redis_queued_locks.explicit_read_lock_release"](#redis_queued_locksexplicit_read_lock_release)
|
|
89
91
|
- ["redis_queued_locks.explicit_all_locks_release"](#redis_queued_locksexplicit_all_locks_release)
|
|
90
92
|
- ["redis_queued_locks.release_locks_of"](#redis_queued_locksrelease_locks_of)
|
|
93
|
+
- ["redis_queued_locks.extend_lock_ttl"](#redis_queued_locksextend_lock_ttl)
|
|
94
|
+
- ["redis_queued_locks.clear_dead_requests"](#redis_queued_locksclear_dead_requests)
|
|
95
|
+
- ["redis_queued_locks.reindex_locks"](#redis_queued_locksreindex_locks)
|
|
91
96
|
- [Roadmap](#roadmap)
|
|
92
97
|
- [Build and Develop](#build-and-develop)
|
|
93
98
|
- [Contributing](#contributing)
|
|
@@ -191,16 +196,19 @@ client = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
|
191
196
|
|
|
192
197
|
# (seconds) (supports nil)
|
|
193
198
|
# - nil means "no timeout" and you are only limited by "retry_count" config;
|
|
199
|
+
# - 0 is the same as nil (no timeout);
|
|
194
200
|
config['try_to_lock_timeout'] = 10
|
|
195
201
|
|
|
196
202
|
# (milliseconds) (default: 5_000)
|
|
197
203
|
# - lock's time to live
|
|
204
|
+
# - should be a positive Integer
|
|
198
205
|
config['default_lock_ttl'] = 5_000
|
|
199
206
|
|
|
200
207
|
# (seconds) (default: 15)
|
|
201
208
|
# - lock request timeout. after this timeout your lock request in queue will be requeued with new position (at the end of the queue);
|
|
202
209
|
# - (the enabled lock index) the lock request queue lives as long as its latest lock request (the queue TTL is refreshed with
|
|
203
210
|
# the queue ttl on each lock request): queues of dead acquirers expire by themselves;
|
|
211
|
+
# - should be a positive Integer
|
|
204
212
|
config['default_queue_ttl'] = 15
|
|
205
213
|
|
|
206
214
|
# (boolean) (default: false)
|
|
@@ -260,6 +268,7 @@ client = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
|
260
268
|
# - (the enabled lock index) how many locks (lock names of the lock index) will be processed at a time
|
|
261
269
|
# (the ZSCAN count of the lock index pass);
|
|
262
270
|
# - affects the performance of your Redis and Ruby Application (configure thoughtfully);
|
|
271
|
+
# - should be a positive Integer
|
|
263
272
|
config['lock_release_batch_size'] = 100
|
|
264
273
|
|
|
265
274
|
# (default: 300)
|
|
@@ -268,12 +277,14 @@ client = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
|
268
277
|
# (the ZSCAN count of the lock index pass, DEL for batch deletion);
|
|
269
278
|
# - 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
279
|
# - affects the performance of your Redis (configure thoughtfully);
|
|
280
|
+
# - should be a positive Integer
|
|
271
281
|
config['clear_locks_of__lock_scan_size'] = 300
|
|
272
282
|
|
|
273
283
|
# (default: 300)
|
|
274
284
|
# - how many lock queues will be processed in one round trip in #clear_locks_of and in #clear_current_locks methods
|
|
275
285
|
# (lock queues are found by the same SCAN pass as locks or derived from the lock names of the same lock index pass);
|
|
276
286
|
# - affects the performance of your Redis (configure thoughtfully);
|
|
287
|
+
# - should be a positive Integer
|
|
277
288
|
config['clear_locks_of__queue_scan_size'] = 300
|
|
278
289
|
|
|
279
290
|
# (default: 500)
|
|
@@ -282,6 +293,7 @@ client = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
|
282
293
|
# - (the enabled lock index) how many locks (lock names of the lock index) should be processed at a time
|
|
283
294
|
# (the ZSCAN count of the lock index pass);
|
|
284
295
|
# - affects the performance of your Redis and Ruby Application (configure thoughtfully);
|
|
296
|
+
# - should be a positive Integer
|
|
285
297
|
config['key_extraction_batch_size'] = 500
|
|
286
298
|
|
|
287
299
|
# (default: 1 day)
|
|
@@ -327,15 +339,28 @@ client = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
|
327
339
|
# - should implement `debug(progname = nil, &block)` (minimal requirement) or be an instance of Ruby's `::Logger` class/subclass;
|
|
328
340
|
# - supports `SemanticLogger::Logger` (see "semantic_logger" gem)
|
|
329
341
|
# - 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.
|
|
342
|
+
# - "[redis_queued_locks.start_lock_obtaining]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
343
|
+
# - "[redis_queued_locks.start_try_to_lock_cycle]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
344
|
+
# - "[redis_queued_locks.dead_score_reached__reset_acquirer_position]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
345
|
+
# - "[redis_queued_locks.lock_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
346
|
+
# - "[redis_queued_locks.lock_obtaining_failed]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time", "result");
|
|
347
|
+
# - "[redis_queued_locks.extendable_reentrant_lock_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
348
|
+
# - "[redis_queued_locks.reentrant_lock_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
349
|
+
# - "[redis_queued_locks.fail_fast_or_limits_reached_or_deadlock__dequeue]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
350
|
+
# - "[redis_queued_locks.expire_lock]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
351
|
+
# - "[redis_queued_locks.decrease_lock]" (logs "lock_key", "decreased_ttl", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
352
|
+
# - "[redis_queued_locks.start_lock_series_obtaining]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
353
|
+
# - "[redis_queued_locks.lock_series_obtained]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
354
|
+
# - "[redis_queued_locks.expire_lock_series]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
355
|
+
# - "[redis_queued_locks.explicit_lock_release]" (logs "lock_key", "lock_key_queue", "rel_time", "at");
|
|
356
|
+
# - "[redis_queued_locks.explicit_read_lock_release]" (logs "lock_key", "acq_id", "lock_res", "rel_time", "at");
|
|
357
|
+
# - "[redis_queued_locks.explicit_all_locks_release]" (logs "at", "rel_time", "rel_key_cnt");
|
|
358
|
+
# - "[redis_queued_locks.release_locks_of]" (logs "at", "hst_id", "acq_id", "rel_time", "rel_key_cnt", "tch_queue_cnt");
|
|
359
|
+
# - "[redis_queued_locks.extend_lock_ttl]" (logs "lock_key", "rw_mode", "all_read_locks", "ext_ttl", "extended_locks_count", "ext_time", "at");
|
|
360
|
+
# - "[redis_queued_locks.clear_dead_requests]" (logs "dead_ttl", "prc_queue_cnt", "rel_time", "at");
|
|
361
|
+
# - "[redis_queued_locks.reindex_locks]" (logs "idx_key_cnt", "idx_time", "at");
|
|
362
|
+
# - "rw_mode" is the requested lock mode ('write'/'read'), the held lock mode for "expire_lock" and "decrease_lock",
|
|
363
|
+
# the mode of the series for lock series logs;
|
|
339
364
|
# - by default uses VoidLogger that does nothing;
|
|
340
365
|
config['logger'] = RedisQueuedLocks::Logging::VoidLogger
|
|
341
366
|
|
|
@@ -343,19 +368,25 @@ client = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
|
343
368
|
# - adds additional debug logs;
|
|
344
369
|
# - enables additional logs for each internal try-retry lock acquiring (a lot of logs can be generated depending on your retry configurations);
|
|
345
370
|
# - 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.
|
|
371
|
+
# - "[redis_queued_locks.try_lock.start]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
372
|
+
# - "[redis_queued_locks.try_lock.rconn_fetched]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
373
|
+
# - "[redis_queued_locks.try_lock.same_process_conflict_detected]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
374
|
+
# - "[redis_queued_locks.try_lock.same_process_conflict_analyzed]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "spc_status");
|
|
375
|
+
# - "[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");
|
|
376
|
+
# - "[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");
|
|
377
|
+
# - "[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");
|
|
378
|
+
# - "[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");
|
|
379
|
+
# - "[redis_queued_locks.try_lock.acq_added_to_queue]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
380
|
+
# - "[redis_queued_locks.try_lock.remove_expired_acqs]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
381
|
+
# - "[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");
|
|
382
|
+
# - "[redis_queued_locks.try_lock.exit__queue_ttl_reached]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
383
|
+
# - "[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>");
|
|
384
|
+
# - "[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>");
|
|
385
|
+
# - "[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");
|
|
386
|
+
# - "[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");
|
|
387
|
+
# - "[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");
|
|
388
|
+
# - "[redis_queued_locks.try_lock.obtain__free_to_acquire]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
389
|
+
# - "rw_mode" is the requested lock mode ('write'/'read');
|
|
359
390
|
config['log_lock_try'] = false
|
|
360
391
|
|
|
361
392
|
# (default: false)
|
|
@@ -366,7 +397,7 @@ client = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
|
366
397
|
|
|
367
398
|
# (default: 15)
|
|
368
399
|
# - the percent of cases that should be logged;
|
|
369
|
-
# - take an effect when <config['
|
|
400
|
+
# - take an effect when <config['log_sampling_enabled']> is true;
|
|
370
401
|
# - works in tandem with <config['log_sampling_enabled']> and <config['log_sampler']> configs;
|
|
371
402
|
config['log_sampling_percent'] = 15
|
|
372
403
|
|
|
@@ -383,11 +414,11 @@ client = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
|
383
414
|
# - enables <instrumentaion sampling>: only the configured percent of RQL cases will be instrumented;
|
|
384
415
|
# - disabled by default;
|
|
385
416
|
# - works in tandem with <config['instr_sampling_percent'] and <config['instr_sampler']>;
|
|
386
|
-
config['
|
|
417
|
+
config['instr_sampling_enabled'] = false
|
|
387
418
|
|
|
388
419
|
# (default: 15)
|
|
389
420
|
# - the percent of cases that should be instrumented;
|
|
390
|
-
# - take an effect when <config['
|
|
421
|
+
# - take an effect when <config['instr_sampling_enabled']> is true;
|
|
391
422
|
# - works in tandem with <config['instr_sampling_enabled']> and <config['instr_sampler']> configs;
|
|
392
423
|
config['instr_sampling_percent'] = 15
|
|
393
424
|
|
|
@@ -409,15 +440,18 @@ end
|
|
|
409
440
|
<sup>\[[back to top](#table-of-contents)\]</sup>
|
|
410
441
|
|
|
411
442
|
- [lock](#lock---obtain-a-lock)
|
|
412
|
-
- [lock!](#lock---
|
|
443
|
+
- [lock!](#lock---exceptional-lock-obtaining)
|
|
413
444
|
- [lock_series](#lock_series---poc-acquire-a-series-of-locks) (PoC)
|
|
414
445
|
- [lock_series!](#lock_series---poc-exceptional-lock_series) (PoC)
|
|
415
446
|
- [lock_info](#lock_info)
|
|
416
447
|
- [queue_info](#queue_info)
|
|
417
448
|
- [locked?](#locked)
|
|
418
449
|
- [queued?](#queued)
|
|
419
|
-
- [unlock](#unlock---release-a-lock)
|
|
420
|
-
- [
|
|
450
|
+
- [unlock](#unlock---release-a-lock) (aka `release_lock`)
|
|
451
|
+
- [unlock_read](#unlock_read---release-the-read-lock-of-the-current-acquirer) (aka `release_read_lock`)
|
|
452
|
+
- [clear_locks](#clear_locks---release-all-locks-and-lock-queues) (aka `release_locks`)
|
|
453
|
+
- [clear_locks_of](#clear_locks_of) (aka `release_locks_of`)
|
|
454
|
+
- [clear_current_locks](#clear_current_locks) (aka `release_current_locks`)
|
|
421
455
|
- [extend_lock_ttl](#extend_lock_ttl)
|
|
422
456
|
- [locks](#locks---get-list-of-obtained-locks)
|
|
423
457
|
- [queues](#queues---get-list-of-lock-request-queues)
|
|
@@ -430,6 +464,10 @@ end
|
|
|
430
464
|
- [current_host_id](#current_host_id)
|
|
431
465
|
- [possible_host_ids](#possible_host_ids)
|
|
432
466
|
|
|
467
|
+
The numeric options of the methods (ttls, timeouts, retries, scan and batch sizes, zombie ttl) are validated with the same
|
|
468
|
+
types and ranges as their `config` keys: `Integer` values only (`timeout: 0.5` is rejected), `RedisQueuedLocks::ArgumentError`
|
|
469
|
+
(`RedisQueuedLocks::SwarmArgumentError` for the swarm methods) otherwise.
|
|
470
|
+
|
|
433
471
|
---
|
|
434
472
|
|
|
435
473
|
#### #lock - obtain a lock
|
|
@@ -488,6 +526,7 @@ def lock(
|
|
|
488
526
|
- Lock name to be obtained.
|
|
489
527
|
- `ttl` - (optional) - [Integer]
|
|
490
528
|
- Lock's time to live (in milliseconds);
|
|
529
|
+
- should be a positive `Integer` (write and read locks);
|
|
491
530
|
- pre-configured in `config['default_lock_ttl']`;
|
|
492
531
|
- `queue_ttl` - (optional) `[Integer]`
|
|
493
532
|
- Lifetime of the acuier's lock request. In seconds.
|
|
@@ -502,9 +541,14 @@ def lock(
|
|
|
502
541
|
- pre-configured in `config['lock_index_grace_period']`;
|
|
503
542
|
- `timeout` - (optional) `[Integer,NilClass]`
|
|
504
543
|
- Time period a client should try to acquire the lock (in seconds). Nil means "without timeout".
|
|
544
|
+
- `0` is the same as `nil` (without timeout);
|
|
505
545
|
- pre-configured in `config['try_to_lock_timeout']`;
|
|
546
|
+
- the timeout can not be swallowed by `rescue` of your logger/instrumenter (see `timed` below);
|
|
506
547
|
- `timed` - (optiona) `[Boolean]`
|
|
507
548
|
- Limit the invocation time period of the passed block of code by the lock's TTL.
|
|
549
|
+
- the block is interrupted by an internal `RedisQueuedLocks::Exception` (an `::Exception`, like `Timeout::ExitException` of Ruby's `Timeout`): `rescue => e`,
|
|
550
|
+
`rescue StandardError`, `rescue Timeout::Error` and `rescue nil` inside the block can not swallow it (only `rescue Exception`),
|
|
551
|
+
`ensure` of the block still runs;
|
|
508
552
|
- pre-configured in `config['is_timed_by_default']`;
|
|
509
553
|
- `false` by default;
|
|
510
554
|
- `retry_count` - (optional) `[Integer,NilClass]`
|
|
@@ -603,7 +647,7 @@ def lock(
|
|
|
603
647
|
- pre-configured in `config['log_sampling_enabled']`;
|
|
604
648
|
- `log_sampling_percent` - (optional) `[Integer]`
|
|
605
649
|
- the percent of cases that should be logged;
|
|
606
|
-
- take an effect when `
|
|
650
|
+
- take an effect when `log_sampling_enabled` is true;
|
|
607
651
|
- works in tandem with `log_sampling_enabled` and `log_sampler` options;
|
|
608
652
|
- pre-configured in `config['log_sampling_percent']`;
|
|
609
653
|
- `log_sampler` - (optional) `[#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]`
|
|
@@ -625,7 +669,7 @@ def lock(
|
|
|
625
669
|
- pre-configured in `config['instr_sampling_enabled']`;
|
|
626
670
|
- `instr_sampling_percent` - (optional) `[Integer]`
|
|
627
671
|
- the percent of cases that should be instrumented;
|
|
628
|
-
- take an effect when `
|
|
672
|
+
- take an effect when `instr_sampling_enabled` is true;
|
|
629
673
|
- works in tandem with `instr_sampling_enabled` and `instr_sampler` options;
|
|
630
674
|
- pre-configured in `config['instr_sampling_percent']`;
|
|
631
675
|
- `instr_sampler` - (optional) `[#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]`
|
|
@@ -663,6 +707,7 @@ Return value:
|
|
|
663
707
|
result: {
|
|
664
708
|
lock_key: "rql:lock:my_lock",
|
|
665
709
|
acq_id: "rql:acq:26672/2280/2300/2320/70ea5dbf10ea1056",
|
|
710
|
+
hst_id: "rql:hst:26672/2280/2320/70ea5dbf10ea1056",
|
|
666
711
|
ts: 1711909612.653696,
|
|
667
712
|
ttl: 10000,
|
|
668
713
|
process: :lock_obtaining
|
|
@@ -676,7 +721,8 @@ Return value:
|
|
|
676
721
|
- `:lock_obtaining` - classic lock obtaining proces. Default behavior (`conflict_strategy: :wait_for_lock`);
|
|
677
722
|
- `:extendable_conflict_work_through` - reentrant lock acquiring process with lock's TTL extension. Suitable for `conflict_strategy: :extendable_work_through`;
|
|
678
723
|
- `:conflict_work_through` - reentrant lock acquiring process without lock's TTL extension. Suitable for `conflict_strategy: :work_through`;
|
|
679
|
-
-
|
|
724
|
+
- (`conflict_strategy: :dead_locking` does not obtain the lock already obtained by the current acquirer:
|
|
725
|
+
it fails with `{ ok: false, result: :conflict_dead_lock }`);
|
|
680
726
|
- For more details see [Deadlocks and Reentrant locks](#deadlocks-and-reentrant-locks) readme section;
|
|
681
727
|
- For successful lock obtaining:
|
|
682
728
|
```ruby
|
|
@@ -688,7 +734,7 @@ Return value:
|
|
|
688
734
|
hst_id: String, # host identifier ("process_id/thread_id/ractor_id/identity")
|
|
689
735
|
ts: Float, # time (epoch) when lock was obtained (float, Time#to_f)
|
|
690
736
|
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
|
|
737
|
+
process: Symbol # which logical process has acquired the lock (:lock_obtaining, :extendable_conflict_work_through, :conflict_work_through)
|
|
692
738
|
}
|
|
693
739
|
}
|
|
694
740
|
```
|
|
@@ -700,21 +746,21 @@ Return value:
|
|
|
700
746
|
result: {
|
|
701
747
|
lock_key: "rql:lock:my_lock",
|
|
702
748
|
acq_id: "rql:acq:26672/2280/2300/2320/70ea5dbf10ea1056",
|
|
703
|
-
|
|
749
|
+
hst_id: "rql:hst:26672/2280/2320/70ea5dbf10ea1056",
|
|
704
750
|
ts: 1711909612.653696,
|
|
705
751
|
ttl: 10000,
|
|
706
|
-
process: :lock_obtaining # for
|
|
752
|
+
process: :lock_obtaining # for reentrant locks (conflict strategies): :conflict_work_through, :extendable_conflict_work_through
|
|
707
753
|
}
|
|
708
754
|
}
|
|
709
755
|
```
|
|
710
756
|
- For failed lock obtaining:
|
|
711
757
|
```ruby
|
|
712
|
-
{ ok: false, result: :timeout_reached }
|
|
713
|
-
{ ok: false, result: :
|
|
758
|
+
{ ok: false, result: :timeout_reached } # see <timeout> option
|
|
759
|
+
{ ok: false, result: :retry_limit_reached } # see <retry_count> option
|
|
714
760
|
{ ok: false, result: :conflict_dead_lock } # see <conflict_strategy> option for details (:dead_locking strategy)
|
|
761
|
+
{ ok: false, result: :conflict_lock_upgrade } # read-to-write lock upgrade (see <read_write_mode> and <conflict_strategy> options)
|
|
715
762
|
{ ok: false, result: :fail_fast_no_try } # see <fail_fast> option
|
|
716
763
|
{ ok: false, result: :fail_fast_after_try } # see <fail_fast> option
|
|
717
|
-
{ ok: false, result: :unknown }
|
|
718
764
|
```
|
|
719
765
|
|
|
720
766
|
Examples:
|
|
@@ -775,8 +821,9 @@ rql.lock_info("my_lock")
|
|
|
775
821
|
# =>
|
|
776
822
|
{
|
|
777
823
|
"lock_key" => "rql:lock:my_lock",
|
|
824
|
+
"rw_mode" => "write",
|
|
778
825
|
"acq_id" => "rql:acq:123/456/567/678/374dd74324",
|
|
779
|
-
"hst_id" => "rql:
|
|
826
|
+
"hst_id" => "rql:hst:123/456/678/374dd74324",
|
|
780
827
|
"ts" => 123456789,
|
|
781
828
|
"ini_ttl" => 123456,
|
|
782
829
|
"rem_ttl" => 123440,
|
|
@@ -1060,9 +1107,9 @@ client.lock_series!("x", "y", "z") { ...some_code... }
|
|
|
1060
1107
|
- `"acq_id"` - `string` - acquirer identifier (process_id/thread_id/fiber_id/ractor_id/identity);
|
|
1061
1108
|
- `"hst_id"` - `string` - host identifier (process_id/thread_id/ractor_id/identity);
|
|
1062
1109
|
- `"ts"` - `numeric`/`epoch` - the time when lock was obtained;
|
|
1063
|
-
- `"
|
|
1110
|
+
- `"ini_ttl"` - `integer` - (milliseconds) initial lock key ttl;
|
|
1064
1111
|
- `"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](
|
|
1112
|
+
- `<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
1113
|
- additional keys for **reentrant locks** and **extendable reentrant locks**:
|
|
1067
1114
|
- for any type of reentrant locks:
|
|
1068
1115
|
- `"spc_cnt"` - `integer` - how many times the lock was obtained as reentrant lock;
|
|
@@ -1082,7 +1129,7 @@ rql.lock_info("your_lock_name")
|
|
|
1082
1129
|
"lock_key" => "rql:lock:your_lock_name",
|
|
1083
1130
|
"rw_mode" => "write",
|
|
1084
1131
|
"acq_id" => "rql:acq:123/456/567/678/374dd74324",
|
|
1085
|
-
"hst_id" => "rql:
|
|
1132
|
+
"hst_id" => "rql:hst:123/456/678/374dd74324",
|
|
1086
1133
|
"ts" => 123456789.12345,
|
|
1087
1134
|
"ini_ttl" => 5_000,
|
|
1088
1135
|
"rem_ttl" => 4_999
|
|
@@ -1099,7 +1146,7 @@ rql.lock_info("your_lock_name")
|
|
|
1099
1146
|
"lock_key" => "rql:lock:your_lock_name",
|
|
1100
1147
|
"rw_mode" => "write",
|
|
1101
1148
|
"acq_id" => "rql:acq:123/456/567/678/374dd74324",
|
|
1102
|
-
"hst_id" => "rql:
|
|
1149
|
+
"hst_id" => "rql:hst:123/456/678/374dd74324",
|
|
1103
1150
|
"ts" => 123456789.12345,
|
|
1104
1151
|
"ini_ttl" => 5_000,
|
|
1105
1152
|
"rem_ttl" => 4_999,
|
|
@@ -1122,12 +1169,12 @@ rql.lock_info("your_lock_name")
|
|
|
1122
1169
|
"lock_key" => "rql:lock:your_lock_name",
|
|
1123
1170
|
"rw_mode" => "write",
|
|
1124
1171
|
"acq_id" => "rql:acq:123/456/567/678/374dd74324",
|
|
1125
|
-
"hst_id" => "rql:
|
|
1172
|
+
"hst_id" => "rql:hst:123/456/678/374dd74324",
|
|
1126
1173
|
"ts" => 123456789.12345,
|
|
1127
1174
|
"ini_ttl" => 5_000,
|
|
1128
1175
|
"rem_ttl" => 9_444,
|
|
1129
1176
|
# ==> keys for any type of reentrant lock:
|
|
1130
|
-
"
|
|
1177
|
+
"spc_cnt" => 2, # how many times the lock was obtained as reentrant lock
|
|
1131
1178
|
# ==> keys for extendable reentarnt locks with `:extendable_work_through` strategy:
|
|
1132
1179
|
"spc_ext_ttl" => 5_000, # sum of TTL of the each <extendable> reentrant lock (3_000 + 2_000)
|
|
1133
1180
|
"l_spc_ext_ini_ttl" => 2_000, # TTL of the last <extendable> reentrant lock
|
|
@@ -1319,7 +1366,7 @@ rql.queued?("your_lock_name") # => true/false
|
|
|
1319
1366
|
Return:
|
|
1320
1367
|
- `[Hash<Symbol,Boolean|Hash<Symbol,Numeric|String|Symbol>>]` (`{ ok: true/false, result: Hasn }`);
|
|
1321
1368
|
- `:result` format;
|
|
1322
|
-
- `:rel_time` - `Float` - time spent to process redis commands (in
|
|
1369
|
+
- `:rel_time` - `Float` - time spent to process redis commands (in milliseconds);
|
|
1323
1370
|
- `:rel_key` - `String` - released lock key (RedisQueudLocks-internal lock key name from Redis);
|
|
1324
1371
|
- `:rel_queue` - `String` - released lock queue key (RedisQueuedLocks-internal queue key name from Redis);
|
|
1325
1372
|
- `:queue_res` - `Symbol` - `:released` (or `:nothing_to_release` if the required queue does not exist);
|
|
@@ -1334,7 +1381,7 @@ rql.unlock("your_lock_name")
|
|
|
1334
1381
|
{
|
|
1335
1382
|
ok: true,
|
|
1336
1383
|
result: {
|
|
1337
|
-
rel_time: 0.02, # time spent to lock release (in
|
|
1384
|
+
rel_time: 0.02, # time spent to lock release (in milliseconds)
|
|
1338
1385
|
rel_key: "rql:lock:your_lock_name", # released lock key
|
|
1339
1386
|
rel_queue: "rql:lock_queue:your_lock_name", # released lock key queue
|
|
1340
1387
|
queue_res: :released, # or :nothing_to_release
|
|
@@ -1684,6 +1731,7 @@ rql.release_locks_of(host_id: rql.current_host_id, acquirer_id: rql.current_acqu
|
|
|
1684
1731
|
- the lock name which ttl should be extended;
|
|
1685
1732
|
- `milliseconds` - (required) `[Integer]`
|
|
1686
1733
|
- how many milliseconds should be added to the lock's TTL;
|
|
1734
|
+
- should be a positive `Integer`;
|
|
1687
1735
|
- `:read_write_mode` - (optional) `[Symbol]`
|
|
1688
1736
|
- `:write` (default) - extends the write lock (the behavior described below);
|
|
1689
1737
|
- `:read` - extends the read lock of the current acquirer only (see [Read/Write Locks](#readwrite-locks)):
|
|
@@ -1743,17 +1791,10 @@ rql.release_locks_of(host_id: rql.current_host_id, acquirer_id: rql.current_acqu
|
|
|
1743
1791
|
and for the read lock of the current acquirer, the number of extended read locks for `all_read_locks: true`;
|
|
1744
1792
|
- returns `{ ok: false, result: :async_expire_or_no_lock }` when a lock not found or a lock is already expired during
|
|
1745
1793
|
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);
|
|
1794
|
+
- **Important** (`:write` mode):
|
|
1795
|
+
- the write lock TTL is extended atomically by one Lua script (`PTTL` + `PEXPIRE` with "current pttl + additional milliseconds");
|
|
1796
|
+
- the lock owner is not checked: the current write lock is extended, so when your lock is expired and newly obtained
|
|
1797
|
+
by another acquirer, the lock of that acquirer is extended;
|
|
1757
1798
|
- use it at your own risk and consider the async nature when calling this method;
|
|
1758
1799
|
|
|
1759
1800
|
```ruby
|
|
@@ -1909,6 +1950,8 @@ rql.locks_info # or rql.locks_info(scan_size: 123)
|
|
|
1909
1950
|
"hst_id"=>"rql:hst:41478/4320/4360/848818f09d8c3420",
|
|
1910
1951
|
"ts"=>1711607112.670343,
|
|
1911
1952
|
"ini_ttl"=>15000,
|
|
1953
|
+
"lock_key"=>"rql:lock:some-lock-123",
|
|
1954
|
+
"rw_mode"=>"write",
|
|
1912
1955
|
"rem_ttl"=>13998}},
|
|
1913
1956
|
{:lock=>"rql:lock:some-lock-456",
|
|
1914
1957
|
:status=>:released,
|
|
@@ -2017,7 +2060,8 @@ Accepts:
|
|
|
2017
2060
|
- makes sense when instrumentation sampling is enabled;
|
|
2018
2061
|
- `false` by default;
|
|
2019
2062
|
|
|
2020
|
-
Returns: `{ ok: true, processed_queues: Set<String> }` returns the
|
|
2063
|
+
Returns: `{ ok: true, result: { processed_queues: Set<String> } }` returns the set of processed lock request queues
|
|
2064
|
+
(write and read lock request queues);
|
|
2021
2065
|
|
|
2022
2066
|
```ruby
|
|
2023
2067
|
rql.clear_dead_requests(dead_ttl: 60 * 60 * 1000) # 1 hour in milliseconds
|
|
@@ -2025,12 +2069,15 @@ rql.clear_dead_requests(dead_ttl: 60 * 60 * 1000) # 1 hour in milliseconds
|
|
|
2025
2069
|
# =>
|
|
2026
2070
|
{
|
|
2027
2071
|
ok: true,
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2072
|
+
result: {
|
|
2073
|
+
processed_queues: #<Set: {
|
|
2074
|
+
"rql:lock_queue:some-lock-123",
|
|
2075
|
+
"rql:lock_queue:some-lock-456",
|
|
2076
|
+
"rql:lock_read_queue:some-lock-456",
|
|
2077
|
+
"rql:lock_queue:your-other-lock",
|
|
2078
|
+
...
|
|
2079
|
+
}>
|
|
2080
|
+
}
|
|
2034
2081
|
}
|
|
2035
2082
|
```
|
|
2036
2083
|
|
|
@@ -2061,6 +2108,41 @@ rql.clear_dead_requests(dead_ttl: 60 * 60 * 1000) # 1 hour in milliseconds
|
|
|
2061
2108
|
- `:lock_index_grace_period` - (optional) `[Integer]`
|
|
2062
2109
|
- the lock index grace period (in milliseconds);
|
|
2063
2110
|
- pre-configured in `config['lock_index_grace_period']`;
|
|
2111
|
+
- `:logger` - (optional) `[::Logger,#debug]`
|
|
2112
|
+
- custom logger object;
|
|
2113
|
+
- pre-configured in `config['logger']`;
|
|
2114
|
+
- `:instrumenter` - (optional) `[#notify]`
|
|
2115
|
+
- custom instrumenter object;
|
|
2116
|
+
- pre-configured in `config['instrumetner']`;
|
|
2117
|
+
- `:instrument` - (optional) `[NilClass,Any]`;
|
|
2118
|
+
- custom instrumentation data wich will be passed to the instrumenter's payload with :instrument key;
|
|
2119
|
+
- `nil` by default (no additional data);
|
|
2120
|
+
- `:log_sampling_enabled` - (optional) `[Boolean]`
|
|
2121
|
+
- enables **log sampling**;
|
|
2122
|
+
- pre-configured in `config['log_sampling_enabled']`;
|
|
2123
|
+
- `:log_sampling_percent` - (optional) `[Integer]`
|
|
2124
|
+
- **log sampling**:the percent of cases that should be logged;
|
|
2125
|
+
- pre-configured in `config['log_sampling_percent']`;
|
|
2126
|
+
- `:log_sampler` - (optional) `[#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]`
|
|
2127
|
+
- **log sampling**: percent-based log sampler that decides should be RQL case logged or not;
|
|
2128
|
+
- pre-configured in `config['log_sampler']`;
|
|
2129
|
+
- `log_sample_this` - (optional) `[Boolean]`
|
|
2130
|
+
- marks the method that everything should be logged despite the enabled log sampling;
|
|
2131
|
+
- makes sense when log sampling is enabled;
|
|
2132
|
+
- `false` by default;
|
|
2133
|
+
- `:instr_sampling_enabled` - (optional) `[Boolean]`
|
|
2134
|
+
- enables **instrumentaion sampling**;
|
|
2135
|
+
- pre-configured in `config['instr_sampling_enabled']`;
|
|
2136
|
+
- `instr_sampling_percent` - (optional) `[Integer]`
|
|
2137
|
+
- the percent of cases that should be instrumented;
|
|
2138
|
+
- pre-configured in `config['instr_sampling_percent']`;
|
|
2139
|
+
- `instr_sampler` - (optional) `[#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]`
|
|
2140
|
+
- percent-based log sampler that decides should be RQL case instrumented or not;
|
|
2141
|
+
- pre-configured in `config['instr_sampler']`;
|
|
2142
|
+
- `instr_sample_this` - (optional) `[Boolean]`
|
|
2143
|
+
- marks the method that everything should be instrumneted despite the enabled instrumentation sampling;
|
|
2144
|
+
- makes sense when instrumentation sampling is enabled;
|
|
2145
|
+
- `false` by default;
|
|
2064
2146
|
- returns `{ ok: true, result: { idx_key_cnt: Integer } }` (the number of indexed keys);
|
|
2065
2147
|
|
|
2066
2148
|
```ruby
|
|
@@ -2235,6 +2317,8 @@ rql.possible_host_ids
|
|
|
2235
2317
|
# NOTE: auto-swarm your RQL client after initalization (run swarm elements and their supervisor)
|
|
2236
2318
|
config['swarm.auto_swarm'] = false
|
|
2237
2319
|
|
|
2320
|
+
# NOTE: the periods, the zombie ttl and the scan sizes should be positive Integers
|
|
2321
|
+
|
|
2238
2322
|
# supervisor configs
|
|
2239
2323
|
config['swarm.supervisor.liveness_probing_period'] = 2 # NOTE: in seconds
|
|
2240
2324
|
|
|
@@ -2526,7 +2610,7 @@ rql.possible_host_ids
|
|
|
2526
2610
|
<sup>\[[back to top](#table-of-contents)\]</sup>
|
|
2527
2611
|
|
|
2528
2612
|
- `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;
|
|
2613
|
+
- `:write` (**default**) - exclusive lock (the classic RQL lock): waits for the write lock and for all read locks;
|
|
2530
2614
|
- `:read` - shared lock: waits for the write lock only (read locks of different acquirers work in parallel);
|
|
2531
2615
|
|
|
2532
2616
|
```ruby
|
|
@@ -2535,7 +2619,9 @@ rql.lock('report', read_write_mode: :read) { read_report }
|
|
|
2535
2619
|
|
|
2536
2620
|
# writer waits for all readers (and readers wait for the writer)
|
|
2537
2621
|
rql.lock('report', read_write_mode: :write) { rebuild_report }
|
|
2538
|
-
|
|
2622
|
+
|
|
2623
|
+
# write mode is default:
|
|
2624
|
+
rql.lock('report') { rebuild_report } # read_write_mode: :write by default
|
|
2539
2625
|
```
|
|
2540
2626
|
|
|
2541
2627
|
- lock request ordering (`access_strategy`):
|
|
@@ -2545,7 +2631,7 @@ rql.lock('report') { rebuild_report } # (write mode is the default mode)
|
|
|
2545
2631
|
- `:random` - lock queues are ignored: any free lock is obtained (a continuous flow of readers can starve writers);
|
|
2546
2632
|
- read locks:
|
|
2547
2633
|
- 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
|
|
2634
|
+
- `ttl` should be a positive `Integer`;
|
|
2549
2635
|
- each read lock has its own data with the same structure as the write lock data: acquirer, host, timestamp,
|
|
2550
2636
|
initial ttl, `:meta` (with the same reserved keys) and reentrant lock data (`spc_cnt`, `spc_ext_ttl`, ...);
|
|
2551
2637
|
the read lock data lives as long as the read lock and is returned by `#lock_info`/`#locks_info` for each reader
|
|
@@ -2645,6 +2731,7 @@ rql.lock('report') { rebuild_report } # (write mode is the default mode)
|
|
|
2645
2731
|
"[redis_queued_locks.start_try_to_lock_cycle]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2646
2732
|
"[redis_queued_locks.dead_score_reached__reset_acquirer_position]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2647
2733
|
"[redis_queued_locks.lock_obtained]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
2734
|
+
"[redis_queued_locks.lock_obtaining_failed]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time", "result");
|
|
2648
2735
|
"[redis_queued_locks.extendable_reentrant_lock_obtained]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
2649
2736
|
"[redis_queued_locks.reentrant_lock_obtained]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
2650
2737
|
"[redis_queued_locks.fail_fast_or_limits_reached_or_deadlock__dequeue]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
@@ -2683,6 +2770,19 @@ rql.lock('report') { rebuild_report } # (write mode is the default mode)
|
|
|
2683
2770
|
"[redis_queued_locks.expire_lock_series]" # (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2684
2771
|
```
|
|
2685
2772
|
|
|
2773
|
+
- operation logs (one log line per operation that changes Redis keys, with the time of the whole operation; the same data as
|
|
2774
|
+
the instrumentation event of the operation without `instrument`):
|
|
2775
|
+
|
|
2776
|
+
```ruby
|
|
2777
|
+
"[redis_queued_locks.explicit_lock_release]" # (logs "lock_key", "lock_key_queue", "rel_time", "at");
|
|
2778
|
+
"[redis_queued_locks.explicit_read_lock_release]" # (logs "lock_key", "acq_id", "lock_res", "rel_time", "at");
|
|
2779
|
+
"[redis_queued_locks.explicit_all_locks_release]" # (logs "at", "rel_time", "rel_key_cnt");
|
|
2780
|
+
"[redis_queued_locks.release_locks_of]" # (logs "at", "hst_id", "acq_id", "rel_time", "rel_key_cnt", "tch_queue_cnt");
|
|
2781
|
+
"[redis_queued_locks.extend_lock_ttl]" # (logs "lock_key", "rw_mode", "all_read_locks", "ext_ttl", "extended_locks_count", "ext_time", "at");
|
|
2782
|
+
"[redis_queued_locks.clear_dead_requests]" # (logs "dead_ttl", "prc_queue_cnt", "rel_time", "at");
|
|
2783
|
+
"[redis_queued_locks.reindex_locks]" # (logs "idx_key_cnt", "idx_time", "at");
|
|
2784
|
+
```
|
|
2785
|
+
|
|
2686
2786
|
- read/write try-lock logs (see [Read/Write Locks](#readwrite-locks)):
|
|
2687
2787
|
- `single_process_lock_conflict__lock_upgrade` - write lock request of the acquirer that holds the read lock
|
|
2688
2788
|
(read-to-write lock upgrade is not supported for `:work_through` and `:extendable_work_through` conflict strategies);
|
|
@@ -2698,7 +2798,7 @@ rql.lock('report') { rebuild_report } # (write mode is the default mode)
|
|
|
2698
2798
|
<sup>\[[back to top](#table-of-contents)\]</sup>
|
|
2699
2799
|
|
|
2700
2800
|
**NOTICE**: logging can be sampled via:
|
|
2701
|
-
- `config['
|
|
2801
|
+
- `config['log_sampling_enabled'] = true` (**false** by default);
|
|
2702
2802
|
- `config['log_sampler'] = RedisQueuedLocks::Logging::Sampler` (used by default);
|
|
2703
2803
|
- see **RedisQueuedLocks::Logging::Sampler** implementation in source code for customization details;
|
|
2704
2804
|
|
|
@@ -2712,6 +2812,7 @@ rql.lock('report') { rebuild_report } # (write mode is the default mode)
|
|
|
2712
2812
|
# - "[redis_queued_locks.start_try_to_lock_cycle]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2713
2813
|
# - "[redis_queued_locks.dead_score_reached__reset_acquirer_position]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2714
2814
|
# - "[redis_queued_locks.lock_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
2815
|
+
# - "[redis_queued_locks.lock_obtaining_failed]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time", "result");
|
|
2715
2816
|
# - "[redis_queued_locks.extendable_reentrant_lock_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
2716
2817
|
# - "[redis_queued_locks.reentrant_lock_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
2717
2818
|
# - "[redis_queued_locks.fail_fast_or_limits_reached_or_deadlock__dequeue]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
@@ -2720,6 +2821,13 @@ rql.lock('report') { rebuild_report } # (write mode is the default mode)
|
|
|
2720
2821
|
# - "[redis_queued_locks.start_lock_series_obtaining]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2721
2822
|
# - "[redis_queued_locks.lock_series_obtained]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
2722
2823
|
# - "[redis_queued_locks.expire_lock_series]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2824
|
+
# - "[redis_queued_locks.explicit_lock_release]" (logs "lock_key", "lock_key_queue", "rel_time", "at");
|
|
2825
|
+
# - "[redis_queued_locks.explicit_read_lock_release]" (logs "lock_key", "acq_id", "lock_res", "rel_time", "at");
|
|
2826
|
+
# - "[redis_queued_locks.explicit_all_locks_release]" (logs "at", "rel_time", "rel_key_cnt");
|
|
2827
|
+
# - "[redis_queued_locks.release_locks_of]" (logs "at", "hst_id", "acq_id", "rel_time", "rel_key_cnt", "tch_queue_cnt");
|
|
2828
|
+
# - "[redis_queued_locks.extend_lock_ttl]" (logs "lock_key", "rw_mode", "all_read_locks", "ext_ttl", "extended_locks_count", "ext_time", "at");
|
|
2829
|
+
# - "[redis_queued_locks.clear_dead_requests]" (logs "dead_ttl", "prc_queue_cnt", "rel_time", "at");
|
|
2830
|
+
# - "[redis_queued_locks.reindex_locks]" (logs "idx_key_cnt", "idx_time", "at");
|
|
2723
2831
|
# - "rw_mode" is the requested lock mode ('write'/'read'), the held lock mode for "expire_lock" and "decrease_lock",
|
|
2724
2832
|
# the mode of the series for lock series logs;
|
|
2725
2833
|
# - by default uses VoidLogger that does nothing;
|
|
@@ -2758,7 +2866,7 @@ config['log_sampling_enabled'] = false
|
|
|
2758
2866
|
|
|
2759
2867
|
# (default: 15)
|
|
2760
2868
|
# - the percent of cases that should be logged;
|
|
2761
|
-
# - take an effect when <config['
|
|
2869
|
+
# - take an effect when <config['log_sampling_enabled']> is true;
|
|
2762
2870
|
# - works in tandem with <config['log_sampling_enabled']> and <config['log_sampler']> configs;
|
|
2763
2871
|
config['log_sampling_percent'] = 15
|
|
2764
2872
|
|
|
@@ -2825,7 +2933,7 @@ config['instr_sampling_enabled'] = false
|
|
|
2825
2933
|
|
|
2826
2934
|
# (default: 15)
|
|
2827
2935
|
# - the percent of cases that should be instrumented;
|
|
2828
|
-
# - take an effect when <config['
|
|
2936
|
+
# - take an effect when <config['instr_sampling_enabled']> is true;
|
|
2829
2937
|
# - works in tandem with <config['instr_sampling_enabled']> and <config['instr_sampler']> configs;
|
|
2830
2938
|
config['instr_sampling_percent'] = 15
|
|
2831
2939
|
|
|
@@ -2848,6 +2956,7 @@ config['instr_sampler'] = RedisQueuedLocks::Instrument::Sampler
|
|
|
2848
2956
|
List of instrumentation events
|
|
2849
2957
|
|
|
2850
2958
|
- ["redis_queued_locks.lock_obtained"](#redis_queued_lockslock_obtained)
|
|
2959
|
+
- ["redis_queued_locks.lock_obtaining_failed"](#redis_queued_lockslock_obtaining_failed)
|
|
2851
2960
|
- ["redis_queued_locks.extendable_reentrant_lock_obtained"](#redis_queued_locksextendable_reentrant_lock_obtained)
|
|
2852
2961
|
- ["redis_queued_locks.reentrant_lock_obtained"](#redis_queued_locksreentrant_lock_obtained)
|
|
2853
2962
|
- ["redis_queued_locks.lock_hold_and_release"](#redis_queued_lockslock_hold_and_release)
|
|
@@ -2858,6 +2967,17 @@ List of instrumentation events
|
|
|
2858
2967
|
- ["redis_queued_locks.explicit_read_lock_release"](#redis_queued_locksexplicit_read_lock_release)
|
|
2859
2968
|
- ["redis_queued_locks.explicit_all_locks_release"](#redis_queued_locksexplicit_all_locks_release)
|
|
2860
2969
|
- ["redis_queued_locks.release_locks_of"](#redis_queued_locksrelease_locks_of)
|
|
2970
|
+
- ["redis_queued_locks.extend_lock_ttl"](#redis_queued_locksextend_lock_ttl)
|
|
2971
|
+
- ["redis_queued_locks.clear_dead_requests"](#redis_queued_locksclear_dead_requests)
|
|
2972
|
+
- ["redis_queued_locks.reindex_locks"](#redis_queued_locksreindex_locks)
|
|
2973
|
+
|
|
2974
|
+
What is instrumented (each event has a log line with the same name, see [Logging](#logging)):
|
|
2975
|
+
- the lock acquirement (`#lock`/`#lock!`, `#lock_series`/`#lock_series!`): each meaningful outcome (the lock is obtained,
|
|
2976
|
+
not obtained, held and released);
|
|
2977
|
+
- each operation that changes Redis keys (`#unlock`, `#unlock_read`, `#clear_locks`, `#clear_locks_of`/`#clear_current_locks`,
|
|
2978
|
+
`#extend_lock_ttl`, `#clear_dead_requests`, `#reindex_locks`): one event with the time of the whole operation;
|
|
2979
|
+
- lock information queries (`#lock_info`, `#locks`, `#queues`, `#keys`, ...) are not instrumented;
|
|
2980
|
+
- the swarm (`#probe_hosts`, `#flush_zombies` and the swarm elements) is not instrumented yet;
|
|
2861
2981
|
|
|
2862
2982
|
Detalized event semantics and payload structure:
|
|
2863
2983
|
|
|
@@ -2875,6 +2995,23 @@ Detalized event semantics and payload structure:
|
|
|
2875
2995
|
- `:acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
|
|
2876
2996
|
- `:instrument` - `nil`/`Any` - custom data passed to the `#lock`/`#lock!` method as `:instrument` attribute;
|
|
2877
2997
|
|
|
2998
|
+
##### `"redis_queued_locks.lock_obtaining_failed"`
|
|
2999
|
+
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
3000
|
+
- a moment when the lock acquirement is failed (the lock is not obtained);
|
|
3001
|
+
- raised from `#lock`/`#lock!` (with `raise_errors: true` - before the error is raised) and from `#lock_series`/`#lock_series!`
|
|
3002
|
+
(for the lock of the series that is not obtained);
|
|
3003
|
+
- payload:
|
|
3004
|
+
- `:lock_key` - `string` - lock name;
|
|
3005
|
+
- `:rw_mode` - `symbol` - requested lock mode: `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
3006
|
+
- `:ttl` - `integer`/`milliseconds` - requested lock ttl;
|
|
3007
|
+
- `:acq_id` - `string` - lock acquirer identifier;
|
|
3008
|
+
- `:hst_id` - `string` - lock's host identifier;
|
|
3009
|
+
- `:ts` - `numeric`/`epoch` - the time when the lock acquirement is failed;
|
|
3010
|
+
- `:acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
|
|
3011
|
+
- `:result` - `symbol` - the failure result: `:timeout_reached`, `:retry_limit_reached`, `:fail_fast_no_try`,
|
|
3012
|
+
`:fail_fast_after_try`, `:conflict_dead_lock`, `:conflict_lock_upgrade` (see [lock](#lock---obtain-a-lock));
|
|
3013
|
+
- `:instrument` - `nil`/`Any` - custom data passed to the `#lock`/`#lock!` method as `:instrument` attribute;
|
|
3014
|
+
|
|
2878
3015
|
##### `"redis_queued_locks.extendable_reentrant_lock_obtained"`
|
|
2879
3016
|
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
2880
3017
|
- an event signalizes about the "extendable reentrant lock" obtaining is happened;
|
|
@@ -2977,6 +3114,7 @@ Detalized event semantics and payload structure:
|
|
|
2977
3114
|
- `:rel_time` - `float`/`milliseconds` - time spent on lock releasing;
|
|
2978
3115
|
- `:lock_key` - `string` - released lock (lock name);
|
|
2979
3116
|
- `:lock_key_queue` - `string` - released lock queue (the queue of write lock requests);
|
|
3117
|
+
- `:instrument` - `nil`/`Any` - custom data passed to the `#unlock` method as `:instrument` attribute;
|
|
2980
3118
|
|
|
2981
3119
|
##### `"redis_queued_locks.explicit_read_lock_release"`
|
|
2982
3120
|
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
@@ -2999,6 +3137,7 @@ Detalized event semantics and payload structure:
|
|
|
2999
3137
|
- `:at` - `float`/`epoch` - the time when the operation has ended;
|
|
3000
3138
|
- `:rel_key_cnt` - `integer` - released redis keys count (write locks, readers registries, read lock data,
|
|
3001
3139
|
write and read lock request queues);
|
|
3140
|
+
- `:instrument` - `nil`/`Any` - custom data passed to the `#clear_locks` method as `:instrument` attribute;
|
|
3002
3141
|
|
|
3003
3142
|
##### `"redis_queued_locks.release_locks_of"`
|
|
3004
3143
|
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
@@ -3011,6 +3150,42 @@ Detalized event semantics and payload structure:
|
|
|
3011
3150
|
- `:hst_id` - `string` - refused host identifier;
|
|
3012
3151
|
- `:rel_key_cnt` - `integer` - released locks count (write locks and read locks);
|
|
3013
3152
|
- `:tch_queue_cnt` - `:integer` - the number of queues (write and read lock request queues) from which the concrete host/acquirer was removed;
|
|
3153
|
+
- `:instrument` - `nil`/`Any` - custom data passed to the `#clear_locks_of`/`#clear_current_locks` method as `:instrument` attribute;
|
|
3154
|
+
|
|
3155
|
+
##### `"redis_queued_locks.extend_lock_ttl"`
|
|
3156
|
+
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
3157
|
+
- an event signalizes about the lock TTL extension (successful or not);
|
|
3158
|
+
- raised from `#extend_lock_ttl`;
|
|
3159
|
+
- payload:
|
|
3160
|
+
- `:lock_key` - `string` - lock key (`rql:lock:<lock_name>`);
|
|
3161
|
+
- `:rw_mode` - `symbol` - `:write` (the write lock) or `:read` (the read lock of the acquirer or all read locks);
|
|
3162
|
+
- `:all_read_locks` - `boolean` - all read locks of the lock are extended (`:read` mode);
|
|
3163
|
+
- `:ext_ttl` - `integer`/`milliseconds` - the extension (`milliseconds` argument);
|
|
3164
|
+
- `:extended_locks_count` - `integer` - the number of extended locks (`0` when nothing is extended);
|
|
3165
|
+
- `:ext_time` - `float`/`milliseconds` - time spent on the extension;
|
|
3166
|
+
- `:at` - `float`/`epoch` - the time when the operation has ended;
|
|
3167
|
+
- `:instrument` - `nil`/`Any` - custom data passed to the `#extend_lock_ttl` method as `:instrument` attribute;
|
|
3168
|
+
|
|
3169
|
+
##### `"redis_queued_locks.clear_dead_requests"`
|
|
3170
|
+
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
3171
|
+
- an event signalizes about the dropped dead lock requests;
|
|
3172
|
+
- raised from `#clear_dead_requests`;
|
|
3173
|
+
- payload:
|
|
3174
|
+
- `:dead_ttl` - `integer`/`milliseconds` - lock requests older than `dead_ttl` are dropped;
|
|
3175
|
+
- `:prc_queue_cnt` - `integer` - the number of processed lock request queues (write and read);
|
|
3176
|
+
- `:rel_time` - `float`/`milliseconds` - time spent on the operation;
|
|
3177
|
+
- `:at` - `float`/`epoch` - the time when the operation has ended;
|
|
3178
|
+
- `:instrument` - `nil`/`Any` - custom data passed to the `#clear_dead_requests` method as `:instrument` attribute;
|
|
3179
|
+
|
|
3180
|
+
##### `"redis_queued_locks.reindex_locks"`
|
|
3181
|
+
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
3182
|
+
- an event signalizes about the lock keys added to the [Lock Index](#lock-index);
|
|
3183
|
+
- raised from `#reindex_locks`;
|
|
3184
|
+
- payload:
|
|
3185
|
+
- `:idx_key_cnt` - `integer` - the number of indexed keys;
|
|
3186
|
+
- `:idx_time` - `float`/`milliseconds` - time spent on the operation;
|
|
3187
|
+
- `:at` - `float`/`epoch` - the time when the operation has ended;
|
|
3188
|
+
- `:instrument` - `nil`/`Any` - custom data passed to the `#reindex_locks` method as `:instrument` attribute;
|
|
3014
3189
|
|
|
3015
3190
|
---
|
|
3016
3191
|
|