redis_queued_locks 1.22.0 → 1.23.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 +88 -36
- data/.claude/rules/acquirer.md +11 -7
- data/.claude/rules/arguments.md +2 -2
- data/.claude/rules/logic.md +5 -4
- data/.claude/rules/tests.md +4 -4
- data/.claude/rules/type-checking.md +2 -3
- data/.claude/rules/visitors.md +8 -6
- data/.claude/skills/rql-code-review/SKILL.md +25 -20
- data/.ruby-version +1 -1
- data/CHANGELOG.md +24 -1
- data/CLAUDE.md +5 -4
- data/README.md +247 -96
- data/lib/redis_queued_locks/acquirer/acquire_lock/dequeue_from_lock_queue.rb +4 -4
- data/lib/redis_queued_locks/acquirer/acquire_lock/try_to_lock.rb +582 -276
- data/lib/redis_queued_locks/acquirer/acquire_lock.rb +26 -26
- data/lib/redis_queued_locks/acquirer/extend_lock_ttl.rb +13 -13
- data/lib/redis_queued_locks/acquirer/lock_series/instr_visitor.rb +243 -0
- data/lib/redis_queued_locks/acquirer/lock_series/log_visitor.rb +472 -0
- data/lib/redis_queued_locks/acquirer/lock_series/release_lock_series.rb +181 -0
- data/lib/redis_queued_locks/acquirer/lock_series/try_to_lock_series.rb +1051 -0
- data/lib/redis_queued_locks/acquirer/lock_series.rb +948 -0
- data/lib/redis_queued_locks/acquirer.rb +1 -1
- data/lib/redis_queued_locks/client.rb +55 -25
- data/lib/redis_queued_locks/version.rb +2 -2
- data/redis_queued_locks.gemspec +21 -14
- data/sig/redis_queued_locks/acquirer/acquire_lock/dequeue_from_lock_queue.rbs +1 -1
- data/sig/redis_queued_locks/acquirer/acquire_lock/try_to_lock.rbs +106 -5
- data/sig/redis_queued_locks/acquirer/acquire_lock.rbs +1 -1
- data/sig/redis_queued_locks/acquirer/extend_lock_ttl.rbs +2 -2
- data/sig/redis_queued_locks/acquirer/lock_series/instr_visitor.rbs +102 -0
- data/sig/redis_queued_locks/acquirer/lock_series/log_visitor.rbs +154 -0
- data/sig/redis_queued_locks/acquirer/lock_series/release_lock_series.rbs +37 -0
- data/sig/redis_queued_locks/acquirer/lock_series/try_to_lock_series.rbs +198 -0
- data/sig/redis_queued_locks/acquirer/lock_series.rbs +106 -0
- data/sig/redis_queued_locks/client.rbs +70 -3
- metadata +32 -11
- data/lib/redis_queued_locks/acquirer/lock_series_poc/instr_visitor.rb +0 -61
- data/lib/redis_queued_locks/acquirer/lock_series_poc/log_visitor.rb +0 -104
- data/lib/redis_queued_locks/acquirer/lock_series_poc.rb +0 -513
data/README.md
CHANGED
|
@@ -29,8 +29,8 @@ Provides flexible invocation flow, parametrized limits (lock request ttl, lock t
|
|
|
29
29
|
- [Usage](#usage)
|
|
30
30
|
- [lock](#lock---obtain-a-lock)
|
|
31
31
|
- [lock!](#lock---exceptional-lock-obtaining)
|
|
32
|
-
- [lock_series](#lock_series---
|
|
33
|
-
- [lock_series!](#lock_series---
|
|
32
|
+
- [lock_series](#lock_series---acquire-a-series-of-locks)
|
|
33
|
+
- [lock_series!](#lock_series---exceptional-lock_series)
|
|
34
34
|
- [lock_info](#lock_info)
|
|
35
35
|
- [queue_info](#queue_info)
|
|
36
36
|
- [locked?](#locked)
|
|
@@ -85,8 +85,12 @@ Provides flexible invocation flow, parametrized limits (lock request ttl, lock t
|
|
|
85
85
|
- ["redis_queued_locks.reentrant_lock_obtained"](#redis_queued_locksreentrant_lock_obtained)
|
|
86
86
|
- ["redis_queued_locks.lock_hold_and_release"](#redis_queued_lockslock_hold_and_release)
|
|
87
87
|
- ["redis_queued_locks.reentrant_lock_hold_completes"](#redis_queued_locksreentrant_lock_hold_completes)
|
|
88
|
+
- ["redis_queued_locks.lock_series_intermediate_lock_obtained"](#redis_queued_lockslock_series_intermediate_lock_obtained)
|
|
89
|
+
- ["redis_queued_locks.lock_series_lock_obtained"](#redis_queued_lockslock_series_lock_obtained)
|
|
90
|
+
- ["redis_queued_locks.lock_series_lock_obtaining_failed"](#redis_queued_lockslock_series_lock_obtaining_failed)
|
|
88
91
|
- ["redis_queued_locks.lock_series_obtained"](#redis_queued_lockslock_series_obtained)
|
|
89
92
|
- ["redis_queued_locks.lock_series_hold_and_release"](#redis_queued_lockslock_series_hold_and_release)
|
|
93
|
+
- ["redis_queued_locks.lock_series_obtaining_failed"](#redis_queued_lockslock_series_obtaining_failed)
|
|
90
94
|
- ["redis_queued_locks.explicit_lock_release"](#redis_queued_locksexplicit_lock_release)
|
|
91
95
|
- ["redis_queued_locks.explicit_read_lock_release"](#redis_queued_locksexplicit_read_lock_release)
|
|
92
96
|
- ["redis_queued_locks.explicit_all_locks_release"](#redis_queued_locksexplicit_all_locks_release)
|
|
@@ -376,8 +380,16 @@ client = RedisQueuedLocks::Client.new(redis_client) do |config|
|
|
|
376
380
|
# - "[redis_queued_locks.expire_lock]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
377
381
|
# - "[redis_queued_locks.decrease_lock]" (logs "lock_key", "decreased_ttl", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
378
382
|
# - "[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.
|
|
383
|
+
# - "[redis_queued_locks.start_try_to_lock_series_cycle]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
384
|
+
# - "[redis_queued_locks.dead_score_reached__reset_series_position]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
385
|
+
# - "[redis_queued_locks.lock_series_intermediate_lock_obtained]" (logs "lock_key", "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time");
|
|
386
|
+
# - "[redis_queued_locks.lock_series_lock_obtained]" (logs "lock_key", "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time");
|
|
387
|
+
# - "[redis_queued_locks.lock_series_lock_obtaining_failed]" (logs "lock_key", "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time", "result");
|
|
388
|
+
# - "[redis_queued_locks.lock_series_obtained]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time", "reentrant_lock_keys");
|
|
389
|
+
# - "[redis_queued_locks.lock_series_obtaining_failed]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time", "result", "failed_lock_key");
|
|
390
|
+
# - "[redis_queued_locks.dequeue_from_lock_series_queues]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
380
391
|
# - "[redis_queued_locks.expire_lock_series]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
392
|
+
# - "[redis_queued_locks.decrease_lock_series]" (logs "lock_keys", "decreased_ttl", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
381
393
|
# - "[redis_queued_locks.explicit_lock_release]" (logs "lock_key", "lock_key_queue", "rel_time", "at");
|
|
382
394
|
# - "[redis_queued_locks.explicit_read_lock_release]" (logs "lock_key", "acq_id", "lock_res", "rel_time", "at");
|
|
383
395
|
# - "[redis_queued_locks.explicit_all_locks_release]" (logs "at", "rel_time", "rel_key_cnt");
|
|
@@ -468,8 +480,8 @@ end
|
|
|
468
480
|
|
|
469
481
|
- [lock](#lock---obtain-a-lock)
|
|
470
482
|
- [lock!](#lock---exceptional-lock-obtaining)
|
|
471
|
-
- [lock_series](#lock_series---
|
|
472
|
-
- [lock_series!](#lock_series---
|
|
483
|
+
- [lock_series](#lock_series---acquire-a-series-of-locks)
|
|
484
|
+
- [lock_series!](#lock_series---exceptional-lock_series)
|
|
473
485
|
- [lock_info](#lock_info)
|
|
474
486
|
- [queue_info](#queue_info)
|
|
475
487
|
- [locked?](#locked)
|
|
@@ -528,7 +540,7 @@ def lock(
|
|
|
528
540
|
raise_errors: false,
|
|
529
541
|
fail_fast: false,
|
|
530
542
|
conflict_strategy: config['default_conflict_strategy'],
|
|
531
|
-
|
|
543
|
+
rw_mode: :write,
|
|
532
544
|
access_strategy: config['default_access_strategy'],
|
|
533
545
|
priority: config['default_lock_priority'],
|
|
534
546
|
identity: uniq_identity, # (attr_accessor) calculated during client instantiation via config['uniq_identifier'] proc;
|
|
@@ -637,7 +649,7 @@ def lock(
|
|
|
637
649
|
- `:wait_for_lock` - (default) - work in classic way (with timeouts, retry delays, retry limits, etc - in classic way :));
|
|
638
650
|
- `:dead_locking` - fail with deadlock exception;
|
|
639
651
|
- See [Deadlocks and Reentrant locks](#deadlocks-and-reentrant-locks) documentation section for details;
|
|
640
|
-
- `
|
|
652
|
+
- `rw_mode` - (optional) - `[Symbol]`
|
|
641
653
|
- The lock mode (read/write lock semantics);
|
|
642
654
|
- `:write` by default;
|
|
643
655
|
- Modes:
|
|
@@ -799,7 +811,7 @@ Return value:
|
|
|
799
811
|
{ ok: false, result: :timeout_reached } # see <timeout> option
|
|
800
812
|
{ ok: false, result: :retry_limit_reached } # see <retry_count> option
|
|
801
813
|
{ 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 <
|
|
814
|
+
{ ok: false, result: :conflict_lock_upgrade } # read-to-write lock upgrade (see <rw_mode> and <conflict_strategy> options)
|
|
803
815
|
{ ok: false, result: :fail_fast_no_try } # see <fail_fast> option
|
|
804
816
|
{ ok: false, result: :fail_fast_after_try } # see <fail_fast> option
|
|
805
817
|
```
|
|
@@ -989,7 +1001,7 @@ def lock!(
|
|
|
989
1001
|
access_strategy: config['default_access_strategy'],
|
|
990
1002
|
priority: config['default_lock_priority'],
|
|
991
1003
|
conflict_strategy: config['default_conflict_strategy'],
|
|
992
|
-
|
|
1004
|
+
rw_mode: :write,
|
|
993
1005
|
log_sampling_enabled: config['log_sampling_enabled'],
|
|
994
1006
|
log_sampling_percent: config['log_sampling_percent'],
|
|
995
1007
|
log_sampler: config['log_sampler'],
|
|
@@ -1006,39 +1018,56 @@ See `#lock` method [documentation](#lock---obtain-a-lock).
|
|
|
1006
1018
|
|
|
1007
1019
|
---
|
|
1008
1020
|
|
|
1009
|
-
#### #lock_series -
|
|
1021
|
+
#### #lock_series - acquire a series of locks
|
|
1010
1022
|
|
|
1011
1023
|
<sup>\[[back to top](#usage)\]</sup>
|
|
1012
1024
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
**
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
-
|
|
1034
|
-
|
|
1025
|
+
`lock_series` - obtain a group of locks atomically: all locks of the series are obtained at the same moment with the same TTL
|
|
1026
|
+
and are held together while your block of code is executing (or, if a block is not passed, while the locks live).
|
|
1027
|
+
|
|
1028
|
+
**Guarantees**:
|
|
1029
|
+
- **all or nothing**: one transaction obtains all locks of the series; an attempt that can not obtain some lock obtains nothing,
|
|
1030
|
+
so a failed series leaves no obtained locks (and no lock requests in the lock queues);
|
|
1031
|
+
- **all locks at the same time**: all locks are obtained with the same TTL (`ttl:`) at the same moment, so the block of code
|
|
1032
|
+
never works with a partially held series and `timed: true` limits it by the real lifetime of the locks;
|
|
1033
|
+
- **no hold-and-wait**: nothing is held while the series waits for its locks: the free locks of the series are not locked
|
|
1034
|
+
and their TTL is not spent on the waiting;
|
|
1035
|
+
- **no deadlocks between lock series**: the order of the lock names does not matter (`lock_series("a", "b")` and
|
|
1036
|
+
`lock_series("b", "a")` work in parallel threads): the lock request of the series has one position in all its lock queues,
|
|
1037
|
+
so the lock requests wait only for earlier lock requests. Your own nested logic is out of it: code that holds a lock and
|
|
1038
|
+
then waits for a series still can wait for a lock that is needed by another acquirer (the waiting is limited by the limits
|
|
1039
|
+
of the series);
|
|
1040
|
+
|
|
1041
|
+
**How the options work**:
|
|
1042
|
+
- method options are the same as for the `#lock` method: `ttl`, `queue_ttl`, `retry_count`, `retry_delay`, `retry_jitter`, etc;
|
|
1043
|
+
- limits are the limits of **the whole series**, not of each lock: `timeout:` is one time period for all locks, `retry_count:` is
|
|
1044
|
+
the number of attempts to obtain all locks, `queue_ttl:` is the lifetime of the lock request of the series;
|
|
1045
|
+
- `ttl:` is the TTL of each lock of the series (the same value for all locks);
|
|
1046
|
+
- `rw_mode:` is the same for all locks of the series (see [Read/Write Locks](#readwrite-locks));
|
|
1047
|
+
- `priority:` is the same for all locks of the series (see [Lock Request Prioritization](#lock-request-prioritization)):
|
|
1048
|
+
the lock request of the series has this priority in all its lock queues;
|
|
1049
|
+
- `conflict_strategy:` works per lock: reentrant locks (already obtained by your outer logic, `:work_through`/`:extendable_work_through`)
|
|
1050
|
+
are not obtained and not released by the series: the extended ones (`:extendable_work_through`) get their TTL back after the block,
|
|
1051
|
+
like in the `#lock` method; a lock with a failed conflict (`:dead_locking`, read-to-write lock upgrade) fails the whole series;
|
|
1052
|
+
- `fail_fast: true` fails the series on the first lock that is already obtained;
|
|
1053
|
+
- a duplicated lock name is the same lock (`lock_series("a", "b", "a")` works with two locks); at least one lock name is required
|
|
1054
|
+
(`RedisQueuedLocks::ArgumentError`);
|
|
1055
|
+
|
|
1056
|
+
**Details and limitations**:
|
|
1057
|
+
- when your block of code has completed its execution (with or without an exception) all locks obtained by the series are released;
|
|
1058
|
+
- an interrupted series (`Timeout.timeout` of your logic, `Thread#kill`, a Redis error) releases its locks and drops its lock requests,
|
|
1059
|
+
when it is possible (a dead process or an unavailable Redis: the locks live till their TTL, the lock requests till their `queue_ttl`);
|
|
1035
1060
|
- the series releases only locks obtained by itself and only if they are still owned by the current acquirer:
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
-
|
|
1039
|
-
|
|
1040
|
-
-
|
|
1041
|
-
|
|
1061
|
+
a lock that is expired during your block and obtained by another acquirer is kept;
|
|
1062
|
+
- if you did not pass a block - all locks live till their TTL;
|
|
1063
|
+
- locks of the series are regular locks with regular keys: `#lock_info`, `#unlock`, `#extend_lock_ttl`, `#clear_locks` and the swarm
|
|
1064
|
+
work with each of them separately;
|
|
1065
|
+
- `:queued` access strategy: a series that is ahead of other lock requests makes them wait even for its free locks (the waiting
|
|
1066
|
+
series is the next obtainer of these locks): one long-waiting series delays the lock requests of all its locks for its waiting time
|
|
1067
|
+
(no longer than its `queue_ttl`, `timeout` and `retry_count` limits allow);
|
|
1068
|
+
- `:random` access strategy: the series is obtained only when all its locks are free at the same moment, so a big series can
|
|
1069
|
+
wait for a long time under load (lock requests of the `:random` strategy have no reserved turn);
|
|
1070
|
+
- all processes that work with the same locks should use the same RQL version: older versions obtain the series lock by lock;
|
|
1042
1071
|
|
|
1043
1072
|
##### How to use:
|
|
1044
1073
|
|
|
@@ -1049,12 +1078,27 @@ applied with the same value to the each lock acquirement process (for each lock
|
|
|
1049
1078
|
client.lock_series("a", "b", "c") { ...some_code... }
|
|
1050
1079
|
|
|
1051
1080
|
# result-oriented way:
|
|
1052
|
-
# - (block is passed)
|
|
1081
|
+
# - (block is not passed)
|
|
1082
|
+
client.lock_series('x', 'y', 'z', ttl: 5_000)
|
|
1083
|
+
# =>
|
|
1084
|
+
{
|
|
1085
|
+
ok: true,
|
|
1086
|
+
result: {
|
|
1087
|
+
lock_series: ["x", "y", "z"], # your locks
|
|
1088
|
+
rql_lock_series: ["rql:lock:x", "rql:lock:y", "rql:lock:z"], # internal RQL lock-keys of your locks
|
|
1089
|
+
acq_id: "rql:acq:4486/1696/1704/1712/e6fd0da7991e4303", # the acquirer of the locks
|
|
1090
|
+
hst_id: "rql:hst:4486/1696/1712/e6fd0da7991e4303", # the host of the locks
|
|
1091
|
+
ts: 1769506100.676884, # the time when the locks were obtained, epoch
|
|
1092
|
+
ttl: 5000 # the ttl of each lock, milliseconds
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
# - (detailed result, block is passed)
|
|
1053
1097
|
detailed_result = client.lock_series('x', 'y', 'z', detailed_result: true) { 1 + 2 }
|
|
1054
1098
|
detailed_result # =>
|
|
1055
1099
|
{
|
|
1056
1100
|
yield_result: 3, # result of your block of code
|
|
1057
|
-
|
|
1101
|
+
locks_release_strategy: :immediate_release_after_yield, # (block is passed)
|
|
1058
1102
|
locks_released_at: 1769506100.676884, # (instrumentation) release time, epoch
|
|
1059
1103
|
locks_acq_time: 7.0, # (instrumentation) time spent to acquire all locks, milliseconds
|
|
1060
1104
|
locks_hold_time: 0.65, # (instrumentation) lock series hold period (hold preiod of all required locks), milliseconds
|
|
@@ -1062,12 +1106,12 @@ detailed_result # =>
|
|
|
1062
1106
|
rql_lock_series: ["rql:lock:x", "rql:lock:y", "rql:lock:z"] # internal RQL lock-keys of your locks
|
|
1063
1107
|
}
|
|
1064
1108
|
|
|
1065
|
-
# - (block is not passed)
|
|
1109
|
+
# - (detailed result, block is not passed)
|
|
1066
1110
|
detailed_result = client.lock_series('x', 'y', 'z', detailed_result: true)
|
|
1067
1111
|
detailed_result # =>
|
|
1068
1112
|
{
|
|
1069
1113
|
yield_result: nil, # block is not given - nothing to return
|
|
1070
|
-
|
|
1114
|
+
locks_release_strategy: :redis_key_ttl, # block is not given - locks are living in redis with their TTL
|
|
1071
1115
|
locks_released_at: nil, # (instrumentation) # block is not given - locks are living in redis with their TTL
|
|
1072
1116
|
locks_acq_time: 7.0, # (instrumentation) time spent to acquire all locks, milliseconds
|
|
1073
1117
|
locks_hold_time: nil, # (instrumentation) # block is not given - locks are living in redis with their TTL
|
|
@@ -1084,53 +1128,71 @@ detailed_result # =>
|
|
|
1084
1128
|
client.lock('a', ttl: 10_000_000)
|
|
1085
1129
|
|
|
1086
1130
|
# - try to bobtain series of locks with `a` inside the series (`a` can not be obtained for now)
|
|
1087
|
-
client.
|
|
1131
|
+
client.lock_series('c', 'a', 'b') # ... `a` is still obtained
|
|
1088
1132
|
# =>
|
|
1089
1133
|
{
|
|
1090
1134
|
ok: false,
|
|
1091
1135
|
result: {
|
|
1092
1136
|
error: :failed_to_acquire_lock_series,
|
|
1093
|
-
detailed_errors: { 'a' => { ok: false, result: :retry_limit_reached } },
|
|
1137
|
+
detailed_errors: { 'a' => { ok: false, result: :retry_limit_reached } }, # (empty without `failed_on_lock`)
|
|
1094
1138
|
lock_series: ["c", "a", "b"],
|
|
1095
|
-
acquired_locks: [
|
|
1096
|
-
missing_locks: ["a", "b"],
|
|
1097
|
-
failed_on_lock:
|
|
1139
|
+
acquired_locks: [], # always empty: nothing is obtained by the failed series
|
|
1140
|
+
missing_locks: ["c", "a", "b"],
|
|
1141
|
+
failed_on_lock: "a", # the first lock that blocked the last completed attempt (`nil`: unknown)
|
|
1098
1142
|
}
|
|
1099
1143
|
}
|
|
1100
1144
|
```
|
|
1101
1145
|
|
|
1102
|
-
######
|
|
1103
|
-
|
|
1104
|
-
- `rw_mode` is the mode of the series (
|
|
1146
|
+
###### Instrumentation events:
|
|
1147
|
+
|
|
1148
|
+
- `rw_mode` is the mode of the series (the same for all locks of the series);
|
|
1149
|
+
- `#lock` events (`lock_obtained`, `lock_obtaining_failed`, ...) are not raised for the locks of the series, each lock has its own series events
|
|
1150
|
+
with the series context in the name (`lock_key` is the lock, `lock_keys` are all locks of the series):
|
|
1151
|
+
- `lock_series_intermediate_lock_obtained` - intermediate obtaining: the lock passed all checks of an attempt of the series (the lock is
|
|
1152
|
+
free and the lock request of the series is next). Nothing is held yet: the series can still wait for its other locks or fail.
|
|
1153
|
+
Raised once per lock of the series (not per attempt), in the order of the lock names up to the lock that blocks the series;
|
|
1154
|
+
- `lock_series_lock_obtained` - full obtaining: the lock is really obtained, all locks of the series are held;
|
|
1155
|
+
- `lock_series_lock_obtaining_failed` - the lock that blocked the failed series, once per series (not raised when the blocking lock
|
|
1156
|
+
is unknown: no attempt found it, see `failed_lock_key` of `lock_series_obtaining_failed`);
|
|
1157
|
+
- reentrant locks are not obtained by the series: they have no lock events (see `reentrant_lock_keys` of the series events);
|
|
1105
1158
|
- see [Instrumentation Events](#instrumentation-events) for the payload details;
|
|
1106
1159
|
|
|
1107
1160
|
```ruby
|
|
1108
|
-
#
|
|
1109
|
-
"redis_queued_locks.
|
|
1110
|
-
"redis_queued_locks.
|
|
1161
|
+
# instrumentation events (examples)
|
|
1162
|
+
"redis_queued_locks.lock_series_intermediate_lock_obtained" => # {lock_key: "rql:lock:s", lock_keys: ["rql:lock:s", "rql:lock:t"], rw_mode: :write, priority: 0, ttl: 5000, acq_id: "rql:acq:4486/1696/1704/1712/e6fd0da7991e4303", hst_id: "rql:hst:4486/1696/1712/e6fd0da7991e4303", ts: 1769627106.4688692, acq_time: 2.61, instrument: nil}
|
|
1163
|
+
"redis_queued_locks.lock_series_lock_obtained" => # {lock_key: "rql:lock:s", lock_keys: ["rql:lock:s", "rql:lock:t"], rw_mode: :write, priority: 0, ttl: 5000, acq_id: "rql:acq:4486/1696/1704/1712/e6fd0da7991e4303", hst_id: "rql:hst:4486/1696/1712/e6fd0da7991e4303", ts: 1769627106.4688692, acq_time: 2.61, instrument: nil}
|
|
1164
|
+
"redis_queued_locks.lock_series_lock_obtaining_failed" => # {lock_key: "rql:lock:y", lock_keys: ["rql:lock:x", "rql:lock:y"], rw_mode: :write, priority: 0, ttl: 5000, acq_id: "rql:acq:4486/1696/1704/1712/e6fd0da7991e4303", hst_id: "rql:hst:4486/1696/1712/e6fd0da7991e4303", ts: 1769627106.4717052, acq_time: 4.26, result: :retry_limit_reached, instrument: nil}
|
|
1165
|
+
"redis_queued_locks.lock_series_obtained" => # {lock_keys: ["rql:lock:s", "rql:lock:t", "rql:lock:u"], rw_mode: :write, priority: 0, ttl: 5000, acq_id: "rql:acq:4486/1696/1704/1712/e6fd0da7991e4303", hst_id: "rql:hst:4486/1696/1712/e6fd0da7991e4303", ts: 1769627106.4688692, acq_time: 2.61, reentrant_lock_keys: [], instrument: nil}
|
|
1166
|
+
"redis_queued_locks.lock_series_hold_and_release" => # {lock_keys: ["rql:lock:x", "rql:lock:y", "rql:lock:z"], rw_mode: :write, priority: 0, hold_time: 0.29, ttl: 5000, acq_id: "rql:acq:4486/1696/1704/1712/e6fd0da7991e4303", hst_id: "rql:hst:4486/1696/1712/e6fd0da7991e4303", ts: 1769627106.4717052, acq_time: 4.26, reentrant_lock_keys: [], instrument: nil}
|
|
1167
|
+
"redis_queued_locks.lock_series_obtaining_failed" => # {lock_keys: ["rql:lock:x", "rql:lock:y"], rw_mode: :write, priority: 0, ttl: 5000, acq_id: "rql:acq:4486/1696/1704/1712/e6fd0da7991e4303", hst_id: "rql:hst:4486/1696/1712/e6fd0da7991e4303", ts: 1769627106.4717052, acq_time: 4.26, result: :retry_limit_reached, failed_lock_key: "rql:lock:y", instrument: nil}
|
|
1111
1168
|
```
|
|
1112
1169
|
|
|
1113
|
-
######
|
|
1170
|
+
###### Logs:
|
|
1114
1171
|
|
|
1115
|
-
- `rw_mode` is the mode of the series (
|
|
1172
|
+
- `rw_mode` is the mode of the series (the same for all locks of the series);
|
|
1116
1173
|
- see [Logging](#logging) for the logged keys;
|
|
1117
1174
|
|
|
1118
1175
|
```shell
|
|
1119
|
-
#
|
|
1176
|
+
# logs (examples)
|
|
1120
1177
|
[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.
|
|
1122
|
-
[redis_queued_locks.
|
|
1178
|
+
[redis_queued_locks.start_try_to_lock_series_cycle] 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
|
|
1179
|
+
[redis_queued_locks.lock_series_intermediate_lock_obtained] lock_key => 'rql:lock:a' 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)
|
|
1180
|
+
[redis_queued_locks.lock_series_lock_obtained] lock_key => 'rql:lock:a' 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)
|
|
1181
|
+
[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) reentrant_lock_keys => '[]'
|
|
1182
|
+
[redis_queued_locks.expire_lock_series] 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
|
|
1123
1183
|
```
|
|
1124
1184
|
|
|
1125
1185
|
----
|
|
1126
1186
|
|
|
1127
|
-
#### #lock_series! -
|
|
1187
|
+
#### #lock_series! - exceptional `lock_series`
|
|
1128
1188
|
|
|
1129
1189
|
<sup>\[[back to top](#usage)\]</sup>
|
|
1130
1190
|
|
|
1131
|
-
|
|
1191
|
+
Raises the same errors as `#lock!` when the series can not be obtained (`RedisQueuedLocks::LockAlreadyObtainedError`,
|
|
1192
|
+
`RedisQueuedLocks::ConflictLockObtainError`, `RedisQueuedLocks::LockAcquirementRetryLimitError`,
|
|
1193
|
+
`RedisQueuedLocks::LockAcquirementTimeoutError`).
|
|
1132
1194
|
|
|
1133
|
-
For more details see `lock_series` [docs](#lock_series---
|
|
1195
|
+
For more details see `lock_series` [docs](#lock_series---acquire-a-series-of-locks)
|
|
1134
1196
|
|
|
1135
1197
|
```ruby
|
|
1136
1198
|
client.lock_series!("x", "y", "z") { ...some_code... }
|
|
@@ -1229,18 +1291,18 @@ rql.lock_info("your_lock_name")
|
|
|
1229
1291
|
|
|
1230
1292
|
```ruby
|
|
1231
1293
|
# <for read/write locks>
|
|
1232
|
-
# (see `
|
|
1294
|
+
# (see `rw_mode:` kwarg attribute of #lock/#lock! methods and "Read/Write Locks" documentation section)
|
|
1233
1295
|
# (lock requests are not included: use #queue_info to see the queued readers and writers)
|
|
1234
1296
|
|
|
1235
1297
|
# -> case 1: the lock is held by readers, writers and readers are waiting in the lock queues;
|
|
1236
1298
|
|
|
1237
1299
|
# process#1 and process#2 (readers, work in parallel):
|
|
1238
|
-
rql.lock("your_lock_name",
|
|
1239
|
-
rql.lock("your_lock_name",
|
|
1300
|
+
rql.lock("your_lock_name", rw_mode: :read, ttl: 5_000, meta: { "kek" => "pek" }) { ... }
|
|
1301
|
+
rql.lock("your_lock_name", rw_mode: :read, ttl: 3_000) { ... }
|
|
1240
1302
|
# process#3 (writer, waits for all readers in the write lock queue):
|
|
1241
|
-
rql.lock("your_lock_name",
|
|
1303
|
+
rql.lock("your_lock_name", rw_mode: :write) { ... }
|
|
1242
1304
|
# process#4 (reader, waits for the earlier writer in the read lock queue):
|
|
1243
|
-
rql.lock("your_lock_name",
|
|
1305
|
+
rql.lock("your_lock_name", rw_mode: :read) { ... }
|
|
1244
1306
|
|
|
1245
1307
|
rql.lock_info("your_lock_name")
|
|
1246
1308
|
|
|
@@ -1445,7 +1507,7 @@ rql.unlock("your_lock_name")
|
|
|
1445
1507
|
|
|
1446
1508
|
- releases the read lock of the current acquirer only (see [Read/Write Locks](#readwrite-locks)):
|
|
1447
1509
|
other read locks, the write lock and lock requests are not affected (`#unlock` releases the lock entirely);
|
|
1448
|
-
- suitable for read locks obtained without a block of code (`lock(...,
|
|
1510
|
+
- suitable for read locks obtained without a block of code (`lock(..., rw_mode: :read)` without a block);
|
|
1449
1511
|
- the current acquirer is calculated in the same way as for `#lock` (`#current_acquirer_id`: process, thread, fiber, ractor, identity),
|
|
1450
1512
|
so call it from the same thread/fiber that obtained the read lock;
|
|
1451
1513
|
- the read lock of a different acquirer can be released via `:acquirer_id` (for example, extracted from `#lock_info`/`#locks_info`);
|
|
@@ -1463,7 +1525,7 @@ rql.unlock("your_lock_name")
|
|
|
1463
1525
|
- raises the `"redis_queued_locks.explicit_read_lock_release"` instrumentation event;
|
|
1464
1526
|
|
|
1465
1527
|
```ruby
|
|
1466
|
-
rql.lock('report',
|
|
1528
|
+
rql.lock('report', rw_mode: :read, ttl: 30_000) # => obtained without a block
|
|
1467
1529
|
# ...
|
|
1468
1530
|
rql.unlock_read('report')
|
|
1469
1531
|
# =>
|
|
@@ -1779,7 +1841,7 @@ rql.release_locks_of(host_id: rql.current_host_id, acquirer_id: rql.current_acqu
|
|
|
1779
1841
|
- `milliseconds` - (required) `[Integer]`
|
|
1780
1842
|
- how many milliseconds should be added to the lock's TTL;
|
|
1781
1843
|
- should be a positive `Integer`;
|
|
1782
|
-
- `:
|
|
1844
|
+
- `:rw_mode` - (optional) `[Symbol]`
|
|
1783
1845
|
- `:write` (default) - extends the write lock (the behavior described below);
|
|
1784
1846
|
- `:read` - extends the read lock of the current acquirer only (see [Read/Write Locks](#readwrite-locks)):
|
|
1785
1847
|
- the read lock expiration, the read locks registry TTL and the read lock data TTL are extended;
|
|
@@ -2774,19 +2836,19 @@ rql.lock("user_#{user.id}", priority: :high) { update_profile } # priority: 50
|
|
|
2774
2836
|
|
|
2775
2837
|
<sup>\[[back to top](#table-of-contents)\]</sup>
|
|
2776
2838
|
|
|
2777
|
-
- `
|
|
2839
|
+
- `rw_mode:` attribute of `#lock` and `#lock!`:
|
|
2778
2840
|
- `:write` (**default**) - exclusive lock (the classic RQL lock): waits for the write lock and for all read locks;
|
|
2779
2841
|
- `:read` - shared lock: waits for the write lock only (read locks of different acquirers work in parallel);
|
|
2780
2842
|
|
|
2781
2843
|
```ruby
|
|
2782
2844
|
# readers work in parallel
|
|
2783
|
-
rql.lock('report',
|
|
2845
|
+
rql.lock('report', rw_mode: :read) { read_report }
|
|
2784
2846
|
|
|
2785
2847
|
# writer waits for all readers (and readers wait for the writer)
|
|
2786
|
-
rql.lock('report',
|
|
2848
|
+
rql.lock('report', rw_mode: :write) { rebuild_report }
|
|
2787
2849
|
|
|
2788
2850
|
# write mode is default:
|
|
2789
|
-
rql.lock('report') { rebuild_report } #
|
|
2851
|
+
rql.lock('report') { rebuild_report } # rw_mode: :write by default
|
|
2790
2852
|
```
|
|
2791
2853
|
|
|
2792
2854
|
- lock request ordering (`access_strategy`):
|
|
@@ -2807,11 +2869,11 @@ rql.lock('report') { rebuild_report } # read_write_mode: :write by default
|
|
|
2807
2869
|
- `#unlock` releases the lock entirely: the write lock, all read locks (with their data) and all lock requests;
|
|
2808
2870
|
- `#unlock_read` (aka `#release_read_lock`) releases the read lock of the current acquirer
|
|
2809
2871
|
(suitable for read locks obtained without a block), or the read lock of the acquirer passed via `acquirer_id:`;
|
|
2810
|
-
- `#extend_lock_ttl(lock_name, milliseconds,
|
|
2872
|
+
- `#extend_lock_ttl(lock_name, milliseconds, rw_mode: :read)` extends the read lock of the current acquirer only;
|
|
2811
2873
|
`all_read_locks: true` extends all live read locks of the lock;
|
|
2812
|
-
- `#lock_series` with `
|
|
2874
|
+
- `#lock_series` with `rw_mode: :read` obtains a series of read locks (the mode is the same for all locks of the series):
|
|
2813
2875
|
- read lock series of different acquirers work in parallel, writers wait for them;
|
|
2814
|
-
- after the block
|
|
2876
|
+
- after the block only the read locks of the current acquirer are released;
|
|
2815
2877
|
- reentrant read locks of the series (already obtained by the outer logic of the same acquirer) are not released by the series;
|
|
2816
2878
|
- reentrant locks (the same acquirer, see [Deadlocks and Reentrant locks](#deadlocks-and-reentrant-locks)):
|
|
2817
2879
|
- read lock request under the obtained read lock or write lock is processed as a reentrant lock of the obtained lock
|
|
@@ -2931,12 +2993,21 @@ rql.lock('report') { rebuild_report } # read_write_mode: :write by default
|
|
|
2931
2993
|
"[redis_queued_locks.try_lock.obtain__free_to_acquire]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2932
2994
|
```
|
|
2933
2995
|
|
|
2934
|
-
- lock series logs (raised from `#lock_series`/`#lock_series
|
|
2996
|
+
- lock series logs (raised from `#lock_series`/`#lock_series!`; `rw_mode` is the mode of the series; with `log_lock_try` each
|
|
2997
|
+
attempt of the series writes the `try_lock.*` logs of the list above with its own `lock_key`, one log per lock of the series):
|
|
2935
2998
|
|
|
2936
2999
|
```ruby
|
|
2937
3000
|
"[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.
|
|
3001
|
+
"[redis_queued_locks.start_try_to_lock_series_cycle]" # (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
3002
|
+
"[redis_queued_locks.dead_score_reached__reset_series_position]" # (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
3003
|
+
"[redis_queued_locks.lock_series_intermediate_lock_obtained]" # (logs "lock_key", "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time");
|
|
3004
|
+
"[redis_queued_locks.lock_series_lock_obtained]" # (logs "lock_key", "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time");
|
|
3005
|
+
"[redis_queued_locks.lock_series_lock_obtaining_failed]" # (logs "lock_key", "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time", "result");
|
|
3006
|
+
"[redis_queued_locks.lock_series_obtained]" # (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time", "reentrant_lock_keys");
|
|
3007
|
+
"[redis_queued_locks.lock_series_obtaining_failed]" # (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time", "result", "failed_lock_key");
|
|
3008
|
+
"[redis_queued_locks.dequeue_from_lock_series_queues]" # (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
2939
3009
|
"[redis_queued_locks.expire_lock_series]" # (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
3010
|
+
"[redis_queued_locks.decrease_lock_series]" # (logs "lock_keys", "decreased_ttl", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
2940
3011
|
```
|
|
2941
3012
|
|
|
2942
3013
|
- operation logs (one log line per operation that changes Redis keys, with the time of the whole operation; the same data as
|
|
@@ -2988,8 +3059,16 @@ rql.lock('report') { rebuild_report } # read_write_mode: :write by default
|
|
|
2988
3059
|
# - "[redis_queued_locks.expire_lock]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2989
3060
|
# - "[redis_queued_locks.decrease_lock]" (logs "lock_key", "decreased_ttl", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2990
3061
|
# - "[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.
|
|
3062
|
+
# - "[redis_queued_locks.start_try_to_lock_series_cycle]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
3063
|
+
# - "[redis_queued_locks.dead_score_reached__reset_series_position]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
3064
|
+
# - "[redis_queued_locks.lock_series_intermediate_lock_obtained]" (logs "lock_key", "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time");
|
|
3065
|
+
# - "[redis_queued_locks.lock_series_lock_obtained]" (logs "lock_key", "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time");
|
|
3066
|
+
# - "[redis_queued_locks.lock_series_lock_obtaining_failed]" (logs "lock_key", "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time", "result");
|
|
3067
|
+
# - "[redis_queued_locks.lock_series_obtained]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time", "reentrant_lock_keys");
|
|
3068
|
+
# - "[redis_queued_locks.lock_series_obtaining_failed]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority", "acq_time", "result", "failed_lock_key");
|
|
3069
|
+
# - "[redis_queued_locks.dequeue_from_lock_series_queues]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
2992
3070
|
# - "[redis_queued_locks.expire_lock_series]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
3071
|
+
# - "[redis_queued_locks.decrease_lock_series]" (logs "lock_keys", "decreased_ttl", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "priority");
|
|
2993
3072
|
# - "[redis_queued_locks.explicit_lock_release]" (logs "lock_key", "lock_key_queue", "rel_time", "at");
|
|
2994
3073
|
# - "[redis_queued_locks.explicit_read_lock_release]" (logs "lock_key", "acq_id", "lock_res", "rel_time", "at");
|
|
2995
3074
|
# - "[redis_queued_locks.explicit_all_locks_release]" (logs "at", "rel_time", "rel_key_cnt");
|
|
@@ -3131,8 +3210,12 @@ List of instrumentation events
|
|
|
3131
3210
|
- ["redis_queued_locks.reentrant_lock_obtained"](#redis_queued_locksreentrant_lock_obtained)
|
|
3132
3211
|
- ["redis_queued_locks.lock_hold_and_release"](#redis_queued_lockslock_hold_and_release)
|
|
3133
3212
|
- ["redis_queued_locks.reentrant_lock_hold_completes"](#redis_queued_locksreentrant_lock_hold_completes)
|
|
3213
|
+
- ["redis_queued_locks.lock_series_intermediate_lock_obtained"](#redis_queued_lockslock_series_intermediate_lock_obtained)
|
|
3214
|
+
- ["redis_queued_locks.lock_series_lock_obtained"](#redis_queued_lockslock_series_lock_obtained)
|
|
3215
|
+
- ["redis_queued_locks.lock_series_lock_obtaining_failed"](#redis_queued_lockslock_series_lock_obtaining_failed)
|
|
3134
3216
|
- ["redis_queued_locks.lock_series_obtained"](#redis_queued_lockslock_series_obtained)
|
|
3135
3217
|
- ["redis_queued_locks.lock_series_hold_and_release"](#redis_queued_lockslock_series_hold_and_release)
|
|
3218
|
+
- ["redis_queued_locks.lock_series_obtaining_failed"](#redis_queued_lockslock_series_obtaining_failed)
|
|
3136
3219
|
- ["redis_queued_locks.explicit_lock_release"](#redis_queued_locksexplicit_lock_release)
|
|
3137
3220
|
- ["redis_queued_locks.explicit_read_lock_release"](#redis_queued_locksexplicit_read_lock_release)
|
|
3138
3221
|
- ["redis_queued_locks.explicit_all_locks_release"](#redis_queued_locksexplicit_all_locks_release)
|
|
@@ -3142,8 +3225,8 @@ List of instrumentation events
|
|
|
3142
3225
|
- ["redis_queued_locks.reindex_locks"](#redis_queued_locksreindex_locks)
|
|
3143
3226
|
|
|
3144
3227
|
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
|
|
3146
|
-
not obtained, held and released);
|
|
3228
|
+
- the lock acquirement (`#lock`/`#lock!`, `#lock_series`/`#lock_series!`): each meaningful outcome (the lock or the whole
|
|
3229
|
+
series is obtained, not obtained, held and released);
|
|
3147
3230
|
- each operation that changes Redis keys (`#unlock`, `#unlock_read`, `#clear_locks`, `#clear_locks_of`/`#clear_current_locks`,
|
|
3148
3231
|
`#extend_lock_ttl`, `#clear_dead_requests`, `#reindex_locks`): one event with the time of the whole operation;
|
|
3149
3232
|
- lock information queries (`#lock_info`, `#locks`, `#queues`, `#keys`, ...) are not instrumented;
|
|
@@ -3169,8 +3252,7 @@ Detalized event semantics and payload structure:
|
|
|
3169
3252
|
##### `"redis_queued_locks.lock_obtaining_failed"`
|
|
3170
3253
|
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
3171
3254
|
- 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)
|
|
3173
|
-
(for the lock of the series that is not obtained);
|
|
3255
|
+
- raised from `#lock`/`#lock!` (with `raise_errors: true` - before the error is raised);
|
|
3174
3256
|
- payload:
|
|
3175
3257
|
- `:lock_key` - `string` - lock name;
|
|
3176
3258
|
- `:rw_mode` - `symbol` - requested lock mode: `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
@@ -3250,36 +3332,110 @@ Detalized event semantics and payload structure:
|
|
|
3250
3332
|
- `:acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
|
|
3251
3333
|
- `:instrument` - `nil`/`Any` - custom data passed to the `#lock`/`#lock!` method as `:instrument` attribute;
|
|
3252
3334
|
|
|
3335
|
+
##### `"redis_queued_locks.lock_series_intermediate_lock_obtained"`
|
|
3336
|
+
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
3337
|
+
- intermediate obtaining: a moment when the lock of the series passed all checks of an attempt of the series (the lock is free and the lock
|
|
3338
|
+
request of the series is next). Nothing is held yet: the series can still wait for its other locks or fail;
|
|
3339
|
+
- raised from `#lock_series`/`#lock_series!` once per lock of the series (not per attempt), in the order of the lock names up to the lock
|
|
3340
|
+
that blocks the series; reentrant locks of the series are not reported;
|
|
3341
|
+
- payload:
|
|
3342
|
+
- `:lock_key` - `string` - the lock of the series;
|
|
3343
|
+
- `:lock_keys` - `array<string>` - all locks of the series;
|
|
3344
|
+
- `:rw_mode` - `symbol` - the mode of the series: `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
3345
|
+
- `: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));
|
|
3346
|
+
- `:ttl` - `integer`/`milliseconds` - the ttl of each lock of the series;
|
|
3347
|
+
- `:acq_id` - `string` - lock acquirer identifier;
|
|
3348
|
+
- `:hst_id` - `string` - lock's host identifier;
|
|
3349
|
+
- `:ts` - `float`/`epoch` - the time of the event;
|
|
3350
|
+
- `:acq_time` - `float`/`milliseconds` - time spent on the series acquiring up to this moment;
|
|
3351
|
+
- `:instrument` - `nil`/`Any` - custom data passed to the `#lock_series`/`#lock_series!` method as `:instrument` attribute;
|
|
3352
|
+
|
|
3353
|
+
##### `"redis_queued_locks.lock_series_lock_obtained"`
|
|
3354
|
+
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
3355
|
+
- full obtaining: a moment when the lock of the series was really obtained (all locks of the series are held);
|
|
3356
|
+
- raised from `#lock_series`/`#lock_series!` for each lock obtained by the series, right before `lock_series_obtained`; reentrant locks of
|
|
3357
|
+
the series are not reported (see `:reentrant_lock_keys` of `lock_series_obtained`);
|
|
3358
|
+
- payload:
|
|
3359
|
+
- `:lock_key` - `string` - the lock of the series;
|
|
3360
|
+
- `:lock_keys` - `array<string>` - all locks of the series;
|
|
3361
|
+
- `:rw_mode` - `symbol` - the mode of the series: `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
3362
|
+
- `: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));
|
|
3363
|
+
- `:ttl` - `integer`/`milliseconds` - the ttl of each lock of the series;
|
|
3364
|
+
- `:acq_id` - `string` - lock acquirer identifier;
|
|
3365
|
+
- `:hst_id` - `string` - lock's host identifier;
|
|
3366
|
+
- `:ts` - `float`/`epoch` - the time when the series was obtained;
|
|
3367
|
+
- `:acq_time` - `float`/`milliseconds` - time spent on the series acquiring;
|
|
3368
|
+
- `:instrument` - `nil`/`Any` - custom data passed to the `#lock_series`/`#lock_series!` method as `:instrument` attribute;
|
|
3369
|
+
|
|
3370
|
+
##### `"redis_queued_locks.lock_series_lock_obtaining_failed"`
|
|
3371
|
+
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
3372
|
+
- a moment when the series acquirement is failed: the lock of the series that blocked it;
|
|
3373
|
+
- raised from `#lock_series`/`#lock_series!` once per failed series (with `raise_errors: true` - before the error is raised), right before
|
|
3374
|
+
`lock_series_obtaining_failed`; it is not raised when the blocking lock is unknown (no attempt found it);
|
|
3375
|
+
- payload:
|
|
3376
|
+
- `:lock_key` - `string` - the lock that blocked the series;
|
|
3377
|
+
- `:lock_keys` - `array<string>` - all locks of the series;
|
|
3378
|
+
- `:rw_mode` - `symbol` - the mode of the series: `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
3379
|
+
- `: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));
|
|
3380
|
+
- `:ttl` - `integer`/`milliseconds` - the ttl of each lock of the series;
|
|
3381
|
+
- `:acq_id` - `string` - lock acquirer identifier;
|
|
3382
|
+
- `:hst_id` - `string` - lock's host identifier;
|
|
3383
|
+
- `:ts` - `float`/`epoch` - the time when the series acquirement was failed;
|
|
3384
|
+
- `:acq_time` - `float`/`milliseconds` - time spent on the series acquiring;
|
|
3385
|
+
- `:result` - `symbol` - the failure result (see `lock_series_obtaining_failed`);
|
|
3386
|
+
- `:instrument` - `nil`/`Any` - custom data passed to the `#lock_series`/`#lock_series!` method as `:instrument` attribute;
|
|
3387
|
+
|
|
3253
3388
|
##### `"redis_queued_locks.lock_series_obtained"`
|
|
3254
3389
|
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
3255
|
-
-
|
|
3390
|
+
- a moment when all locks of the series were obtained;
|
|
3256
3391
|
- raised from `#lock_series`/`#lock_series!`;
|
|
3257
3392
|
- payload:
|
|
3258
3393
|
- `:lock_keys` - `array<string>` - lock names of the series;
|
|
3259
|
-
- `:rw_mode` - `symbol` - the mode of the series
|
|
3394
|
+
- `:rw_mode` - `symbol` - the mode of the series: `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
3260
3395
|
- `: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));
|
|
3261
|
-
- `:ttl` - `integer`/`milliseconds` - the
|
|
3262
|
-
see [lock_series](#lock_series---poc-acquire-a-series-of-locks));
|
|
3396
|
+
- `:ttl` - `integer`/`milliseconds` - the ttl of each lock of the series (see [lock_series](#lock_series---acquire-a-series-of-locks));
|
|
3263
3397
|
- `:acq_id` - `string` - lock acquirer identifier;
|
|
3264
3398
|
- `:hst_id` - `string` - lock's host identifier;
|
|
3265
3399
|
- `:ts` - `numeric`/`epoch` - the time when the series was obtained;
|
|
3266
3400
|
- `:acq_time` - `float`/`milliseconds` - time spent on the series acquiring;
|
|
3401
|
+
- `:reentrant_lock_keys` - `array<string>` - the locks of the series that are obtained by your outer logic (reentrant locks:
|
|
3402
|
+
they are not obtained and not released by the series);
|
|
3267
3403
|
- `:instrument` - `nil`/`Any` - custom data passed to the `#lock_series`/`#lock_series!` method as `:instrument` attribute;
|
|
3268
3404
|
|
|
3269
3405
|
##### `"redis_queued_locks.lock_series_hold_and_release"`
|
|
3270
3406
|
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
3271
|
-
-
|
|
3407
|
+
- an event signalizes about the "hold+and+release" process of the series is finished;
|
|
3272
3408
|
- raised from `#lock_series`/`#lock_series!` when invoked with a block of code;
|
|
3273
3409
|
- payload:
|
|
3274
3410
|
- `:lock_keys` - `array<string>` - lock names of the series;
|
|
3275
|
-
- `:rw_mode` - `symbol` - the mode of the series
|
|
3411
|
+
- `:rw_mode` - `symbol` - the mode of the series: `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
3276
3412
|
- `: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));
|
|
3277
3413
|
- `:hold_time` - `float`/`milliseconds` - the series hold time;
|
|
3278
|
-
- `:ttl` - `integer`/`milliseconds` - the
|
|
3414
|
+
- `:ttl` - `integer`/`milliseconds` - the ttl of each lock of the series;
|
|
3279
3415
|
- `:acq_id` - `string` - lock acquirer identifier;
|
|
3280
3416
|
- `:hst_id` - `string` - lock's host identifier;
|
|
3281
3417
|
- `:ts` - `float`/`epoch` - the time when the series was released;
|
|
3282
3418
|
- `:acq_time` - `float`/`milliseconds` - time spent on the series acquiring;
|
|
3419
|
+
- `:reentrant_lock_keys` - `array<string>` - the locks of the series that are obtained by your outer logic (reentrant locks:
|
|
3420
|
+
they are not obtained and not released by the series);
|
|
3421
|
+
- `:instrument` - `nil`/`Any` - custom data passed to the `#lock_series`/`#lock_series!` method as `:instrument` attribute;
|
|
3422
|
+
|
|
3423
|
+
##### `"redis_queued_locks.lock_series_obtaining_failed"`
|
|
3424
|
+
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
3425
|
+
- a moment when the series acquirement is failed (no lock of the series is obtained);
|
|
3426
|
+
- raised from `#lock_series`/`#lock_series!` (with `raise_errors: true` - before the error is raised);
|
|
3427
|
+
- payload:
|
|
3428
|
+
- `:lock_keys` - `array<string>` - lock names of the series;
|
|
3429
|
+
- `:rw_mode` - `symbol` - the mode of the series: `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
3430
|
+
- `: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));
|
|
3431
|
+
- `:ttl` - `integer`/`milliseconds` - the ttl of each lock of the series;
|
|
3432
|
+
- `:acq_id` - `string` - lock acquirer identifier;
|
|
3433
|
+
- `:hst_id` - `string` - lock's host identifier;
|
|
3434
|
+
- `:ts` - `float`/`epoch` - the time when the series acquirement was failed;
|
|
3435
|
+
- `:acq_time` - `float`/`milliseconds` - time spent on the series acquiring;
|
|
3436
|
+
- `:result` - `symbol` - the failure result: `:timeout_reached`, `:retry_limit_reached`, `:fail_fast_no_try`, `:fail_fast_after_try`, `:conflict_dead_lock`, `:conflict_lock_upgrade`;
|
|
3437
|
+
- `:failed_lock_key` - `string`/`nil` - the first lock that blocked the last completed attempt of the series (`nil`: unknown:
|
|
3438
|
+
no attempt was completed or some lock state was changed during the attempt);
|
|
3283
3439
|
- `:instrument` - `nil`/`Any` - custom data passed to the `#lock_series`/`#lock_series!` method as `:instrument` attribute;
|
|
3284
3440
|
|
|
3285
3441
|
##### `"redis_queued_locks.explicit_lock_release"`
|
|
@@ -3382,11 +3538,6 @@ Detalized event semantics and payload structure:
|
|
|
3382
3538
|
the global "timeout request" queue and managed by a single global "timeout wathcer" thread). it should prevent any logic and timeout intersections,
|
|
3383
3539
|
some GVL-related things and problem situations when the global watcher thread is "dead";
|
|
3384
3540
|
- **strict redlock algorithm support** (support for many `RedisClient` instances that are fully independent (distributed redis instances));
|
|
3385
|
-
- #lock_series` - acquire a series of locks:
|
|
3386
|
-
- (NOTE: "Proof of Concept" implementation is ready to use);
|
|
3387
|
-
```ruby
|
|
3388
|
-
rql.lock_series('lock_a', 'lock_b', 'lock_c') { puts 'locked' }
|
|
3389
|
-
```
|
|
3390
3541
|
- an ability to release all locks and all requests of the concrete acquirer id or host id (or both in validation-orianted combination);
|
|
3391
3542
|
- detailed lock informotion inside the error object in cases of exceptions (at the moment we have this info inside the error message only that hard to analyze in work);
|
|
3392
3543
|
- a convenient way to mark any `lock` invocation as "non-instrumentable" / "non-loggable" (as an alternative to `VoidNotifier` and to `VoidLogger`);
|
|
@@ -3435,7 +3586,7 @@ Detalized event semantics and payload structure:
|
|
|
3435
3586
|
- add a library-level exception, when RQL-related key in Redis (required for its logic) has incompatible type (means: some other program uses our key with their own type and logic and RQL can't work properly);
|
|
3436
3587
|
- yardoc docs with CI check (full doc coverage check);
|
|
3437
3588
|
- split *exception* inheritance to the groups: `lock obtaining errors`, `block invocation errors`, `swarm errors`, and other groups (research possible groups):
|
|
3438
|
-
- in some cases we need to intercept "Lock Obtaining Process Erros", in other cases: "Block Invocation Errors", and so on
|
|
3589
|
+
- in some cases we need to intercept "Lock Obtaining Process Erros", in other cases: "Block Invocation Errors", and so on;
|
|
3439
3590
|
- think about `#debounce` and `#throttle` mechanisms;
|
|
3440
3591
|
|
|
3441
3592
|
---
|