redis_queued_locks 1.16.2 → 1.18.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 +281 -0
- data/.claude/rules/acquirer.md +81 -0
- data/.claude/rules/arguments.md +60 -0
- data/.claude/rules/git.md +46 -0
- data/.claude/rules/logic.md +63 -0
- data/.claude/rules/swarm.md +120 -0
- data/.claude/rules/tests.md +48 -0
- data/.claude/rules/type-checking.md +50 -0
- data/.claude/rules/visitors.md +69 -0
- data/.rubocop.rbs.yml +31 -0
- data/.rubocop.yml +3 -3
- data/.ruby-version +1 -1
- data/CHANGELOG.md +80 -0
- data/CLAUDE.md +36 -0
- data/README.md +367 -82
- data/Rakefile +31 -2
- data/github_ci/.keep +0 -0
- data/lib/redis_queued_locks/acquirer/acquire_lock/delay_execution.rb +2 -2
- 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 -132
- 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 +33 -9
- data/lib/redis_queued_locks/acquirer/acquire_lock.rb +73 -24
- 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 -4
- data/lib/redis_queued_locks/acquirer/lock_series_poc/log_visitor.rb +18 -7
- data/lib/redis_queued_locks/acquirer/lock_series_poc.rb +126 -29
- 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 +274 -158
- data/lib/redis_queued_locks/config/dsl.rb +4 -10
- data/lib/redis_queued_locks/resource.rb +117 -9
- data/lib/redis_queued_locks/swarm/flush_zombies.rb +46 -6
- data/lib/redis_queued_locks/swarm/supervisor.rb +1 -1
- data/lib/redis_queued_locks/swarm/swarm_element/isolated.rb +166 -44
- data/lib/redis_queued_locks/swarm/swarm_element/threaded.rb +45 -47
- 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/redis_queued_locks.gemspec +1 -1
- 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 +22 -2
- data/sig/redis_queued_locks/resource.rbs +9 -2
- data/sig/redis_queued_locks/swarm/flush_zombies.rbs +1 -1
- data/sig/redis_queued_locks/swarm/swarm_element/isolated.rbs +13 -5
- data/sig/redis_queued_locks/swarm/swarm_element/threaded.rbs +6 -6
- data/sig/redis_queued_locks/swarm/zombie_info.rbs +5 -0
- metadata +17 -5
- data/github_ci/ruby3.3.gemfile +0 -17
- data/github_ci/ruby3.3.gemfile.lock +0 -216
|
@@ -11,11 +11,12 @@ module RedisQueuedLocks::Acquirer::AcquireLock::YieldExpire::LogVisitor
|
|
|
11
11
|
# @param acquirer_id [String]
|
|
12
12
|
# @param host_id [String]
|
|
13
13
|
# @param access_strategy [Symbol]
|
|
14
|
+
# @param rw_mode [Symbol]
|
|
14
15
|
# @return [void]
|
|
15
16
|
#
|
|
16
17
|
# @api private
|
|
17
18
|
# @since 1.7.0
|
|
18
|
-
# @version 1.
|
|
19
|
+
# @version 1.18.0
|
|
19
20
|
def expire_lock(
|
|
20
21
|
logger,
|
|
21
22
|
log_sampled,
|
|
@@ -23,7 +24,8 @@ module RedisQueuedLocks::Acquirer::AcquireLock::YieldExpire::LogVisitor
|
|
|
23
24
|
queue_ttl,
|
|
24
25
|
acquirer_id,
|
|
25
26
|
host_id,
|
|
26
|
-
access_strategy
|
|
27
|
+
access_strategy,
|
|
28
|
+
rw_mode
|
|
27
29
|
)
|
|
28
30
|
return unless log_sampled
|
|
29
31
|
|
|
@@ -33,7 +35,8 @@ module RedisQueuedLocks::Acquirer::AcquireLock::YieldExpire::LogVisitor
|
|
|
33
35
|
"queue_ttl => #{queue_ttl} " \
|
|
34
36
|
"acq_id => '#{acquirer_id}' " \
|
|
35
37
|
"hst_id => '#{host_id}' " \
|
|
36
|
-
"acs_strat => '#{access_strategy}'"
|
|
38
|
+
"acs_strat => '#{access_strategy}' " \
|
|
39
|
+
"rw_mode => '#{rw_mode}'"
|
|
37
40
|
end rescue nil
|
|
38
41
|
end
|
|
39
42
|
|
|
@@ -45,11 +48,12 @@ module RedisQueuedLocks::Acquirer::AcquireLock::YieldExpire::LogVisitor
|
|
|
45
48
|
# @param acquirer_id [String]
|
|
46
49
|
# @param host_id [String]
|
|
47
50
|
# @param access_strategy [Symbol]
|
|
51
|
+
# @param rw_mode [Symbol]
|
|
48
52
|
# @return [void]
|
|
49
53
|
#
|
|
50
54
|
# @api private
|
|
51
55
|
# @since 1.7.0
|
|
52
|
-
# @version 1.
|
|
56
|
+
# @version 1.18.0
|
|
53
57
|
def decrease_lock(
|
|
54
58
|
logger,
|
|
55
59
|
log_sampled,
|
|
@@ -58,7 +62,8 @@ module RedisQueuedLocks::Acquirer::AcquireLock::YieldExpire::LogVisitor
|
|
|
58
62
|
queue_ttl,
|
|
59
63
|
acquirer_id,
|
|
60
64
|
host_id,
|
|
61
|
-
access_strategy
|
|
65
|
+
access_strategy,
|
|
66
|
+
rw_mode
|
|
62
67
|
)
|
|
63
68
|
return unless log_sampled
|
|
64
69
|
|
|
@@ -69,7 +74,8 @@ module RedisQueuedLocks::Acquirer::AcquireLock::YieldExpire::LogVisitor
|
|
|
69
74
|
"queue_ttl => #{queue_ttl} " \
|
|
70
75
|
"acq_id => '#{acquirer_id}' " \
|
|
71
76
|
"hst_id => '#{host_id}' " \
|
|
72
|
-
"acs_strat => '#{access_strategy}'"
|
|
77
|
+
"acs_strat => '#{access_strategy}' " \
|
|
78
|
+
"rw_mode => '#{rw_mode}'"
|
|
73
79
|
end rescue nil
|
|
74
80
|
end
|
|
75
81
|
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# @api private
|
|
4
4
|
# @since 1.3.0
|
|
5
|
-
# @version 1.
|
|
5
|
+
# @version 1.18.0
|
|
6
6
|
module RedisQueuedLocks::Acquirer::AcquireLock::YieldExpire
|
|
7
7
|
require_relative 'yield_expire/log_visitor'
|
|
8
8
|
|
|
@@ -18,6 +18,11 @@ module RedisQueuedLocks::Acquirer::AcquireLock::YieldExpire
|
|
|
18
18
|
# @param redis [RedisClient] Redis connection.
|
|
19
19
|
# @param logger [::Logger,#debug] Logger object.
|
|
20
20
|
# @param lock_key [String] Obtained lock key that should be expired.
|
|
21
|
+
# @param lock_readers_key [String] Registry of read lock holders.
|
|
22
|
+
# @param read_lock_key [String] Read lock data of the current acquirer.
|
|
23
|
+
# @param rw_mode [Symbol]
|
|
24
|
+
# - The mode of the obtained lock (`:read` or `:write`);
|
|
25
|
+
# - `:read` => only the read lock of the current acquirer is released/decreased;
|
|
21
26
|
# @param acquirer_id [String] Acquirer identifier.
|
|
22
27
|
# @param host_id [String] Host identifier.
|
|
23
28
|
# @param access_strategy [Symbol] Lock obtaining strategy.
|
|
@@ -38,12 +43,15 @@ module RedisQueuedLocks::Acquirer::AcquireLock::YieldExpire
|
|
|
38
43
|
#
|
|
39
44
|
# @api private
|
|
40
45
|
# @since 1.3.0
|
|
41
|
-
# @version 1.
|
|
46
|
+
# @version 1.18.0
|
|
42
47
|
# rubocop:disable Metrics/MethodLength
|
|
43
48
|
def yield_expire(
|
|
44
49
|
redis,
|
|
45
50
|
logger,
|
|
46
51
|
lock_key,
|
|
52
|
+
lock_readers_key,
|
|
53
|
+
read_lock_key,
|
|
54
|
+
rw_mode,
|
|
47
55
|
acquirer_id,
|
|
48
56
|
host_id,
|
|
49
57
|
access_strategy,
|
|
@@ -87,7 +95,7 @@ module RedisQueuedLocks::Acquirer::AcquireLock::YieldExpire
|
|
|
87
95
|
acquirer_id,
|
|
88
96
|
host_id,
|
|
89
97
|
meta,
|
|
90
|
-
&block
|
|
98
|
+
&block
|
|
91
99
|
)
|
|
92
100
|
else
|
|
93
101
|
yield
|
|
@@ -97,9 +105,17 @@ module RedisQueuedLocks::Acquirer::AcquireLock::YieldExpire
|
|
|
97
105
|
if should_expire # TODO: comment all cases/examples when should_expire is true
|
|
98
106
|
LogVisitor.expire_lock(
|
|
99
107
|
logger, log_sampled, lock_key,
|
|
100
|
-
queue_ttl, acquirer_id, host_id, access_strategy
|
|
108
|
+
queue_ttl, acquirer_id, host_id, access_strategy, rw_mode
|
|
101
109
|
)
|
|
102
|
-
|
|
110
|
+
if rw_mode == :read
|
|
111
|
+
# NOTE: release only the read lock of the current acquirer (other readers keep working)
|
|
112
|
+
redis.multi do |transact|
|
|
113
|
+
transact.call('ZREM', lock_readers_key, acquirer_id)
|
|
114
|
+
transact.call('DEL', read_lock_key)
|
|
115
|
+
end
|
|
116
|
+
else
|
|
117
|
+
redis.call('EXPIRE', lock_key, '0')
|
|
118
|
+
end
|
|
103
119
|
elsif should_decrease # TODO: comment all cases/examples when should_expire is true
|
|
104
120
|
finish_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :millisecond)
|
|
105
121
|
# @type var initial_time: Integer
|
|
@@ -110,11 +126,19 @@ module RedisQueuedLocks::Acquirer::AcquireLock::YieldExpire
|
|
|
110
126
|
if decreased_ttl > 0
|
|
111
127
|
LogVisitor.decrease_lock(
|
|
112
128
|
logger, log_sampled, lock_key,
|
|
113
|
-
decreased_ttl, queue_ttl, acquirer_id, host_id, access_strategy
|
|
129
|
+
decreased_ttl, queue_ttl, acquirer_id, host_id, access_strategy, rw_mode
|
|
114
130
|
)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
131
|
+
if rw_mode == :read
|
|
132
|
+
# NOTE:
|
|
133
|
+
# - decrease the read lock expiration of the current acquirer;
|
|
134
|
+
# - XX => do nothing if the read lock is already expired and removed from the registry;
|
|
135
|
+
# - the read lock data TTL is not decreased (it is not used without the read lock);
|
|
136
|
+
redis.call('ZADD', lock_readers_key, 'XX', 'INCR', -decreased_ttl, acquirer_id)
|
|
137
|
+
else
|
|
138
|
+
# NOTE:# NOTE: EVAL signature -> <lua script>, (number of keys), *(keys), *(arguments)
|
|
139
|
+
redis.call('EVAL', DECREASE_LOCK_PTTL, 1, lock_key, decreased_ttl)
|
|
140
|
+
# TODO: upload scripts to the redis
|
|
141
|
+
end
|
|
118
142
|
end
|
|
119
143
|
end
|
|
120
144
|
end
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
# @api private
|
|
4
4
|
# @since 1.0.0
|
|
5
|
-
# @version 1.
|
|
5
|
+
# @version 1.18.0
|
|
6
6
|
# rubocop:disable Metrics/ModuleLength
|
|
7
7
|
# rubocop:disable Metrics/MethodLength
|
|
8
8
|
# rubocop:disable Metrics/ClassLength
|
|
9
9
|
# rubocop:disable Metrics/BlockNesting
|
|
10
|
-
# rubocop:disable Style/IfInsideElse
|
|
11
10
|
module RedisQueuedLocks::Acquirer::AcquireLock
|
|
12
11
|
require_relative 'acquire_lock/log_visitor'
|
|
13
12
|
require_relative 'acquire_lock/instr_visitor'
|
|
@@ -97,7 +96,15 @@ module RedisQueuedLocks::Acquirer::AcquireLock
|
|
|
97
96
|
# - `:wait_for_lock`;
|
|
98
97
|
# - `:dead_locking`;
|
|
99
98
|
# @option read_write_mode [Symbol]
|
|
100
|
-
# -
|
|
99
|
+
# - `:write` (default) - exclusive lock: waits for the write lock and for all read locks;
|
|
100
|
+
# - `:read` - shared lock: waits for the write lock only (read locks do not block each other);
|
|
101
|
+
# - `:queued` access strategy orders read and write requests in FIFO between modes
|
|
102
|
+
# (read request waits for earlier write requests, write request waits for earlier
|
|
103
|
+
# read requests and for earlier write requests);
|
|
104
|
+
# - `:meta` of the read lock is stored in the read lock data of the acquirer;
|
|
105
|
+
# - same-process conflicts: read-to-write lock upgrade fails with
|
|
106
|
+
# `RedisQueuedLocks::ConflictLockObtainError` for any conflict strategy except
|
|
107
|
+
# `:wait_for_lock` (it waits for the read lock expiration);
|
|
101
108
|
# @option access_strategy [Symbol]
|
|
102
109
|
# - The way in which the lock will be obtained;
|
|
103
110
|
# - By default it uses `:queued` strategy;
|
|
@@ -159,7 +166,7 @@ module RedisQueuedLocks::Acquirer::AcquireLock
|
|
|
159
166
|
#
|
|
160
167
|
# @api private
|
|
161
168
|
# @since 1.0.0
|
|
162
|
-
# @version 1.
|
|
169
|
+
# @version 1.18.0
|
|
163
170
|
def acquire_lock(
|
|
164
171
|
redis,
|
|
165
172
|
lock_name,
|
|
@@ -207,6 +214,24 @@ module RedisQueuedLocks::Acquirer::AcquireLock
|
|
|
207
214
|
)
|
|
208
215
|
end
|
|
209
216
|
|
|
217
|
+
# Step 0.1 (RW): prevent :read_write_mode incompatabilities
|
|
218
|
+
if read_write_mode != :read && read_write_mode != :write
|
|
219
|
+
raise(
|
|
220
|
+
RedisQueuedLocks::ArgumentError,
|
|
221
|
+
"`:read_write_mode` argument should be `:read` or `:write`, " \
|
|
222
|
+
"got #{read_write_mode.inspect}."
|
|
223
|
+
)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Step 0.1 (RW): prevent read lock incompatabilities
|
|
227
|
+
# - read lock expiration time is calculated from the lock ttl;
|
|
228
|
+
if read_write_mode == :read && (!ttl.is_a?(::Integer) || ttl <= 0)
|
|
229
|
+
raise(
|
|
230
|
+
RedisQueuedLocks::ArgumentError,
|
|
231
|
+
"`:ttl` argument should be a positive Integer for read locks, got #{ttl.inspect}."
|
|
232
|
+
)
|
|
233
|
+
end
|
|
234
|
+
|
|
210
235
|
# Step 0.2: prevent :meta incompatabiltiies (structure)
|
|
211
236
|
if meta.is_a?(::Hash) && (meta.any? do |key, _value|
|
|
212
237
|
key == 'acq_id' ||
|
|
@@ -219,13 +244,14 @@ module RedisQueuedLocks::Acquirer::AcquireLock
|
|
|
219
244
|
key == 'spc_cnt' ||
|
|
220
245
|
key == 'l_spc_ext_ini_ttl' ||
|
|
221
246
|
key == 'l_spc_ext_ts' ||
|
|
222
|
-
key == 'l_spc_ts'
|
|
247
|
+
key == 'l_spc_ts' ||
|
|
248
|
+
key == 'rw_mode'
|
|
223
249
|
end)
|
|
224
250
|
raise(
|
|
225
251
|
RedisQueuedLocks::ArgumentError,
|
|
226
252
|
'`:meta` keys can not overlap reserved lock data keys ' \
|
|
227
253
|
'"acq_id", "hst_id", "ts", "ini_ttl", "lock_key", "rem_ttl", "spc_cnt", ' \
|
|
228
|
-
'"spc_ext_ttl", "l_spc_ext_ini_ttl", "l_spc_ext_ts", "l_spc_ts"'
|
|
254
|
+
'"spc_ext_ttl", "l_spc_ext_ini_ttl", "l_spc_ext_ts", "l_spc_ts", "rw_mode"'
|
|
229
255
|
)
|
|
230
256
|
end
|
|
231
257
|
|
|
@@ -247,8 +273,10 @@ module RedisQueuedLocks::Acquirer::AcquireLock
|
|
|
247
273
|
lock_key = RedisQueuedLocks::Resource.prepare_lock_key(lock_name)
|
|
248
274
|
lock_key_queue = RedisQueuedLocks::Resource.prepare_lock_queue(lock_name)
|
|
249
275
|
|
|
276
|
+
# NOTE: (RW) write lock requests use the classic lock queue (lock_key_queue)
|
|
250
277
|
read_lock_key_queue = RedisQueuedLocks::Resource.prepare_read_lock_queue(lock_name)
|
|
251
|
-
|
|
278
|
+
lock_readers_key = RedisQueuedLocks::Resource.prepare_lock_readers(lock_name)
|
|
279
|
+
read_lock_key = RedisQueuedLocks::Resource.prepare_read_lock_key(lock_name, acquirer_id)
|
|
252
280
|
|
|
253
281
|
acquirer_position = RedisQueuedLocks::Resource.calc_initial_acquirer_position
|
|
254
282
|
|
|
@@ -286,7 +314,6 @@ module RedisQueuedLocks::Acquirer::AcquireLock
|
|
|
286
314
|
read_write_mode,
|
|
287
315
|
lock_key_queue,
|
|
288
316
|
read_lock_key_queue,
|
|
289
|
-
write_lock_key_queue,
|
|
290
317
|
queue_ttl,
|
|
291
318
|
acquirer_id,
|
|
292
319
|
host_id,
|
|
@@ -298,7 +325,7 @@ module RedisQueuedLocks::Acquirer::AcquireLock
|
|
|
298
325
|
|
|
299
326
|
LogVisitor.start_lock_obtaining(
|
|
300
327
|
logger, log_sampled, lock_key,
|
|
301
|
-
queue_ttl, acquirer_id, host_id, access_strategy
|
|
328
|
+
queue_ttl, acquirer_id, host_id, access_strategy, read_write_mode
|
|
302
329
|
)
|
|
303
330
|
|
|
304
331
|
# Step 2: try to lock with timeout
|
|
@@ -318,7 +345,7 @@ module RedisQueuedLocks::Acquirer::AcquireLock
|
|
|
318
345
|
|
|
319
346
|
LogVisitor.start_try_to_lock_cycle(
|
|
320
347
|
logger, log_sampled, lock_key,
|
|
321
|
-
queue_ttl, acquirer_id, host_id, access_strategy
|
|
348
|
+
queue_ttl, acquirer_id, host_id, access_strategy, read_write_mode
|
|
322
349
|
)
|
|
323
350
|
|
|
324
351
|
# Step 2.X: check the actual score: is it in queue ttl limit or not?
|
|
@@ -328,7 +355,7 @@ module RedisQueuedLocks::Acquirer::AcquireLock
|
|
|
328
355
|
|
|
329
356
|
LogVisitor.dead_score_reached__reset_acquirer_position(
|
|
330
357
|
logger, log_sampled, lock_key,
|
|
331
|
-
queue_ttl, acquirer_id, host_id, access_strategy
|
|
358
|
+
queue_ttl, acquirer_id, host_id, access_strategy, read_write_mode
|
|
332
359
|
)
|
|
333
360
|
end
|
|
334
361
|
|
|
@@ -342,7 +369,8 @@ module RedisQueuedLocks::Acquirer::AcquireLock
|
|
|
342
369
|
read_write_mode,
|
|
343
370
|
lock_key_queue,
|
|
344
371
|
read_lock_key_queue,
|
|
345
|
-
|
|
372
|
+
lock_readers_key,
|
|
373
|
+
read_lock_key,
|
|
346
374
|
acquirer_id,
|
|
347
375
|
host_id,
|
|
348
376
|
acquirer_position,
|
|
@@ -376,10 +404,10 @@ module RedisQueuedLocks::Acquirer::AcquireLock
|
|
|
376
404
|
# instrumetnation: (reentrant lock with ttl extension)
|
|
377
405
|
LogVisitor.extendable_reentrant_lock_obtained(
|
|
378
406
|
logger, log_sampled, result[:lock_key],
|
|
379
|
-
queue_ttl, acquirer_id, host_id, acq_time, access_strategy
|
|
407
|
+
queue_ttl, acquirer_id, host_id, acq_time, access_strategy, read_write_mode
|
|
380
408
|
)
|
|
381
409
|
InstrVisitor.extendable_reentrant_lock_obtained(
|
|
382
|
-
instrumenter, instr_sampled, result[:lock_key],
|
|
410
|
+
instrumenter, instr_sampled, result[:lock_key], read_write_mode,
|
|
383
411
|
result[:ttl], result[:acq_id], result[:hst_id], result[:ts], acq_time,
|
|
384
412
|
instrument
|
|
385
413
|
)
|
|
@@ -387,10 +415,10 @@ module RedisQueuedLocks::Acquirer::AcquireLock
|
|
|
387
415
|
# instrumetnation: (reentrant lock without ttl extension)
|
|
388
416
|
LogVisitor.reentrant_lock_obtained(
|
|
389
417
|
logger, log_sampled, result[:lock_key],
|
|
390
|
-
queue_ttl, acquirer_id, host_id, acq_time, access_strategy
|
|
418
|
+
queue_ttl, acquirer_id, host_id, acq_time, access_strategy, read_write_mode
|
|
391
419
|
)
|
|
392
420
|
InstrVisitor.reentrant_lock_obtained(
|
|
393
|
-
instrumenter, instr_sampled, result[:lock_key],
|
|
421
|
+
instrumenter, instr_sampled, result[:lock_key], read_write_mode,
|
|
394
422
|
result[:ttl], result[:acq_id], result[:hst_id], result[:ts], acq_time,
|
|
395
423
|
instrument
|
|
396
424
|
)
|
|
@@ -399,10 +427,10 @@ module RedisQueuedLocks::Acquirer::AcquireLock
|
|
|
399
427
|
# NOTE: classic is: acq_process[:result][:process] == :lock_obtaining
|
|
400
428
|
LogVisitor.lock_obtained(
|
|
401
429
|
logger, log_sampled, result[:lock_key],
|
|
402
|
-
queue_ttl, acquirer_id, host_id, acq_time, access_strategy
|
|
430
|
+
queue_ttl, acquirer_id, host_id, acq_time, access_strategy, read_write_mode
|
|
403
431
|
)
|
|
404
432
|
InstrVisitor.lock_obtained(
|
|
405
|
-
instrumenter, instr_sampled, result[:lock_key],
|
|
433
|
+
instrumenter, instr_sampled, result[:lock_key], read_write_mode,
|
|
406
434
|
result[:ttl], result[:acq_id], result[:hst_id], result[:ts], acq_time,
|
|
407
435
|
instrument
|
|
408
436
|
)
|
|
@@ -417,6 +445,8 @@ module RedisQueuedLocks::Acquirer::AcquireLock
|
|
|
417
445
|
ttl: result[:ttl],
|
|
418
446
|
process: result[:process]
|
|
419
447
|
}
|
|
448
|
+
# NOTE: (RW) the mode of the really held lock (internal, used for the lock release)
|
|
449
|
+
acq_process[:held_rw_mode] = result[:rw_mode]
|
|
420
450
|
acq_process[:acquired] = true
|
|
421
451
|
acq_process[:should_try] = false
|
|
422
452
|
acq_process[:acq_time] = acq_time
|
|
@@ -425,7 +455,8 @@ module RedisQueuedLocks::Acquirer::AcquireLock
|
|
|
425
455
|
# @type var result: Symbol
|
|
426
456
|
|
|
427
457
|
# Step 2.2: failed to acquire. anylize each case and act in accordance
|
|
428
|
-
|
|
458
|
+
case acq_process[:result]
|
|
459
|
+
when :fail_fast_no_try # Step 2.2.a: fail without try
|
|
429
460
|
acq_process[:should_try] = false
|
|
430
461
|
|
|
431
462
|
if raise_errors
|
|
@@ -434,7 +465,7 @@ module RedisQueuedLocks::Acquirer::AcquireLock
|
|
|
434
465
|
"Lock \"#{lock_key}\" is already obtained."
|
|
435
466
|
)
|
|
436
467
|
end
|
|
437
|
-
|
|
468
|
+
when :conflict_dead_lock # Step 2.2.b: fail after dead lock
|
|
438
469
|
acq_process[:tries] += 1
|
|
439
470
|
acq_process[:should_try] = false
|
|
440
471
|
acq_process[:result] = :conflict_dead_lock
|
|
@@ -447,6 +478,21 @@ module RedisQueuedLocks::Acquirer::AcquireLock
|
|
|
447
478
|
"that is already acquired by the current acquirer (acq_id: \"#{acquirer_id}\")."
|
|
448
479
|
)
|
|
449
480
|
end
|
|
481
|
+
when :conflict_lock_upgrade # Step 2.2.c (RW): fail after lock upgrade attempt
|
|
482
|
+
acq_process[:tries] += 1
|
|
483
|
+
acq_process[:should_try] = false
|
|
484
|
+
acq_process[:result] = :conflict_lock_upgrade
|
|
485
|
+
acq_dequeue.call
|
|
486
|
+
|
|
487
|
+
if raise_errors
|
|
488
|
+
raise(
|
|
489
|
+
RedisQueuedLocks::ConflictLockObtainError,
|
|
490
|
+
"Lock Conflict: trying to acquire the write lock \"#{lock_key}\" " \
|
|
491
|
+
"while the current acquirer holds its read lock (acq_id: \"#{acquirer_id}\"): " \
|
|
492
|
+
"read-to-write lock upgrade is not supported " \
|
|
493
|
+
"(conflict strategy: :#{conflict_strategy})."
|
|
494
|
+
)
|
|
495
|
+
end
|
|
450
496
|
else
|
|
451
497
|
acq_process[:tries] += 1 # Step RETRY: possible retry case
|
|
452
498
|
|
|
@@ -511,6 +557,9 @@ module RedisQueuedLocks::Acquirer::AcquireLock
|
|
|
511
557
|
redis,
|
|
512
558
|
logger,
|
|
513
559
|
lock_key,
|
|
560
|
+
lock_readers_key,
|
|
561
|
+
read_lock_key,
|
|
562
|
+
acq_process[:held_rw_mode],
|
|
514
563
|
acquirer_id,
|
|
515
564
|
host_id,
|
|
516
565
|
access_strategy,
|
|
@@ -538,6 +587,7 @@ module RedisQueuedLocks::Acquirer::AcquireLock
|
|
|
538
587
|
instrumenter,
|
|
539
588
|
instr_sampled,
|
|
540
589
|
acq_process[:lock_info][:lock_key],
|
|
590
|
+
read_write_mode,
|
|
541
591
|
acq_process[:lock_info][:ttl],
|
|
542
592
|
acq_process[:lock_info][:acq_id],
|
|
543
593
|
acq_process[:lock_info][:hst_id],
|
|
@@ -552,6 +602,7 @@ module RedisQueuedLocks::Acquirer::AcquireLock
|
|
|
552
602
|
instrumenter,
|
|
553
603
|
instr_sampled,
|
|
554
604
|
acq_process[:lock_info][:lock_key],
|
|
605
|
+
read_write_mode,
|
|
555
606
|
acq_process[:lock_info][:ttl],
|
|
556
607
|
acq_process[:lock_info][:acq_id],
|
|
557
608
|
acq_process[:lock_info][:hst_id],
|
|
@@ -563,15 +614,14 @@ module RedisQueuedLocks::Acquirer::AcquireLock
|
|
|
563
614
|
end
|
|
564
615
|
end
|
|
565
616
|
else
|
|
566
|
-
# rubocop:disable Layout/LineLength
|
|
567
617
|
{ ok: true, result: acq_process[:lock_info] } #: { ok: bool, result: Hash[Symbol,untyped] }
|
|
568
|
-
# rubocop:enable Layout/LineLength
|
|
569
618
|
end
|
|
570
619
|
else
|
|
571
620
|
if acq_process[:result] != :retry_limit_reached &&
|
|
572
621
|
acq_process[:result] != :fail_fast_no_try &&
|
|
573
622
|
acq_process[:result] != :fail_fast_after_try &&
|
|
574
|
-
acq_process[:result] != :conflict_dead_lock
|
|
623
|
+
acq_process[:result] != :conflict_dead_lock &&
|
|
624
|
+
acq_process[:result] != :conflict_lock_upgrade
|
|
575
625
|
# NOTE: we have only two situations if lock is not acquired without explicit failures:
|
|
576
626
|
# - time limit is reached;
|
|
577
627
|
# - retry count limit is reached;
|
|
@@ -588,4 +638,3 @@ end
|
|
|
588
638
|
# rubocop:enable Metrics/MethodLength
|
|
589
639
|
# rubocop:enable Metrics/ClassLength
|
|
590
640
|
# rubocop:enable Metrics/BlockNesting
|
|
591
|
-
# rubocop:enable Style/IfInsideElse
|
|
@@ -22,7 +22,7 @@ module RedisQueuedLocks::Acquirer::ClearDeadRequests
|
|
|
22
22
|
#
|
|
23
23
|
# @api private
|
|
24
24
|
# @since 1.0.0
|
|
25
|
-
# @version 1.
|
|
25
|
+
# @version 1.18.0
|
|
26
26
|
def clear_dead_requests(
|
|
27
27
|
redis_client,
|
|
28
28
|
scan_size,
|
|
@@ -66,6 +66,7 @@ module RedisQueuedLocks::Acquirer::ClearDeadRequests
|
|
|
66
66
|
#
|
|
67
67
|
# @api private
|
|
68
68
|
# @since 1.0.0
|
|
69
|
+
# @version 1.18.0
|
|
69
70
|
def each_lock_queue(redis_client, scan_size, &block)
|
|
70
71
|
redis_client.scan(
|
|
71
72
|
'MATCH',
|
|
@@ -73,6 +74,13 @@ module RedisQueuedLocks::Acquirer::ClearDeadRequests
|
|
|
73
74
|
count: scan_size,
|
|
74
75
|
&block
|
|
75
76
|
)
|
|
77
|
+
# NOTE: (RW) read lock queues
|
|
78
|
+
redis_client.scan(
|
|
79
|
+
'MATCH',
|
|
80
|
+
RedisQueuedLocks::Resource::READ_LOCK_QUEUE_PATTERN,
|
|
81
|
+
count: scan_size,
|
|
82
|
+
&block
|
|
83
|
+
)
|
|
76
84
|
end
|
|
77
85
|
end
|
|
78
86
|
end
|