redis_queued_locks 1.17.0 → 1.19.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 +87 -10
- data/.claude/rules/acquirer.md +31 -11
- data/.claude/rules/arguments.md +2 -1
- data/.claude/rules/git.md +46 -0
- data/.claude/rules/logic.md +9 -3
- data/.claude/rules/swarm.md +4 -3
- data/.claude/rules/tests.md +9 -3
- data/.claude/rules/type-checking.md +10 -2
- data/.claude/rules/visitors.md +12 -7
- data/CHANGELOG.md +68 -0
- data/CLAUDE.md +7 -2
- data/README.md +427 -79
- data/lib/redis_queued_locks/acquirer/acquire_lock/dequeue_from_lock_queue/log_visitor.rb +6 -3
- data/lib/redis_queued_locks/acquirer/acquire_lock/dequeue_from_lock_queue.rb +9 -7
- data/lib/redis_queued_locks/acquirer/acquire_lock/instr_visitor.rb +20 -10
- data/lib/redis_queued_locks/acquirer/acquire_lock/log_visitor.rb +34 -16
- data/lib/redis_queued_locks/acquirer/acquire_lock/try_to_lock/log_visitor.rb +240 -28
- data/lib/redis_queued_locks/acquirer/acquire_lock/try_to_lock.rb +398 -129
- data/lib/redis_queued_locks/acquirer/acquire_lock/yield_expire/log_visitor.rb +12 -6
- data/lib/redis_queued_locks/acquirer/acquire_lock/yield_expire.rb +32 -8
- data/lib/redis_queued_locks/acquirer/acquire_lock.rb +73 -20
- data/lib/redis_queued_locks/acquirer/clear_dead_requests.rb +9 -1
- data/lib/redis_queued_locks/acquirer/extend_lock_ttl.rb +182 -3
- data/lib/redis_queued_locks/acquirer/is_locked.rb +19 -2
- data/lib/redis_queued_locks/acquirer/is_queued.rb +6 -2
- data/lib/redis_queued_locks/acquirer/lock_info.rb +96 -6
- data/lib/redis_queued_locks/acquirer/lock_series_poc/instr_visitor.rb +8 -2
- data/lib/redis_queued_locks/acquirer/lock_series_poc/log_visitor.rb +18 -6
- data/lib/redis_queued_locks/acquirer/lock_series_poc.rb +126 -27
- data/lib/redis_queued_locks/acquirer/locks.rb +103 -6
- data/lib/redis_queued_locks/acquirer/queue_info.rb +38 -9
- data/lib/redis_queued_locks/acquirer/queues.rb +28 -4
- data/lib/redis_queued_locks/acquirer/release_all_locks.rb +35 -1
- data/lib/redis_queued_locks/acquirer/release_lock.rb +40 -9
- data/lib/redis_queued_locks/acquirer/release_locks_of.rb +43 -3
- data/lib/redis_queued_locks/acquirer/release_read_lock.rb +143 -0
- data/lib/redis_queued_locks/acquirer.rb +1 -0
- data/lib/redis_queued_locks/client.rb +143 -14
- data/lib/redis_queued_locks/resource.rb +117 -9
- data/lib/redis_queued_locks/swarm/flush_zombies.rb +38 -0
- data/lib/redis_queued_locks/swarm/zombie_info.rb +42 -0
- data/lib/redis_queued_locks/version.rb +2 -2
- data/rbs_collection.lock.yaml +10 -2
- data/rbs_collection.yaml +0 -2
- data/sig/redis_queued_locks/acquirer/acquire_lock/dequeue_from_lock_queue/log_visitor.rbs +2 -1
- data/sig/redis_queued_locks/acquirer/acquire_lock/dequeue_from_lock_queue.rbs +0 -1
- data/sig/redis_queued_locks/acquirer/acquire_lock/instr_visitor.rbs +5 -0
- data/sig/redis_queued_locks/acquirer/acquire_lock/log_visitor.rbs +12 -6
- data/sig/redis_queued_locks/acquirer/acquire_lock/try_to_lock/log_visitor.rbs +74 -7
- data/sig/redis_queued_locks/acquirer/acquire_lock/try_to_lock.rbs +4 -2
- data/sig/redis_queued_locks/acquirer/acquire_lock/yield_expire.rbs +3 -0
- data/sig/redis_queued_locks/acquirer/acquire_lock/yield_with_expire/log_visitor.rbs +4 -2
- data/sig/redis_queued_locks/acquirer/extend_lock_ttl.rbs +19 -1
- data/sig/redis_queued_locks/acquirer/lock_info.rbs +9 -1
- data/sig/redis_queued_locks/acquirer/locks.rbs +10 -1
- data/sig/redis_queued_locks/acquirer/queue_info.rbs +2 -4
- data/sig/redis_queued_locks/acquirer/queues.rbs +1 -1
- data/sig/redis_queued_locks/acquirer/release_lock.rbs +8 -1
- data/sig/redis_queued_locks/acquirer/release_read_lock.rbs +44 -0
- data/sig/redis_queued_locks/client.rbs +23 -2
- data/sig/redis_queued_locks/resource.rbs +9 -2
- data/sig/redis_queued_locks/swarm/zombie_info.rbs +5 -0
- metadata +4 -1
data/README.md
CHANGED
|
@@ -8,7 +8,9 @@ Each lock request is put into the request queue (each lock is hosted by its own
|
|
|
8
8
|
|
|
9
9
|
In addition to the classic `queued` (FIFO) strategy RQL supports `random` (RANDOM) lock obtaining strategy when any acquirer from the lock queue can obtain the lock regardless the position in the queue.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Supports `Read`/`Write` locks and `Reentrant` locks.
|
|
12
|
+
|
|
13
|
+
Provides flexible invocation flow, parametrized limits (lock request ttl, lock ttl, queue ttl, lock attempts limit, fast failing, etc), **zombie locks elimination**, logging and instrumentation (and much more).
|
|
12
14
|
|
|
13
15
|
---
|
|
14
16
|
|
|
@@ -34,6 +36,7 @@ Provides flexible invocation flow, parametrized limits (lock request ttl, lock t
|
|
|
34
36
|
- [locked?](#locked)
|
|
35
37
|
- [queued?](#queued)
|
|
36
38
|
- [unlock](#unlock---release-a-lock) (aka `release_lock`)
|
|
39
|
+
- [unlock_read](#unlock_read---release-the-read-lock-of-the-current-acquirer) (aka `release_read_lock`)
|
|
37
40
|
- [clear_locks](#clear_locks---release-all-locks-and-lock-queues) (aka `release_locks`)
|
|
38
41
|
- [clear_locks_of](#clear_locks_of) (aka `release_locks_of`)
|
|
39
42
|
- [clear_current_locks](#clear_current_locks) (aka `release_current_locks`)
|
|
@@ -64,18 +67,22 @@ Provides flexible invocation flow, parametrized limits (lock request ttl, lock t
|
|
|
64
67
|
- [Lock Access Strategies](#lock-access-strategies)
|
|
65
68
|
- [queued](#lock-access-strategies)
|
|
66
69
|
- [random](#lock-access-strategies)
|
|
70
|
+
- [Read/Write Locks](#readwrite-locks)
|
|
67
71
|
- [Deadlocks and Reentrant locks](#deadlocks-and-reentrant-locks)
|
|
68
72
|
- [Logging](#logging)
|
|
69
73
|
- [Logging Configuration](#logging-configuration)
|
|
70
74
|
- [Instrumentation](#instrumentation)
|
|
71
75
|
- [Instrumentation Configuration](#instrumentation-configuration)
|
|
72
76
|
- [Instrumentation Events](#instrumentation-events)
|
|
73
|
-
- ["redis_queued_locks.lock_obtained"](#
|
|
77
|
+
- ["redis_queued_locks.lock_obtained"](#redis_queued_lockslock_obtained)
|
|
74
78
|
- ["redis_queued_locks.extendable_reentrant_lock_obtained"](#redis_queued_locksextendable_reentrant_lock_obtained)
|
|
75
79
|
- ["redis_queued_locks.reentrant_lock_obtained"](#redis_queued_locksreentrant_lock_obtained)
|
|
76
80
|
- ["redis_queued_locks.lock_hold_and_release"](#redis_queued_lockslock_hold_and_release)
|
|
77
81
|
- ["redis_queued_locks.reentrant_lock_hold_completes"](#redis_queued_locksreentrant_lock_hold_completes)
|
|
82
|
+
- ["redis_queued_locks.lock_series_obtained"](#redis_queued_lockslock_series_obtained)
|
|
83
|
+
- ["redis_queued_locks.lock_series_hold_and_release"](#redis_queued_lockslock_series_hold_and_release)
|
|
78
84
|
- ["redis_queued_locks.explicit_lock_release"](#redis_queued_locksexplicit_lock_release)
|
|
85
|
+
- ["redis_queued_locks.explicit_read_lock_release"](#redis_queued_locksexplicit_read_lock_release)
|
|
79
86
|
- ["redis_queued_locks.explicit_all_locks_release"](#redis_queued_locksexplicit_all_locks_release)
|
|
80
87
|
- ["redis_queued_locks.release_locks_of"](#redis_queued_locksrelease_locks_of)
|
|
81
88
|
- [Roadmap](#roadmap)
|
|
@@ -422,6 +429,7 @@ def lock(
|
|
|
422
429
|
raise_errors: false,
|
|
423
430
|
fail_fast: false,
|
|
424
431
|
conflict_strategy: config['default_conflict_strategy'],
|
|
432
|
+
read_write_mode: :write,
|
|
425
433
|
access_strategy: config['default_access_strategy'],
|
|
426
434
|
identity: uniq_identity, # (attr_accessor) calculated during client instantiation via config['uniq_identifier'] proc;
|
|
427
435
|
meta: nil,
|
|
@@ -502,6 +510,13 @@ def lock(
|
|
|
502
510
|
- `:wait_for_lock` - (default) - work in classic way (with timeouts, retry delays, retry limits, etc - in classic way :));
|
|
503
511
|
- `:dead_locking` - fail with deadlock exception;
|
|
504
512
|
- See [Deadlocks and Reentrant locks](#deadlocks-and-reentrant-locks) documentation section for details;
|
|
513
|
+
- `read_write_mode` - (optional) - `[Symbol]`
|
|
514
|
+
- The lock mode (read/write lock semantics);
|
|
515
|
+
- `:write` by default;
|
|
516
|
+
- Modes:
|
|
517
|
+
- `:write` - exclusive lock: waits for the write lock and for all read locks;
|
|
518
|
+
- `:read` - shared lock: waits for the write lock only (read locks do not block each other);
|
|
519
|
+
- See [Read/Write Locks](#readwrite-locks) documentation section for details;
|
|
505
520
|
- `identity` - (optional) `[String]`
|
|
506
521
|
- An unique string that is unique per `RedisQueuedLock::Client` instance. Resolves the
|
|
507
522
|
collisions between the same process_id/thread_id/fiber_id/ractor_id identifiers on different
|
|
@@ -511,7 +526,7 @@ def lock(
|
|
|
511
526
|
- Identity calculator is pre-configured in `config['uniq_identifier']`;
|
|
512
527
|
- `meta` - (optional) `[NilClass,Hash<String|Symbol,Any>]`
|
|
513
528
|
- A custom metadata wich will be passed to the lock data in addition to the existing data;
|
|
514
|
-
- Custom metadata can not contain reserved lock data keys (such as `lock_key`, `acq_id`, `ts`, `ini_ttl`, `rem_ttl`);
|
|
529
|
+
- Custom metadata can not contain reserved lock data keys (such as `lock_key`, `acq_id`, `ts`, `ini_ttl`, `rem_ttl`, `rw_mode`);
|
|
515
530
|
- `nil` by default (means "no metadata");
|
|
516
531
|
- `detailed_acq_timeout_error` - (optional) `[Boolean]`
|
|
517
532
|
- When the lock acquirement try reached the acquirement time limit (:timeout option) the
|
|
@@ -842,6 +857,7 @@ def lock!(
|
|
|
842
857
|
instrumenter: config['instrumenter'],
|
|
843
858
|
access_strategy: config['default_access_strategy'],
|
|
844
859
|
conflict_strategy: config['default_conflict_strategy'],
|
|
860
|
+
read_write_mode: :write,
|
|
845
861
|
log_sampling_enabled: config['log_sampling_enabled'],
|
|
846
862
|
log_sampling_percent: config['log_sampling_percent'],
|
|
847
863
|
log_sampler: config['log_sampler'],
|
|
@@ -882,6 +898,12 @@ applied with the same value to the each lock acquirement process (for each lock
|
|
|
882
898
|
- `"y"` - 10 seconds
|
|
883
899
|
- `"z"` - 5 seconds
|
|
884
900
|
- when your block of code has completed their execution - all locks will be released immediatly;
|
|
901
|
+
- when the series fails (any lock can not be obtained, with or without an exception) - the locks that are already
|
|
902
|
+
obtained by the series are released immediatly (`acquired_locks` of the failure result lists them);
|
|
903
|
+
- the series releases only locks obtained by itself and only if they are still owned by the current acquirer:
|
|
904
|
+
- reentrant locks (already obtained by your outer logic, `conflict_strategy: :work_through`/`:extendable_work_through`) are kept;
|
|
905
|
+
- a lock that is expired during your block and obtained by another acquirer is kept;
|
|
906
|
+
- `read_write_mode:` is the same for all locks of the series (see [Read/Write Locks](#readwrite-locks));
|
|
885
907
|
- if you did not pass a block - all locks will be released with their forumla-based TTL described above;
|
|
886
908
|
- **HOW WILL THIS CASE WORK IN FUTURE RELESAE**: all locks will have the same TTL (`5 seconds` for our example, not `15/10/5 seconds`);
|
|
887
909
|
|
|
@@ -946,19 +968,25 @@ client.lock_seires('c', 'a', 'b') # ... `a` is still obtained
|
|
|
946
968
|
|
|
947
969
|
###### New instrumentaiton events:
|
|
948
970
|
|
|
971
|
+
- `rw_mode` is the mode of the series (`read_write_mode`, the same for all locks of the series);
|
|
972
|
+
- see [Instrumentation Events](#instrumentation-events) for the payload details;
|
|
973
|
+
|
|
949
974
|
```ruby
|
|
950
975
|
# NEW instrumentation events (examples)
|
|
951
|
-
"redis_queued_locks.lock_series_obtained" => # {lock_keys: ["rql:lock:s", "rql:lock:t", "rql:lock:u"], ttl: 5000, acq_id: "rql:acq:4486/1696/1704/1712/e6fd0da7991e4303", hst_id: "rql:hst:4486/1696/1712/e6fd0da7991e4303", ts:
|
|
952
|
-
"redis_queued_locks.lock_series_hold_and_release" => # {lock_keys: ["rql:lock:x", "rql:lock:y", "rql:lock:z"], 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, instrument: nil}
|
|
976
|
+
"redis_queued_locks.lock_series_obtained" => # {lock_keys: ["rql:lock:s", "rql:lock:t", "rql:lock:u"], rw_mode: :write, 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}
|
|
977
|
+
"redis_queued_locks.lock_series_hold_and_release" => # {lock_keys: ["rql:lock:x", "rql:lock:y", "rql:lock:z"], rw_mode: :write, 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, instrument: nil}
|
|
953
978
|
```
|
|
954
979
|
|
|
955
980
|
###### New logs:
|
|
956
981
|
|
|
982
|
+
- `rw_mode` is the mode of the series (`read_write_mode`, the same for all locks of the series);
|
|
983
|
+
- see [Logging](#logging) for the logged keys;
|
|
984
|
+
|
|
957
985
|
```shell
|
|
958
986
|
# NEW logs (examples)
|
|
959
|
-
[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'
|
|
960
|
-
[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' acq_time => 2.55 (ms)
|
|
961
|
-
[redis_queued_locks.expire_lock_series] lock_keys => '["rql:lock:x", "rql:lock:y", "rql:lock:z"]' queue_ttl => 15 acq_id => 'rql:acq:4486/1696/1704/1712/e6fd0da7991e4303' hst_id => 'rql:hst:4486/1696/1712/e6fd0da7991e4303' acs_strat => 'queued'
|
|
987
|
+
[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'
|
|
988
|
+
[redis_queued_locks.lock_series_obtained] lock_keys => '["rql:lock:a", "rql:lock:b"]' queue_ttl => 15 acq_id => 'rql:acq:4486/1696/1704/1712/e6fd0da7991e4303' hst_id => 'rql:hst:4486/1696/1712/e6fd0da7991e4303' acs_strat => 'queued' rw_mode => 'write' acq_time => 2.55 (ms)
|
|
989
|
+
[redis_queued_locks.expire_lock_series] lock_keys => '["rql:lock:x", "rql:lock:y", "rql:lock:z"]' queue_ttl => 15 acq_id => 'rql:acq:4486/1696/1704/1712/e6fd0da7991e4303' hst_id => 'rql:hst:4486/1696/1712/e6fd0da7991e4303' acs_strat => 'queued' rw_mode => 'write'
|
|
962
990
|
```
|
|
963
991
|
|
|
964
992
|
----
|
|
@@ -985,6 +1013,7 @@ client.lock_series!("x", "y", "z") { ...some_code... }
|
|
|
985
1013
|
- returns `nil` if lock does not exist;
|
|
986
1014
|
- lock data (`Hash<String,String|Integer>`):
|
|
987
1015
|
- `"lock_key"` - `string` - lock key in redis;
|
|
1016
|
+
- `"rw_mode"` - `string` - lock mode: `"write"` (or `"read"` for read locks info, see [Read/Write Locks](#readwrite-locks));
|
|
988
1017
|
- `"acq_id"` - `string` - acquirer identifier (process_id/thread_id/fiber_id/ractor_id/identity);
|
|
989
1018
|
- `"hst_id"` - `string` - host identifier (process_id/thread_id/ractor_id/identity);
|
|
990
1019
|
- `"ts"` - `numeric`/`epoch` - the time when lock was obtained;
|
|
@@ -1008,6 +1037,7 @@ rql.lock_info("your_lock_name")
|
|
|
1008
1037
|
# =>
|
|
1009
1038
|
{
|
|
1010
1039
|
"lock_key" => "rql:lock:your_lock_name",
|
|
1040
|
+
"rw_mode" => "write",
|
|
1011
1041
|
"acq_id" => "rql:acq:123/456/567/678/374dd74324",
|
|
1012
1042
|
"hst_id" => "rql:acq:123/456/678/374dd74324",
|
|
1013
1043
|
"ts" => 123456789.12345,
|
|
@@ -1024,6 +1054,7 @@ rql.lock_info("your_lock_name")
|
|
|
1024
1054
|
# =>
|
|
1025
1055
|
{
|
|
1026
1056
|
"lock_key" => "rql:lock:your_lock_name",
|
|
1057
|
+
"rw_mode" => "write",
|
|
1027
1058
|
"acq_id" => "rql:acq:123/456/567/678/374dd74324",
|
|
1028
1059
|
"hst_id" => "rql:acq:123/456/678/374dd74324",
|
|
1029
1060
|
"ts" => 123456789.12345,
|
|
@@ -1046,6 +1077,7 @@ rql.lock_info("your_lock_name")
|
|
|
1046
1077
|
# =>
|
|
1047
1078
|
{
|
|
1048
1079
|
"lock_key" => "rql:lock:your_lock_name",
|
|
1080
|
+
"rw_mode" => "write",
|
|
1049
1081
|
"acq_id" => "rql:acq:123/456/567/678/374dd74324",
|
|
1050
1082
|
"hst_id" => "rql:acq:123/456/678/374dd74324",
|
|
1051
1083
|
"ts" => 123456789.12345,
|
|
@@ -1062,6 +1094,64 @@ rql.lock_info("your_lock_name")
|
|
|
1062
1094
|
}
|
|
1063
1095
|
```
|
|
1064
1096
|
|
|
1097
|
+
```ruby
|
|
1098
|
+
# <for read/write locks>
|
|
1099
|
+
# (see `read_write_mode:` kwarg attribute of #lock/#lock! methods and "Read/Write Locks" documentation section)
|
|
1100
|
+
# (lock requests are not included: use #queue_info to see the queued readers and writers)
|
|
1101
|
+
|
|
1102
|
+
# -> case 1: the lock is held by readers, writers and readers are waiting in the lock queues;
|
|
1103
|
+
|
|
1104
|
+
# process#1 and process#2 (readers, work in parallel):
|
|
1105
|
+
rql.lock("your_lock_name", read_write_mode: :read, ttl: 5_000, meta: { "kek" => "pek" }) { ... }
|
|
1106
|
+
rql.lock("your_lock_name", read_write_mode: :read, ttl: 3_000) { ... }
|
|
1107
|
+
# process#3 (writer, waits for all readers in the write lock queue):
|
|
1108
|
+
rql.lock("your_lock_name", read_write_mode: :write) { ... }
|
|
1109
|
+
# process#4 (reader, waits for the earlier writer in the read lock queue):
|
|
1110
|
+
rql.lock("your_lock_name", read_write_mode: :read) { ... }
|
|
1111
|
+
|
|
1112
|
+
rql.lock_info("your_lock_name")
|
|
1113
|
+
|
|
1114
|
+
# => (read locks info: process#1 and process#2)
|
|
1115
|
+
{
|
|
1116
|
+
"lock_key" => "rql:lock:your_lock_name",
|
|
1117
|
+
"rw_mode" => "read",
|
|
1118
|
+
"rem_ttl" => 4_985, # remaining ttl of the longest read lock
|
|
1119
|
+
"readers" => [
|
|
1120
|
+
{
|
|
1121
|
+
"acq_id" => "rql:acq:123/456/567/678/374dd74324",
|
|
1122
|
+
"hst_id" => "rql:hst:123/456/678/374dd74324",
|
|
1123
|
+
"ts" => 123456789.12345,
|
|
1124
|
+
"ini_ttl" => 5_000,
|
|
1125
|
+
"rem_ttl" => 4_985,
|
|
1126
|
+
"kek" => "pek" # custom metadata of the read lock
|
|
1127
|
+
},
|
|
1128
|
+
{
|
|
1129
|
+
"acq_id" => "rql:acq:124/457/568/679/a83cb4f9d1",
|
|
1130
|
+
"hst_id" => "rql:hst:124/457/679/a83cb4f9d1",
|
|
1131
|
+
"ts" => 123456789.54321,
|
|
1132
|
+
"ini_ttl" => 3_000,
|
|
1133
|
+
"rem_ttl" => 2_412
|
|
1134
|
+
}
|
|
1135
|
+
]
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
# -> case 2: (some time later) readers are released, the lock is held by the writer (process#3),
|
|
1139
|
+
# the reader (process#4) is still waiting in the read lock queue;
|
|
1140
|
+
|
|
1141
|
+
rql.lock_info("your_lock_name")
|
|
1142
|
+
|
|
1143
|
+
# => (write lock info: process#3, read lock requests are not included)
|
|
1144
|
+
{
|
|
1145
|
+
"lock_key" => "rql:lock:your_lock_name",
|
|
1146
|
+
"rw_mode" => "write",
|
|
1147
|
+
"acq_id" => "rql:acq:125/458/569/680/c7bfcaf4f9",
|
|
1148
|
+
"hst_id" => "rql:hst:125/458/680/c7bfcaf4f9",
|
|
1149
|
+
"ts" => 123456795.12345,
|
|
1150
|
+
"ini_ttl" => 5_000,
|
|
1151
|
+
"rem_ttl" => 4_870
|
|
1152
|
+
}
|
|
1153
|
+
```
|
|
1154
|
+
|
|
1065
1155
|
---
|
|
1066
1156
|
|
|
1067
1157
|
#### #queue_info
|
|
@@ -1079,12 +1169,15 @@ you can receive the lock queue info with empty queue value (an empty array).
|
|
|
1079
1169
|
- set is ordered by score in ASC manner (inside the Redis Set);
|
|
1080
1170
|
- score is represented as a timestamp when the lock request was made;
|
|
1081
1171
|
- represents the acquirer identifier and their score as an array of hashes;
|
|
1082
|
-
- returns `nil` if lock
|
|
1172
|
+
- returns `nil` if lock queues (write and read) do not exist;
|
|
1083
1173
|
- lock queue data (`Hash<String,String|Array<Hash<String|Numeric>>`):
|
|
1084
|
-
- `"lock_queue"` - `string` - lock queue key in redis;
|
|
1174
|
+
- `"lock_queue"` - `string` - lock queue key in redis (write lock requests);
|
|
1085
1175
|
- `"queue"` - `array` - an array of lock requests (array of hashes):
|
|
1086
1176
|
- `"acq_id"` - `string` - acquirer identifier (process_id/thread_id/fiber_id/ractor_id/identity by default);
|
|
1087
1177
|
- `"score"` - `float`/`epoch` - time when the lock request was made (epoch);
|
|
1178
|
+
- `"rw_mode"` - `string` - the requested lock mode (`"write"`);
|
|
1179
|
+
- `"read_lock_queue"`, `"read_queue"` - (only when read lock requests exist) the read lock queue key and read lock requests
|
|
1180
|
+
with the same structure (`"rw_mode"` - `"read"`), see [Read/Write Locks](#readwrite-locks);
|
|
1088
1181
|
|
|
1089
1182
|
```ruby
|
|
1090
1183
|
rql.queue_info("your_lock_name")
|
|
@@ -1093,9 +1186,15 @@ rql.queue_info("your_lock_name")
|
|
|
1093
1186
|
{
|
|
1094
1187
|
"lock_queue" => "rql:lock_queue:your_lock_name",
|
|
1095
1188
|
"queue" => [
|
|
1096
|
-
{ "acq_id" => "rql:acq:123/456/567/678/fa76df9cc2", "score" => 1711606640.540842},
|
|
1097
|
-
{ "acq_id" => "rql:acq:123/567/456/679/c7bfcaf4f9", "score" => 1711606640.540906},
|
|
1098
|
-
{ "acq_id" => "rql:acq:555/329/523/127/7329553b11", "score" => 1711606640.540963},
|
|
1189
|
+
{ "acq_id" => "rql:acq:123/456/567/678/fa76df9cc2", "score" => 1711606640.540842, "rw_mode" => "write" },
|
|
1190
|
+
{ "acq_id" => "rql:acq:123/567/456/679/c7bfcaf4f9", "score" => 1711606640.540906, "rw_mode" => "write" },
|
|
1191
|
+
{ "acq_id" => "rql:acq:555/329/523/127/7329553b11", "score" => 1711606640.540963, "rw_mode" => "write" },
|
|
1192
|
+
# ...etc
|
|
1193
|
+
],
|
|
1194
|
+
# (when read lock requests exist)
|
|
1195
|
+
"read_lock_queue" => "rql:lock_read_queue:your_lock_name",
|
|
1196
|
+
"read_queue" => [
|
|
1197
|
+
{ "acq_id" => "rql:acq:123/456/567/700/fa76df9cc2", "score" => 1711606640.541011, "rw_mode" => "read" },
|
|
1099
1198
|
# ...etc
|
|
1100
1199
|
]
|
|
1101
1200
|
}
|
|
@@ -1203,6 +1302,63 @@ rql.unlock("your_lock_name")
|
|
|
1203
1302
|
|
|
1204
1303
|
---
|
|
1205
1304
|
|
|
1305
|
+
#### #unlock_read - release the read lock of the current acquirer
|
|
1306
|
+
|
|
1307
|
+
<sup>\[[back to top](#usage)\]</sup>
|
|
1308
|
+
|
|
1309
|
+
- releases the read lock of the current acquirer only (see [Read/Write Locks](#readwrite-locks)):
|
|
1310
|
+
other read locks, the write lock and lock requests are not affected (`#unlock` releases the lock entirely);
|
|
1311
|
+
- suitable for read locks obtained without a block of code (`lock(..., read_write_mode: :read)` without a block);
|
|
1312
|
+
- the current acquirer is calculated in the same way as for `#lock` (`#current_acquirer_id`: process, thread, fiber, ractor, identity),
|
|
1313
|
+
so call it from the same thread/fiber that obtained the read lock;
|
|
1314
|
+
- the read lock of a different acquirer can be released via `:acquirer_id` (for example, extracted from `#lock_info`/`#locks_info`);
|
|
1315
|
+
`:identity` is ignored in this case (the acquirer id already contains its own identity);
|
|
1316
|
+
- has an alias: `#release_read_lock`;
|
|
1317
|
+
- accepts:
|
|
1318
|
+
- `lock_name` - (required) `[String]` - the lock name whose read lock should be released;
|
|
1319
|
+
- `:acquirer_id` - (optional) `[String, NilClass]` - the acquirer whose read lock should be released
|
|
1320
|
+
(`nil` by default: the current acquirer is used);
|
|
1321
|
+
- `:identity` - (optional) `[String]` - the same identity as for `#lock` (`uniq_identity` by default);
|
|
1322
|
+
ignored when `:acquirer_id` is passed: the acquirer id already contains its own identity;
|
|
1323
|
+
- `:logger`, `:instrumenter`, `:instrument`, `:log_sampling_enabled`, `:log_sampling_percent`, `:log_sampler`, `:log_sample_this`,
|
|
1324
|
+
`:instr_sampling_enabled`, `:instr_sampling_percent`, `:instr_sampler`, `:instr_sample_this` - (optional) - the same as for `#unlock`;
|
|
1325
|
+
- returns `{ ok: true, result: { rel_time:, rel_key:, rel_acq_id:, lock_res: } }` (`lock_res` - `:released` or `:nothing_to_release`);
|
|
1326
|
+
- raises the `"redis_queued_locks.explicit_read_lock_release"` instrumentation event;
|
|
1327
|
+
|
|
1328
|
+
```ruby
|
|
1329
|
+
rql.lock('report', read_write_mode: :read, ttl: 30_000) # => obtained without a block
|
|
1330
|
+
# ...
|
|
1331
|
+
rql.unlock_read('report')
|
|
1332
|
+
# =>
|
|
1333
|
+
{
|
|
1334
|
+
ok: true,
|
|
1335
|
+
result: {
|
|
1336
|
+
rel_time: 0.32, # milliseconds
|
|
1337
|
+
rel_key: 'rql:lock:report',
|
|
1338
|
+
rel_acq_id: 'rql:acq:17580/2260/2380/2280/3f16b93973612580',
|
|
1339
|
+
lock_res: :released # or :nothing_to_release
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
```
|
|
1343
|
+
|
|
1344
|
+
```ruby
|
|
1345
|
+
# release the read lock of a different acquirer
|
|
1346
|
+
reader = rql.lock_info('report')['readers'].first
|
|
1347
|
+
rql.unlock_read('report', acquirer_id: reader['acq_id'])
|
|
1348
|
+
# =>
|
|
1349
|
+
{
|
|
1350
|
+
ok: true,
|
|
1351
|
+
result: {
|
|
1352
|
+
rel_time: 0.29, # milliseconds
|
|
1353
|
+
rel_key: 'rql:lock:report',
|
|
1354
|
+
rel_acq_id: 'rql:acq:17580/2260/2380/2280/3f16b93973612580', # reader['acq_id']
|
|
1355
|
+
lock_res: :released # or :nothing_to_release
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
```
|
|
1359
|
+
|
|
1360
|
+
---
|
|
1361
|
+
|
|
1206
1362
|
#### #clear_locks - release all locks and lock queues
|
|
1207
1363
|
|
|
1208
1364
|
<sup>\[[back to top](#usage)\]</sup>
|
|
@@ -1471,6 +1627,25 @@ rql.release_locks_of(host_id: rql.current_host_id, acquirer_id: rql.current_acqu
|
|
|
1471
1627
|
- the lock name which ttl should be extended;
|
|
1472
1628
|
- `milliseconds` - (required) `[Integer]`
|
|
1473
1629
|
- how many milliseconds should be added to the lock's TTL;
|
|
1630
|
+
- `:read_write_mode` - (optional) `[Symbol]`
|
|
1631
|
+
- `:write` (default) - extends the write lock (the behavior described below);
|
|
1632
|
+
- `:read` - extends the read lock of the current acquirer only (see [Read/Write Locks](#readwrite-locks)):
|
|
1633
|
+
- the read lock expiration, the read locks registry TTL and the read lock data TTL are extended;
|
|
1634
|
+
- an expired read lock (or a read lock replaced by a writer) is never extended ("revived"):
|
|
1635
|
+
`{ ok: false, result: :async_expire_or_no_lock }` is returned;
|
|
1636
|
+
- the extension is atomic (it is checked and applied inside a transaction that watches the write lock);
|
|
1637
|
+
- `:all_read_locks` - (optional) `[Boolean]`
|
|
1638
|
+
- `false` by default;
|
|
1639
|
+
- `:read` mode only (ignored in `:write` mode): `true` extends **all live read locks** of the lock
|
|
1640
|
+
(of all acquirers) instead of the read lock of the current acquirer:
|
|
1641
|
+
- each live read lock (its expiration and its data TTL) is extended by `milliseconds`,
|
|
1642
|
+
the read locks registry TTL is extended to the longest read lock;
|
|
1643
|
+
- expired read locks are not extended ("revived"); nothing is extended while the write lock is held;
|
|
1644
|
+
- read locks obtained concurrently with the extension (after it reads the read locks) are not extended;
|
|
1645
|
+
- returns `{ ok: true, result: { extended_locks_count: <the number of extended read locks> } }`
|
|
1646
|
+
when at least one read lock is extended;
|
|
1647
|
+
- `:identity` - (optional) `[String]`
|
|
1648
|
+
- the same identity as for `#lock` (`uniq_identity` by default); used for the current acquirer calculation in `:read` mode;
|
|
1474
1649
|
- `:instrumenter` - (optional) `[#notify]`
|
|
1475
1650
|
- custom instrumenter object;
|
|
1476
1651
|
- pre-configured in `config['instrumetner']`;
|
|
@@ -1506,7 +1681,9 @@ rql.release_locks_of(host_id: rql.current_host_id, acquirer_id: rql.current_acqu
|
|
|
1506
1681
|
- marks the method that everything should be instrumneted despite the enabled instrumentation sampling;
|
|
1507
1682
|
- makes sense when instrumentation sampling is enabled;
|
|
1508
1683
|
- `false` by default;
|
|
1509
|
-
- returns `{ ok: true, result: :
|
|
1684
|
+
- returns `{ ok: true, result: { extended_locks_count: Integer } }` when ttl is extended:
|
|
1685
|
+
- `extended_locks_count` - the number of locks whose TTL is extended: `1` for the write lock
|
|
1686
|
+
and for the read lock of the current acquirer, the number of extended read locks for `all_read_locks: true`;
|
|
1510
1687
|
- returns `{ ok: false, result: :async_expire_or_no_lock }` when a lock not found or a lock is already expired during
|
|
1511
1688
|
some steps of invocation (see **Important** section below);
|
|
1512
1689
|
- **Important**:
|
|
@@ -1526,7 +1703,7 @@ rql.release_locks_of(host_id: rql.current_host_id, acquirer_id: rql.current_acqu
|
|
|
1526
1703
|
rql.extend_lock_ttl("my_lock", 5_000) # NOTE: add 5_000 milliseconds
|
|
1527
1704
|
|
|
1528
1705
|
# => `ok` case
|
|
1529
|
-
{ ok: true, result: :
|
|
1706
|
+
{ ok: true, result: { extended_locks_count: 1 } }
|
|
1530
1707
|
|
|
1531
1708
|
# => `failed` case
|
|
1532
1709
|
{ ok: false, result: :async_expire_or_no_lock }
|
|
@@ -1682,7 +1859,8 @@ rql.locks_info # or rql.locks_info(scan_size: 123)
|
|
|
1682
1859
|
- returns `Set<Hash<Symbol,Any>>` (see [#queue_info](#queue_info) and examples below for details).
|
|
1683
1860
|
- contained data: `{ queue: String, requests: Array<Hash<String,Any>> }`
|
|
1684
1861
|
- `:queue` - `String` - lock key queue in Redis;
|
|
1685
|
-
- `:requests` - `Array<Hash<String,Any>>` - lock requests in the que with their acquirer id and
|
|
1862
|
+
- `:requests` - `Array<Hash<String,Any>>` - lock requests in the que with their acquirer id, score and requested lock mode
|
|
1863
|
+
(`"rw_mode"` - `"write"` for `rql:lock_queue:*`, `"read"` for read lock queues `rql:lock_read_queue:*`).
|
|
1686
1864
|
|
|
1687
1865
|
```ruby
|
|
1688
1866
|
rql.queues_info # or rql.qeuues_info(scan_size: 123)
|
|
@@ -1690,14 +1868,13 @@ rql.queues_info # or rql.qeuues_info(scan_size: 123)
|
|
|
1690
1868
|
=> #<Set:
|
|
1691
1869
|
{{:queue=>"rql:lock_queue:some-lock-123",
|
|
1692
1870
|
:requests=>
|
|
1693
|
-
[{"acq_id"=>"rql:acq:38529/4500/4520/4360/66093702f24a3129", "score"=>1711606640.540842},
|
|
1694
|
-
{"acq_id"=>"rql:acq:38529/4580/4600/4360/66093702f24a3129", "score"=>1711606640.540906},
|
|
1695
|
-
{"acq_id"=>"rql:acq:38529/4620/4640/4360/66093702f24a3129", "score"=>1711606640.5409632}]},
|
|
1696
|
-
{:queue=>"rql:
|
|
1871
|
+
[{"acq_id"=>"rql:acq:38529/4500/4520/4360/66093702f24a3129", "score"=>1711606640.540842, "rw_mode"=>"write"},
|
|
1872
|
+
{"acq_id"=>"rql:acq:38529/4580/4600/4360/66093702f24a3129", "score"=>1711606640.540906, "rw_mode"=>"write"},
|
|
1873
|
+
{"acq_id"=>"rql:acq:38529/4620/4640/4360/66093702f24a3129", "score"=>1711606640.5409632, "rw_mode"=>"write"}]},
|
|
1874
|
+
{:queue=>"rql:lock_read_queue:some-lock-456",
|
|
1697
1875
|
:requests=>
|
|
1698
|
-
[{"acq_id"=>"rql:acq:38529/4380/4400/4360/66093702f24a3129", "score"=>1711606640.540722},
|
|
1699
|
-
{"acq_id"=>"rql:acq:38529/4420/4440/4360/66093702f24a3129", "score"=>1711606640.5407748},
|
|
1700
|
-
{"acq_id"=>"rql:acq:38529/4460/4480/4360/66093702f24a3129", "score"=>1711606640.540808}]},
|
|
1876
|
+
[{"acq_id"=>"rql:acq:38529/4380/4400/4360/66093702f24a3129", "score"=>1711606640.540722, "rw_mode"=>"read"},
|
|
1877
|
+
{"acq_id"=>"rql:acq:38529/4420/4440/4360/66093702f24a3129", "score"=>1711606640.5407748, "rw_mode"=>"read"}]},
|
|
1701
1878
|
...}>
|
|
1702
1879
|
```
|
|
1703
1880
|
---
|
|
@@ -2110,6 +2287,92 @@ rql.possible_host_ids
|
|
|
2110
2287
|
|
|
2111
2288
|
---
|
|
2112
2289
|
|
|
2290
|
+
## Read/Write Locks
|
|
2291
|
+
|
|
2292
|
+
<sup>\[[back to top](#table-of-contents)\]</sup>
|
|
2293
|
+
|
|
2294
|
+
- `read_write_mode:` attribute of `#lock` and `#lock!`:
|
|
2295
|
+
- `:write` (default) - exclusive lock (the classic RQL lock): waits for the write lock and for all read locks;
|
|
2296
|
+
- `:read` - shared lock: waits for the write lock only (read locks of different acquirers work in parallel);
|
|
2297
|
+
|
|
2298
|
+
```ruby
|
|
2299
|
+
# readers work in parallel
|
|
2300
|
+
rql.lock('report', read_write_mode: :read) { read_report }
|
|
2301
|
+
|
|
2302
|
+
# writer waits for all readers (and readers wait for the writer)
|
|
2303
|
+
rql.lock('report', read_write_mode: :write) { rebuild_report }
|
|
2304
|
+
rql.lock('report') { rebuild_report } # (write mode is the default mode)
|
|
2305
|
+
```
|
|
2306
|
+
|
|
2307
|
+
- lock request ordering (`access_strategy`):
|
|
2308
|
+
- `:queued` (default) - FIFO between modes: read lock request waits for every earlier write lock request,
|
|
2309
|
+
write lock request waits for every earlier read and write lock request (so writers are not starved by a
|
|
2310
|
+
continuous flow of readers and consecutive readers obtain the lock together);
|
|
2311
|
+
- `:random` - lock queues are ignored: any free lock is obtained (a continuous flow of readers can starve writers);
|
|
2312
|
+
- read locks:
|
|
2313
|
+
- each read lock has its own `ttl` and is released (after the block execution) or expired independently of other read locks;
|
|
2314
|
+
- `ttl` should be a positive `Integer` (`RedisQueuedLocks::ArgumentError`);
|
|
2315
|
+
- each read lock has its own data with the same structure as the write lock data: acquirer, host, timestamp,
|
|
2316
|
+
initial ttl, `:meta` (with the same reserved keys) and reentrant lock data (`spc_cnt`, `spc_ext_ttl`, ...);
|
|
2317
|
+
the read lock data lives as long as the read lock and is returned by `#lock_info`/`#locks_info` for each reader;
|
|
2318
|
+
- `#unlock` releases the lock entirely: the write lock, all read locks (with their data) and all lock requests;
|
|
2319
|
+
- `#unlock_read` (aka `#release_read_lock`) releases the read lock of the current acquirer
|
|
2320
|
+
(suitable for read locks obtained without a block), or the read lock of the acquirer passed via `acquirer_id:`;
|
|
2321
|
+
- `#extend_lock_ttl(lock_name, milliseconds, read_write_mode: :read)` extends the read lock of the current acquirer only;
|
|
2322
|
+
`all_read_locks: true` extends all live read locks of the lock;
|
|
2323
|
+
- `#lock_series` with `read_write_mode: :read` obtains a series of read locks (the mode is the same for all locks of the series):
|
|
2324
|
+
- read lock series of different acquirers work in parallel, writers wait for them;
|
|
2325
|
+
- after the block (or when the series fails) only the read locks of the current acquirer are released;
|
|
2326
|
+
- reentrant read locks of the series (already obtained by the outer logic of the same acquirer) are not released by the series;
|
|
2327
|
+
- reentrant locks (the same acquirer, see [Deadlocks and Reentrant locks](#deadlocks-and-reentrant-locks)):
|
|
2328
|
+
- read lock request under the obtained read lock or write lock is processed as a reentrant lock of the obtained lock
|
|
2329
|
+
in accordance with `conflict_strategy`;
|
|
2330
|
+
- write lock request under the obtained read lock (read-to-write lock upgrade) fails with
|
|
2331
|
+
`RedisQueuedLocks::ConflictLockObtainError` (`{ ok: false, result: :conflict_lock_upgrade }`) for
|
|
2332
|
+
`:work_through` and `:extendable_work_through` (`:dead_locking` fails as usual, `:wait_for_lock` waits for the read lock expiration);
|
|
2333
|
+
- info methods:
|
|
2334
|
+
- `#locked?` - the lock is obtained by a writer or by any reader;
|
|
2335
|
+
- `#queued?` - there are write or read lock requests;
|
|
2336
|
+
- `#lock_info` - write lock info (`'rw_mode' => 'write'`), or read locks info when there is no write lock:
|
|
2337
|
+
```ruby
|
|
2338
|
+
{
|
|
2339
|
+
'lock_key' => 'rql:lock:report',
|
|
2340
|
+
'rw_mode' => 'read',
|
|
2341
|
+
'rem_ttl' => 4985, # remaining ttl of the longest read lock (milliseconds)
|
|
2342
|
+
'readers' => [
|
|
2343
|
+
{
|
|
2344
|
+
'acq_id' => 'rql:acq:...',
|
|
2345
|
+
'hst_id' => 'rql:hst:...',
|
|
2346
|
+
'ts' => 1791582966.125299,
|
|
2347
|
+
'ini_ttl' => 5000,
|
|
2348
|
+
'rem_ttl' => 4985,
|
|
2349
|
+
'some_meta_key' => 'some_meta_value' # (custom `:meta`)
|
|
2350
|
+
},
|
|
2351
|
+
{ 'acq_id' => 'rql:acq:...', 'hst_id' => 'rql:hst:...', 'ts' => 1791582967.5, 'ini_ttl' => 5000, 'rem_ttl' => 3120 }
|
|
2352
|
+
]
|
|
2353
|
+
}
|
|
2354
|
+
```
|
|
2355
|
+
- `#queue_info` - additional `"read_lock_queue"`/`"read_queue"` keys when read lock requests exist;
|
|
2356
|
+
each request of `#queue_info`/`#queues_info` has `"rw_mode"` (`"write"`/`"read"`);
|
|
2357
|
+
- `#locks`/`#locks_info`, `#queues`/`#queues_info`, `#keys` - include read locks and read lock queues
|
|
2358
|
+
(`#locks_info` info has `'rw_mode'` - `'write'` or `'read'`);
|
|
2359
|
+
- logs and instrumentation: `rw_mode` is added to the lock logs and to the instrumentation payloads
|
|
2360
|
+
(lock series logs and events: the mode of the series), see [Logging](#logging) and [Instrumentation Events](#instrumentation-events);
|
|
2361
|
+
- data layout:
|
|
2362
|
+
- `rql:lock:<lock_name>` - write lock (`HASH`, the classic RQL lock);
|
|
2363
|
+
- `rql:lock_queue:<lock_name>` - write lock requests (`ZSET`, the classic RQL lock queue);
|
|
2364
|
+
- `rql:lock_readers:<lock_name>` - read locks (`ZSET`: acquirer id => read lock expiration time in Redis time, milliseconds);
|
|
2365
|
+
- `rql:lock_read_queue:<lock_name>` - read lock requests (`ZSET`);
|
|
2366
|
+
- `rql:lock_reader:<lock_name>:<acquirer_id>` - read lock data (`HASH`, the same structure as the write lock, read lock ttl);
|
|
2367
|
+
- implementation notes:
|
|
2368
|
+
- optimistic transactions only (`WATCH`/`MULTI`): readers watch the write lock key, writers watch the write lock key and the read locks key,
|
|
2369
|
+
so readers never invalidate transactions of each other;
|
|
2370
|
+
- lock queues are responsible for the ordering only, mutual exclusion is guaranteed by the lock state check;
|
|
2371
|
+
- **all your processes should use the RQL version with read/write locks** before you start to use read locks:
|
|
2372
|
+
write locks of older RQL versions do not check read locks;
|
|
2373
|
+
|
|
2374
|
+
---
|
|
2375
|
+
|
|
2113
2376
|
## Deadlocks and Reentrant locks
|
|
2114
2377
|
|
|
2115
2378
|
<sup>\[[back to top](#table-of-contents)\]</sup>
|
|
@@ -2135,39 +2398,64 @@ rql.possible_host_ids
|
|
|
2135
2398
|
- [Logging Configuration](#logging-configuration)
|
|
2136
2399
|
|
|
2137
2400
|
|
|
2401
|
+
- each lock log contains `rw_mode` - the lock mode (`'write'` or `'read'`, see [Read/Write Locks](#readwrite-locks)):
|
|
2402
|
+
- the **requested** mode in lock obtaining logs, try-lock logs (`try_lock.*`) and `fail_fast_or_limits_reached_or_deadlock__dequeue`;
|
|
2403
|
+
- the **held** mode in `expire_lock` and `decrease_lock` (the lock that is released or decreased: a reentrant read lock request
|
|
2404
|
+
under the write lock of the same acquirer holds the write lock);
|
|
2405
|
+
|
|
2138
2406
|
- default logs (raised from `#lock`/`#lock!`):
|
|
2139
2407
|
|
|
2140
2408
|
```ruby
|
|
2141
|
-
"[redis_queued_locks.start_lock_obtaining]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
2142
|
-
"[redis_queued_locks.start_try_to_lock_cycle]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
2143
|
-
"[redis_queued_locks.dead_score_reached__reset_acquirer_position]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
2144
|
-
"[redis_queued_locks.lock_obtained]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acq_time");
|
|
2145
|
-
"[redis_queued_locks.extendable_reentrant_lock_obtained]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "acq_time");
|
|
2146
|
-
"[redis_queued_locks.reentrant_lock_obtained]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "acq_time");
|
|
2147
|
-
"[redis_queued_locks.fail_fast_or_limits_reached_or_deadlock__dequeue]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
2148
|
-
"[redis_queued_locks.expire_lock]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
2149
|
-
"[redis_queued_locks.decrease_lock]" # (logs "lock_key", "decreased_ttl", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
2409
|
+
"[redis_queued_locks.start_lock_obtaining]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2410
|
+
"[redis_queued_locks.start_try_to_lock_cycle]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2411
|
+
"[redis_queued_locks.dead_score_reached__reset_acquirer_position]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2412
|
+
"[redis_queued_locks.lock_obtained]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
2413
|
+
"[redis_queued_locks.extendable_reentrant_lock_obtained]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
2414
|
+
"[redis_queued_locks.reentrant_lock_obtained]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
2415
|
+
"[redis_queued_locks.fail_fast_or_limits_reached_or_deadlock__dequeue]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2416
|
+
"[redis_queued_locks.expire_lock]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2417
|
+
"[redis_queued_locks.decrease_lock]" # (logs "lock_key", "decreased_ttl", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2150
2418
|
```
|
|
2151
2419
|
|
|
2152
2420
|
- additional logs (raised from `#lock`/`#lock!` with `confg[:log_lock_try] == true`):
|
|
2153
2421
|
|
|
2154
2422
|
```ruby
|
|
2155
|
-
"[redis_queued_locks.try_lock.start]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
2156
|
-
"[redis_queued_locks.try_lock.rconn_fetched]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
2157
|
-
"[redis_queued_locks.try_lock.same_process_conflict_detected]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
2158
|
-
"[redis_queued_locks.try_lock.same_process_conflict_analyzed]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "spc_status");
|
|
2159
|
-
"[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");
|
|
2160
|
-
"[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);
|
|
2161
|
-
"[redis_queued_locks.try_lock.single_process_lock_conflict__dead_lock]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "spc_status", "last_spc_ts");
|
|
2162
|
-
"[redis_queued_locks.try_lock.
|
|
2163
|
-
"[redis_queued_locks.try_lock.
|
|
2164
|
-
"[redis_queued_locks.try_lock.
|
|
2165
|
-
"[redis_queued_locks.try_lock.
|
|
2166
|
-
"[redis_queued_locks.try_lock.
|
|
2167
|
-
"[redis_queued_locks.try_lock.
|
|
2168
|
-
"[redis_queued_locks.try_lock.
|
|
2423
|
+
"[redis_queued_locks.try_lock.start]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2424
|
+
"[redis_queued_locks.try_lock.rconn_fetched]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2425
|
+
"[redis_queued_locks.try_lock.same_process_conflict_detected]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2426
|
+
"[redis_queued_locks.try_lock.same_process_conflict_analyzed]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "spc_status");
|
|
2427
|
+
"[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");
|
|
2428
|
+
"[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");
|
|
2429
|
+
"[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");
|
|
2430
|
+
"[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");
|
|
2431
|
+
"[redis_queued_locks.try_lock.acq_added_to_queue]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2432
|
+
"[redis_queued_locks.try_lock.remove_expired_acqs]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2433
|
+
"[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");
|
|
2434
|
+
"[redis_queued_locks.try_lock.exit__queue_ttl_reached]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2435
|
+
"[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>");
|
|
2436
|
+
"[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>");
|
|
2437
|
+
"[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");
|
|
2438
|
+
"[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");
|
|
2439
|
+
"[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");
|
|
2440
|
+
"[redis_queued_locks.try_lock.obtain__free_to_acquire]" # (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2169
2441
|
```
|
|
2170
2442
|
|
|
2443
|
+
- lock series logs (raised from `#lock_series`/`#lock_series!`, PoC; `rw_mode` is the mode of the series):
|
|
2444
|
+
|
|
2445
|
+
```ruby
|
|
2446
|
+
"[redis_queued_locks.start_lock_series_obtaining]" # (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2447
|
+
"[redis_queued_locks.lock_series_obtained]" # (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
2448
|
+
"[redis_queued_locks.expire_lock_series]" # (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2449
|
+
```
|
|
2450
|
+
|
|
2451
|
+
- read/write try-lock logs (see [Read/Write Locks](#readwrite-locks)):
|
|
2452
|
+
- `single_process_lock_conflict__lock_upgrade` - write lock request of the acquirer that holds the read lock
|
|
2453
|
+
(read-to-write lock upgrade is not supported for `:work_through` and `:extendable_work_through` conflict strategies);
|
|
2454
|
+
- `exit__write_request_ahead` - read lock request waits for an earlier write lock request (`ahead_acq_id`, `:queued` access strategy);
|
|
2455
|
+
- `exit__read_request_ahead` - write lock request waits for an earlier read lock request (`ahead_acq_id`, `:queued` access strategy);
|
|
2456
|
+
- `exit__read_lock_still_obtained` - write lock request waits for live read locks
|
|
2457
|
+
(read lock request: waits for the own read lock with `:wait_for_lock` conflict strategy);
|
|
2458
|
+
|
|
2171
2459
|
---
|
|
2172
2460
|
|
|
2173
2461
|
### Logging Configuration
|
|
@@ -2185,15 +2473,20 @@ rql.possible_host_ids
|
|
|
2185
2473
|
# - should implement `debug(progname = nil, &block)` (minimal requirement) or be an instance of Ruby's `::Logger` class/subclass;
|
|
2186
2474
|
# - supports `SemanticLogger::Logger` (see "semantic_logger" gem)
|
|
2187
2475
|
# - at this moment the only debug logs are realised in following cases:
|
|
2188
|
-
# - "[redis_queued_locks.start_lock_obtaining]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
2189
|
-
# - "[redis_queued_locks.start_try_to_lock_cycle]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
2190
|
-
# - "[redis_queued_locks.dead_score_reached__reset_acquirer_position]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
2191
|
-
# - "[redis_queued_locks.lock_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "
|
|
2192
|
-
# - "[redis_queued_locks.extendable_reentrant_lock_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "
|
|
2193
|
-
# - "[redis_queued_locks.reentrant_lock_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "
|
|
2194
|
-
# - "[redis_queued_locks.fail_fast_or_limits_reached_or_deadlock__dequeue]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
2195
|
-
# - "[redis_queued_locks.expire_lock]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
2196
|
-
# - "[redis_queued_locks.decrease_lock]" (logs "lock_key", "decreased_ttl", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
2476
|
+
# - "[redis_queued_locks.start_lock_obtaining]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2477
|
+
# - "[redis_queued_locks.start_try_to_lock_cycle]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2478
|
+
# - "[redis_queued_locks.dead_score_reached__reset_acquirer_position]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2479
|
+
# - "[redis_queued_locks.lock_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
2480
|
+
# - "[redis_queued_locks.extendable_reentrant_lock_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
2481
|
+
# - "[redis_queued_locks.reentrant_lock_obtained]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
2482
|
+
# - "[redis_queued_locks.fail_fast_or_limits_reached_or_deadlock__dequeue]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2483
|
+
# - "[redis_queued_locks.expire_lock]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2484
|
+
# - "[redis_queued_locks.decrease_lock]" (logs "lock_key", "decreased_ttl", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2485
|
+
# - "[redis_queued_locks.start_lock_series_obtaining]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2486
|
+
# - "[redis_queued_locks.lock_series_obtained]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "acq_time");
|
|
2487
|
+
# - "[redis_queued_locks.expire_lock_series]" (logs "lock_keys", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2488
|
+
# - "rw_mode" is the requested lock mode ('write'/'read'), the held lock mode for "expire_lock" and "decrease_lock",
|
|
2489
|
+
# the mode of the series for lock series logs;
|
|
2197
2490
|
# - by default uses VoidLogger that does nothing;
|
|
2198
2491
|
config['logger'] = RedisQueuedLocks::Logging::VoidLogger
|
|
2199
2492
|
|
|
@@ -2201,19 +2494,25 @@ config['logger'] = RedisQueuedLocks::Logging::VoidLogger
|
|
|
2201
2494
|
# - adds additional debug logs;
|
|
2202
2495
|
# - enables additional logs for each internal try-retry lock acquiring (a lot of logs can be generated depending on your retry configurations);
|
|
2203
2496
|
# - it adds following debug logs in addition to the existing:
|
|
2204
|
-
# - "[redis_queued_locks.try_lock.start]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
2205
|
-
# - "[redis_queued_locks.try_lock.rconn_fetched]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
2206
|
-
# - "[redis_queued_locks.try_lock.same_process_conflict_detected]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat");
|
|
2207
|
-
# - "[redis_queued_locks.try_lock.same_process_conflict_analyzed]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "spc_status");
|
|
2208
|
-
# - "[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");
|
|
2209
|
-
# - "[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);
|
|
2210
|
-
# - "[redis_queued_locks.try_lock.
|
|
2211
|
-
# - "[redis_queued_locks.try_lock.
|
|
2212
|
-
# - "[redis_queued_locks.try_lock.
|
|
2213
|
-
# - "[redis_queued_locks.try_lock.
|
|
2214
|
-
# - "[redis_queued_locks.try_lock.
|
|
2215
|
-
# - "[redis_queued_locks.try_lock.
|
|
2216
|
-
# - "[redis_queued_locks.try_lock.
|
|
2497
|
+
# - "[redis_queued_locks.try_lock.start]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2498
|
+
# - "[redis_queued_locks.try_lock.rconn_fetched]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2499
|
+
# - "[redis_queued_locks.try_lock.same_process_conflict_detected]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2500
|
+
# - "[redis_queued_locks.try_lock.same_process_conflict_analyzed]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode", "spc_status");
|
|
2501
|
+
# - "[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");
|
|
2502
|
+
# - "[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");
|
|
2503
|
+
# - "[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");
|
|
2504
|
+
# - "[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");
|
|
2505
|
+
# - "[redis_queued_locks.try_lock.acq_added_to_queue]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2506
|
+
# - "[redis_queued_locks.try_lock.remove_expired_acqs]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2507
|
+
# - "[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");
|
|
2508
|
+
# - "[redis_queued_locks.try_lock.exit__queue_ttl_reached]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2509
|
+
# - "[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>");
|
|
2510
|
+
# - "[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>");
|
|
2511
|
+
# - "[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");
|
|
2512
|
+
# - "[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");
|
|
2513
|
+
# - "[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");
|
|
2514
|
+
# - "[redis_queued_locks.try_lock.obtain__free_to_acquire]" (logs "lock_key", "queue_ttl", "acq_id", "hst_id", "acs_strat", "rw_mode");
|
|
2515
|
+
# - "rw_mode" is the requested lock mode ('write'/'read');
|
|
2217
2516
|
config['log_lock_try'] = false
|
|
2218
2517
|
|
|
2219
2518
|
# (default: false)
|
|
@@ -2313,12 +2612,15 @@ config['instr_sampler'] = RedisQueuedLocks::Instrument::Sampler
|
|
|
2313
2612
|
|
|
2314
2613
|
List of instrumentation events
|
|
2315
2614
|
|
|
2316
|
-
- ["redis_queued_locks.lock_obtained"](#
|
|
2615
|
+
- ["redis_queued_locks.lock_obtained"](#redis_queued_lockslock_obtained)
|
|
2317
2616
|
- ["redis_queued_locks.extendable_reentrant_lock_obtained"](#redis_queued_locksextendable_reentrant_lock_obtained)
|
|
2318
2617
|
- ["redis_queued_locks.reentrant_lock_obtained"](#redis_queued_locksreentrant_lock_obtained)
|
|
2319
2618
|
- ["redis_queued_locks.lock_hold_and_release"](#redis_queued_lockslock_hold_and_release)
|
|
2320
2619
|
- ["redis_queued_locks.reentrant_lock_hold_completes"](#redis_queued_locksreentrant_lock_hold_completes)
|
|
2620
|
+
- ["redis_queued_locks.lock_series_obtained"](#redis_queued_lockslock_series_obtained)
|
|
2621
|
+
- ["redis_queued_locks.lock_series_hold_and_release"](#redis_queued_lockslock_series_hold_and_release)
|
|
2321
2622
|
- ["redis_queued_locks.explicit_lock_release"](#redis_queued_locksexplicit_lock_release)
|
|
2623
|
+
- ["redis_queued_locks.explicit_read_lock_release"](#redis_queued_locksexplicit_read_lock_release)
|
|
2322
2624
|
- ["redis_queued_locks.explicit_all_locks_release"](#redis_queued_locksexplicit_all_locks_release)
|
|
2323
2625
|
- ["redis_queued_locks.release_locks_of"](#redis_queued_locksrelease_locks_of)
|
|
2324
2626
|
|
|
@@ -2333,6 +2635,7 @@ Detalized event semantics and payload structure:
|
|
|
2333
2635
|
- `:acq_id` - `string` - lock acquirer identifier;
|
|
2334
2636
|
- `:hst_id` - `string` - lock's host identifier;
|
|
2335
2637
|
- `:lock_key` - `string` - lock name;
|
|
2638
|
+
- `:rw_mode` - `symbol` - requested lock mode: `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
2336
2639
|
- `:ts` - `numeric`/`epoch` - the time when the lock was obtaiend;
|
|
2337
2640
|
- `:acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
|
|
2338
2641
|
- `:instrument` - `nil`/`Any` - custom data passed to the `#lock`/`#lock!` method as `:instrument` attribute;
|
|
@@ -2343,6 +2646,8 @@ Detalized event semantics and payload structure:
|
|
|
2343
2646
|
- raised from `#lock`/`#lock!` when the lock was obtained as reentrant lock;
|
|
2344
2647
|
- payload:
|
|
2345
2648
|
- `:lock_key` - `string` - lock name;
|
|
2649
|
+
- `:rw_mode` - `symbol` - requested lock mode: `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
2650
|
+
a read lock request under the write lock of the same acquirer works through the write lock (`:rw_mode` is `:read`);
|
|
2346
2651
|
- `:ttl` - `integer`/`milliseconds` - last lock ttl by reentrant locking;
|
|
2347
2652
|
- `:acq_id` - `string` - lock acquirer identifier;
|
|
2348
2653
|
- `:hst_id` - `string` - lock's host identifier;
|
|
@@ -2356,6 +2661,8 @@ Detalized event semantics and payload structure:
|
|
|
2356
2661
|
- raised from `#lock`/`#lock!` when the lock was obtained as reentrant lock;
|
|
2357
2662
|
- payload:
|
|
2358
2663
|
- `:lock_key` - `string` - lock name;
|
|
2664
|
+
- `:rw_mode` - `symbol` - requested lock mode: `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
2665
|
+
a read lock request under the write lock of the same acquirer works through the write lock (`:rw_mode` is `:read`);
|
|
2359
2666
|
- `:ttl` - `integer`/`milliseconds` - last lock ttl by reentrant locking;
|
|
2360
2667
|
- `:acq_id` - `string` - lock acquirer identifier;
|
|
2361
2668
|
- `:hst_id` - `string` - lock's host identifier;
|
|
@@ -2373,6 +2680,7 @@ Detalized event semantics and payload structure:
|
|
|
2373
2680
|
- `:acq_id` - `string` - lock acquirer identifier;
|
|
2374
2681
|
- `:hst_id` - `string` - lock's host identifier;
|
|
2375
2682
|
- `:lock_key` - `string` - lock name;
|
|
2683
|
+
- `:rw_mode` - `symbol` - requested lock mode: `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
2376
2684
|
- `:ts` - `numeric`/`epoch` - the time when lock was obtained;
|
|
2377
2685
|
- `:acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
|
|
2378
2686
|
- `:instrument` - `nil`/`Any` - custom data passed to the `#lock`/`#lock!` method as `:instrument` attribute;
|
|
@@ -2389,18 +2697,63 @@ Detalized event semantics and payload structure:
|
|
|
2389
2697
|
- `:hst_id` - `string` - lock's host identifier;
|
|
2390
2698
|
- `:ts` - `numeric`/`epoch` - the time when the lock was obtaiend as reentrant lock;
|
|
2391
2699
|
- `:lock_key` - `string` - lock name;
|
|
2700
|
+
- `:rw_mode` - `symbol` - requested lock mode: `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
2701
|
+
a read lock request under the write lock of the same acquirer works through the write lock (`:rw_mode` is `:read`);
|
|
2392
2702
|
- `:acq_time` - `float`/`milliseconds` - time spent on lock acquiring;
|
|
2393
2703
|
- `:instrument` - `nil`/`Any` - custom data passed to the `#lock`/`#lock!` method as `:instrument` attribute;
|
|
2394
2704
|
|
|
2705
|
+
##### `"redis_queued_locks.lock_series_obtained"`
|
|
2706
|
+
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
2707
|
+
- (PoC) a moment when all locks of the series were obtained;
|
|
2708
|
+
- raised from `#lock_series`/`#lock_series!`;
|
|
2709
|
+
- payload:
|
|
2710
|
+
- `:lock_keys` - `array<string>` - lock names of the series;
|
|
2711
|
+
- `:rw_mode` - `symbol` - the mode of the series (`read_write_mode`): `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
2712
|
+
- `:ttl` - `integer`/`milliseconds` - the `ttl` option of the series (each lock of the series gets its own ttl,
|
|
2713
|
+
see [lock_series](#lock_series---poc-acquire-a-series-of-locks));
|
|
2714
|
+
- `:acq_id` - `string` - lock acquirer identifier;
|
|
2715
|
+
- `:hst_id` - `string` - lock's host identifier;
|
|
2716
|
+
- `:ts` - `numeric`/`epoch` - the time when the series was obtained;
|
|
2717
|
+
- `:acq_time` - `float`/`milliseconds` - time spent on the series acquiring;
|
|
2718
|
+
- `:instrument` - `nil`/`Any` - custom data passed to the `#lock_series`/`#lock_series!` method as `:instrument` attribute;
|
|
2719
|
+
|
|
2720
|
+
##### `"redis_queued_locks.lock_series_hold_and_release"`
|
|
2721
|
+
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
2722
|
+
- (PoC) an event signalizes about the "hold+and+release" process of the series is finished;
|
|
2723
|
+
- raised from `#lock_series`/`#lock_series!` when invoked with a block of code;
|
|
2724
|
+
- payload:
|
|
2725
|
+
- `:lock_keys` - `array<string>` - lock names of the series;
|
|
2726
|
+
- `:rw_mode` - `symbol` - the mode of the series (`read_write_mode`): `:write` or `:read` (see [Read/Write Locks](#readwrite-locks));
|
|
2727
|
+
- `:hold_time` - `float`/`milliseconds` - the series hold time;
|
|
2728
|
+
- `:ttl` - `integer`/`milliseconds` - the `ttl` option of the series;
|
|
2729
|
+
- `:acq_id` - `string` - lock acquirer identifier;
|
|
2730
|
+
- `:hst_id` - `string` - lock's host identifier;
|
|
2731
|
+
- `:ts` - `float`/`epoch` - the time when the series was released;
|
|
2732
|
+
- `:acq_time` - `float`/`milliseconds` - time spent on the series acquiring;
|
|
2733
|
+
- `:instrument` - `nil`/`Any` - custom data passed to the `#lock_series`/`#lock_series!` method as `:instrument` attribute;
|
|
2734
|
+
|
|
2395
2735
|
##### `"redis_queued_locks.explicit_lock_release"`
|
|
2396
2736
|
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
2397
2737
|
- an event signalizes about the explicit lock release (invoked via `RedisQueuedLock#unlock`);
|
|
2398
2738
|
- raised from `#unlock`;
|
|
2739
|
+
- the write lock, all read locks and both lock request queues (write and read) are released;
|
|
2399
2740
|
- payload:
|
|
2400
2741
|
- `:at` - `float`/`epoch` - the time when the lock was released;
|
|
2401
2742
|
- `:rel_time` - `float`/`milliseconds` - time spent on lock releasing;
|
|
2402
2743
|
- `:lock_key` - `string` - released lock (lock name);
|
|
2403
|
-
- `:lock_key_queue` - `string` - released lock queue (
|
|
2744
|
+
- `:lock_key_queue` - `string` - released lock queue (the queue of write lock requests);
|
|
2745
|
+
|
|
2746
|
+
##### `"redis_queued_locks.explicit_read_lock_release"`
|
|
2747
|
+
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
2748
|
+
- an event signalizes about the explicit release of the read lock of the concrete acquirer (invoked via `RedisQueuedLock#unlock_read`);
|
|
2749
|
+
- raised from `#unlock_read` (aka `#release_read_lock`);
|
|
2750
|
+
- payload:
|
|
2751
|
+
- `:at` - `float`/`epoch` - the time when the read lock was released;
|
|
2752
|
+
- `:rel_time` - `float`/`milliseconds` - time spent on read lock releasing;
|
|
2753
|
+
- `:lock_key` - `string` - lock key (`rql:lock:<lock_name>`);
|
|
2754
|
+
- `:acq_id` - `string` - the read lock acquirer;
|
|
2755
|
+
- `:lock_res` - `symbol` - `:released` or `:nothing_to_release`;
|
|
2756
|
+
- `:instrument` - `nil`/`Any` - custom data passed to the `#unlock_read` method as `:instrument` attribute;
|
|
2404
2757
|
|
|
2405
2758
|
##### `"redis_queued_locks.explicit_all_locks_release"`
|
|
2406
2759
|
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
@@ -2409,7 +2762,8 @@ Detalized event semantics and payload structure:
|
|
|
2409
2762
|
- payload:
|
|
2410
2763
|
- `:rel_time` - `float`/`milliseconds` - time spent on "realese all locks" operation;
|
|
2411
2764
|
- `:at` - `float`/`epoch` - the time when the operation has ended;
|
|
2412
|
-
- `:
|
|
2765
|
+
- `:rel_key_cnt` - `integer` - released redis keys count (write locks, readers registries, read lock data,
|
|
2766
|
+
write and read lock request queues);
|
|
2413
2767
|
|
|
2414
2768
|
##### `"redis_queued_locks.release_locks_of"`
|
|
2415
2769
|
- <sup>\[[back to the list](#instrumentation-events)\]</sup>
|
|
@@ -2420,8 +2774,8 @@ Detalized event semantics and payload structure:
|
|
|
2420
2774
|
- `:at` - `float`/`epoch` - the time when the opertaion has ended;
|
|
2421
2775
|
- `:acq_id` - `string` - refused acquirer identifier;
|
|
2422
2776
|
- `:hst_id` - `string` - refused host identifier;
|
|
2423
|
-
- `:rel_key_cnt` - `integer` - released locks count;
|
|
2424
|
-
- `:tch_queue_cnt` - `:integer` - the number of queues from which the concrete host/acquirer was removed;
|
|
2777
|
+
- `:rel_key_cnt` - `integer` - released locks count (write locks and read locks);
|
|
2778
|
+
- `:tch_queue_cnt` - `:integer` - the number of queues (write and read lock request queues) from which the concrete host/acquirer was removed;
|
|
2425
2779
|
|
|
2426
2780
|
---
|
|
2427
2781
|
|
|
@@ -2449,12 +2803,6 @@ Detalized event semantics and payload structure:
|
|
|
2449
2803
|
- an ability to release all locks and all requests of the concrete acquirer id or host id (or both in validation-orianted combination);
|
|
2450
2804
|
- 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);
|
|
2451
2805
|
- a convenient way to mark any `lock` invocation as "non-instrumentable" / "non-loggable" (as an alternative to `VoidNotifier` and to `VoidLogger`);
|
|
2452
|
-
- `Read`/`Write` semantics: you can mark your locks as `read` or `write` lock in order to simulate `read`/`write` lock behavior:
|
|
2453
|
-
- `read` - watis - `write`;
|
|
2454
|
-
- `read` - not waits - `read`;
|
|
2455
|
-
- `write` - waits - `read`;
|
|
2456
|
-
- `write` - waits - `write`;
|
|
2457
|
-
- **write** mode is a default behavior for all RQL locks;
|
|
2458
2806
|
- `README`-section `Issues with other Libs` about issues in other libraries that can broke RQL features:
|
|
2459
2807
|
- `Sentry`'s' bugs with Ractors:
|
|
2460
2808
|
- `Sentry`-OpenTelemetry problems with its global mutexes and non-sharable objects that is not accessible from the ractors (sentry tyies to
|