redis_queued_locks 1.22.0 → 1.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.claude/project-overview.md +88 -36
- data/.claude/rules/acquirer.md +11 -7
- data/.claude/rules/arguments.md +2 -2
- data/.claude/rules/logic.md +5 -4
- data/.claude/rules/tests.md +4 -4
- data/.claude/rules/type-checking.md +2 -3
- data/.claude/rules/visitors.md +8 -6
- data/.claude/skills/rql-code-review/SKILL.md +25 -20
- data/.ruby-version +1 -1
- data/CHANGELOG.md +24 -1
- data/CLAUDE.md +5 -4
- data/README.md +247 -96
- data/lib/redis_queued_locks/acquirer/acquire_lock/dequeue_from_lock_queue.rb +4 -4
- data/lib/redis_queued_locks/acquirer/acquire_lock/try_to_lock.rb +582 -276
- data/lib/redis_queued_locks/acquirer/acquire_lock.rb +26 -26
- data/lib/redis_queued_locks/acquirer/extend_lock_ttl.rb +13 -13
- data/lib/redis_queued_locks/acquirer/lock_series/instr_visitor.rb +243 -0
- data/lib/redis_queued_locks/acquirer/lock_series/log_visitor.rb +472 -0
- data/lib/redis_queued_locks/acquirer/lock_series/release_lock_series.rb +181 -0
- data/lib/redis_queued_locks/acquirer/lock_series/try_to_lock_series.rb +1051 -0
- data/lib/redis_queued_locks/acquirer/lock_series.rb +948 -0
- data/lib/redis_queued_locks/acquirer.rb +1 -1
- data/lib/redis_queued_locks/client.rb +55 -25
- data/lib/redis_queued_locks/version.rb +2 -2
- data/redis_queued_locks.gemspec +21 -14
- data/sig/redis_queued_locks/acquirer/acquire_lock/dequeue_from_lock_queue.rbs +1 -1
- data/sig/redis_queued_locks/acquirer/acquire_lock/try_to_lock.rbs +106 -5
- data/sig/redis_queued_locks/acquirer/acquire_lock.rbs +1 -1
- data/sig/redis_queued_locks/acquirer/extend_lock_ttl.rbs +2 -2
- data/sig/redis_queued_locks/acquirer/lock_series/instr_visitor.rbs +102 -0
- data/sig/redis_queued_locks/acquirer/lock_series/log_visitor.rbs +154 -0
- data/sig/redis_queued_locks/acquirer/lock_series/release_lock_series.rbs +37 -0
- data/sig/redis_queued_locks/acquirer/lock_series/try_to_lock_series.rbs +198 -0
- data/sig/redis_queued_locks/acquirer/lock_series.rbs +106 -0
- data/sig/redis_queued_locks/client.rbs +70 -3
- metadata +32 -11
- data/lib/redis_queued_locks/acquirer/lock_series_poc/instr_visitor.rb +0 -61
- data/lib/redis_queued_locks/acquirer/lock_series_poc/log_visitor.rb +0 -104
- data/lib/redis_queued_locks/acquirer/lock_series_poc.rb +0 -513
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# @api private
|
|
4
|
+
# @since 1.23.0
|
|
5
|
+
# rubocop:disable Metrics/ModuleLength
|
|
6
|
+
module RedisQueuedLocks::Acquirer::LockSeries::LogVisitor
|
|
7
|
+
# rubocop:disable Metrics/ClassLength
|
|
8
|
+
class << self
|
|
9
|
+
# @param logger [::Logger,#debug]
|
|
10
|
+
# @param log_sampled [Boolean]
|
|
11
|
+
# @param lock_keys [Array<String>]
|
|
12
|
+
# @param queue_ttl [Integer]
|
|
13
|
+
# @param acquirer_id [String]
|
|
14
|
+
# @param host_id [String]
|
|
15
|
+
# @param access_strategy [Symbol]
|
|
16
|
+
# @param rw_mode [Symbol] The mode of the lock series (`:read` or `:write`).
|
|
17
|
+
# @param priority [Integer] The lock request priority of the series.
|
|
18
|
+
# @return [void]
|
|
19
|
+
#
|
|
20
|
+
# @api private
|
|
21
|
+
# @since 1.23.0
|
|
22
|
+
def start_lock_series_obtaining(
|
|
23
|
+
logger,
|
|
24
|
+
log_sampled,
|
|
25
|
+
lock_keys,
|
|
26
|
+
queue_ttl,
|
|
27
|
+
acquirer_id,
|
|
28
|
+
host_id,
|
|
29
|
+
access_strategy,
|
|
30
|
+
rw_mode,
|
|
31
|
+
priority
|
|
32
|
+
)
|
|
33
|
+
return unless log_sampled
|
|
34
|
+
|
|
35
|
+
logger.debug do
|
|
36
|
+
"[redis_queued_locks.start_lock_series_obtaining] " \
|
|
37
|
+
"lock_keys => '#{lock_keys.inspect}' " \
|
|
38
|
+
"queue_ttl => #{queue_ttl} " \
|
|
39
|
+
"acq_id => '#{acquirer_id}' " \
|
|
40
|
+
"hst_id => '#{host_id}' " \
|
|
41
|
+
"acs_strat => '#{access_strategy}' " \
|
|
42
|
+
"rw_mode => '#{rw_mode}' " \
|
|
43
|
+
"priority => #{priority}"
|
|
44
|
+
end rescue nil
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @param logger [::Logger,#debug]
|
|
48
|
+
# @param log_sampled [Boolean]
|
|
49
|
+
# @param lock_keys [Array<String>]
|
|
50
|
+
# @param queue_ttl [Integer]
|
|
51
|
+
# @param acquirer_id [String]
|
|
52
|
+
# @param host_id [String]
|
|
53
|
+
# @param access_strategy [Symbol]
|
|
54
|
+
# @param rw_mode [Symbol] The mode of the lock series (`:read` or `:write`).
|
|
55
|
+
# @param priority [Integer] The lock request priority of the series.
|
|
56
|
+
# @return [void]
|
|
57
|
+
#
|
|
58
|
+
# @api private
|
|
59
|
+
# @since 1.23.0
|
|
60
|
+
def start_try_to_lock_series_cycle(
|
|
61
|
+
logger,
|
|
62
|
+
log_sampled,
|
|
63
|
+
lock_keys,
|
|
64
|
+
queue_ttl,
|
|
65
|
+
acquirer_id,
|
|
66
|
+
host_id,
|
|
67
|
+
access_strategy,
|
|
68
|
+
rw_mode,
|
|
69
|
+
priority
|
|
70
|
+
)
|
|
71
|
+
return unless log_sampled
|
|
72
|
+
|
|
73
|
+
logger.debug do
|
|
74
|
+
"[redis_queued_locks.start_try_to_lock_series_cycle] " \
|
|
75
|
+
"lock_keys => '#{lock_keys.inspect}' " \
|
|
76
|
+
"queue_ttl => #{queue_ttl} " \
|
|
77
|
+
"acq_id => '#{acquirer_id}' " \
|
|
78
|
+
"hst_id => '#{host_id}' " \
|
|
79
|
+
"acs_strat => '#{access_strategy}' " \
|
|
80
|
+
"rw_mode => '#{rw_mode}' " \
|
|
81
|
+
"priority => #{priority}"
|
|
82
|
+
end rescue nil
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# @param logger [::Logger,#debug]
|
|
86
|
+
# @param log_sampled [Boolean]
|
|
87
|
+
# @param lock_keys [Array<String>]
|
|
88
|
+
# @param queue_ttl [Integer]
|
|
89
|
+
# @param acquirer_id [String]
|
|
90
|
+
# @param host_id [String]
|
|
91
|
+
# @param access_strategy [Symbol]
|
|
92
|
+
# @param rw_mode [Symbol] The mode of the lock series (`:read` or `:write`).
|
|
93
|
+
# @param priority [Integer] The lock request priority of the series.
|
|
94
|
+
# @return [void]
|
|
95
|
+
#
|
|
96
|
+
# @api private
|
|
97
|
+
# @since 1.23.0
|
|
98
|
+
def dead_score_reached__reset_series_position(
|
|
99
|
+
logger,
|
|
100
|
+
log_sampled,
|
|
101
|
+
lock_keys,
|
|
102
|
+
queue_ttl,
|
|
103
|
+
acquirer_id,
|
|
104
|
+
host_id,
|
|
105
|
+
access_strategy,
|
|
106
|
+
rw_mode,
|
|
107
|
+
priority
|
|
108
|
+
)
|
|
109
|
+
return unless log_sampled
|
|
110
|
+
|
|
111
|
+
logger.debug do
|
|
112
|
+
"[redis_queued_locks.dead_score_reached__reset_series_position] " \
|
|
113
|
+
"lock_keys => '#{lock_keys.inspect}' " \
|
|
114
|
+
"queue_ttl => #{queue_ttl} " \
|
|
115
|
+
"acq_id => '#{acquirer_id}' " \
|
|
116
|
+
"hst_id => '#{host_id}' " \
|
|
117
|
+
"acs_strat => '#{access_strategy}' " \
|
|
118
|
+
"rw_mode => '#{rw_mode}' " \
|
|
119
|
+
"priority => #{priority}"
|
|
120
|
+
end rescue nil
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# @param logger [::Logger,#debug]
|
|
124
|
+
# @param log_sampled [Boolean]
|
|
125
|
+
# @param lock_key [String] The lock of the series.
|
|
126
|
+
# @param lock_keys [Array<String>] All locks of the series.
|
|
127
|
+
# @param queue_ttl [Integer]
|
|
128
|
+
# @param acquirer_id [String]
|
|
129
|
+
# @param host_id [String]
|
|
130
|
+
# @param acq_time [Numeric]
|
|
131
|
+
# @param access_strategy [Symbol]
|
|
132
|
+
# @param rw_mode [Symbol] The mode of the lock series (`:read` or `:write`).
|
|
133
|
+
# @param priority [Integer] The lock request priority of the series.
|
|
134
|
+
# @return [void]
|
|
135
|
+
#
|
|
136
|
+
# @api private
|
|
137
|
+
# @since 1.23.0
|
|
138
|
+
def lock_series_intermediate_lock_obtained(
|
|
139
|
+
logger,
|
|
140
|
+
log_sampled,
|
|
141
|
+
lock_key,
|
|
142
|
+
lock_keys,
|
|
143
|
+
queue_ttl,
|
|
144
|
+
acquirer_id,
|
|
145
|
+
host_id,
|
|
146
|
+
acq_time,
|
|
147
|
+
access_strategy,
|
|
148
|
+
rw_mode,
|
|
149
|
+
priority
|
|
150
|
+
)
|
|
151
|
+
return unless log_sampled
|
|
152
|
+
|
|
153
|
+
logger.debug do
|
|
154
|
+
"[redis_queued_locks.lock_series_intermediate_lock_obtained] " \
|
|
155
|
+
"lock_key => '#{lock_key}' " \
|
|
156
|
+
"lock_keys => '#{lock_keys.inspect}' " \
|
|
157
|
+
"queue_ttl => #{queue_ttl} " \
|
|
158
|
+
"acq_id => '#{acquirer_id}' " \
|
|
159
|
+
"hst_id => '#{host_id}' " \
|
|
160
|
+
"acs_strat => '#{access_strategy}' " \
|
|
161
|
+
"rw_mode => '#{rw_mode}' " \
|
|
162
|
+
"priority => #{priority} " \
|
|
163
|
+
"acq_time => #{acq_time} (ms)"
|
|
164
|
+
end rescue nil
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# @param logger [::Logger,#debug]
|
|
168
|
+
# @param log_sampled [Boolean]
|
|
169
|
+
# @param lock_key [String] The lock of the series.
|
|
170
|
+
# @param lock_keys [Array<String>] All locks of the series.
|
|
171
|
+
# @param queue_ttl [Integer]
|
|
172
|
+
# @param acquirer_id [String]
|
|
173
|
+
# @param host_id [String]
|
|
174
|
+
# @param acq_time [Numeric]
|
|
175
|
+
# @param access_strategy [Symbol]
|
|
176
|
+
# @param rw_mode [Symbol] The mode of the lock series (`:read` or `:write`).
|
|
177
|
+
# @param priority [Integer] The lock request priority of the series.
|
|
178
|
+
# @return [void]
|
|
179
|
+
#
|
|
180
|
+
# @api private
|
|
181
|
+
# @since 1.23.0
|
|
182
|
+
def lock_series_lock_obtained(
|
|
183
|
+
logger,
|
|
184
|
+
log_sampled,
|
|
185
|
+
lock_key,
|
|
186
|
+
lock_keys,
|
|
187
|
+
queue_ttl,
|
|
188
|
+
acquirer_id,
|
|
189
|
+
host_id,
|
|
190
|
+
acq_time,
|
|
191
|
+
access_strategy,
|
|
192
|
+
rw_mode,
|
|
193
|
+
priority
|
|
194
|
+
)
|
|
195
|
+
return unless log_sampled
|
|
196
|
+
|
|
197
|
+
logger.debug do
|
|
198
|
+
"[redis_queued_locks.lock_series_lock_obtained] " \
|
|
199
|
+
"lock_key => '#{lock_key}' " \
|
|
200
|
+
"lock_keys => '#{lock_keys.inspect}' " \
|
|
201
|
+
"queue_ttl => #{queue_ttl} " \
|
|
202
|
+
"acq_id => '#{acquirer_id}' " \
|
|
203
|
+
"hst_id => '#{host_id}' " \
|
|
204
|
+
"acs_strat => '#{access_strategy}' " \
|
|
205
|
+
"rw_mode => '#{rw_mode}' " \
|
|
206
|
+
"priority => #{priority} " \
|
|
207
|
+
"acq_time => #{acq_time} (ms)"
|
|
208
|
+
end rescue nil
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# @param logger [::Logger,#debug]
|
|
212
|
+
# @param log_sampled [Boolean]
|
|
213
|
+
# @param lock_keys [Array<String>]
|
|
214
|
+
# @param queue_ttl [Integer]
|
|
215
|
+
# @param acquirer_id [String]
|
|
216
|
+
# @param host_id [String]
|
|
217
|
+
# @param acq_time [Numeric]
|
|
218
|
+
# @param access_strategy [Symbol]
|
|
219
|
+
# @param rw_mode [Symbol] The mode of the lock series (`:read` or `:write`).
|
|
220
|
+
# @param priority [Integer] The lock request priority of the series.
|
|
221
|
+
# @param reentrant_lock_keys [Array<String>]
|
|
222
|
+
# The locks of the series that are obtained by the outer logic of the current acquirer.
|
|
223
|
+
# @return [void]
|
|
224
|
+
#
|
|
225
|
+
# @api private
|
|
226
|
+
# @since 1.23.0
|
|
227
|
+
def lock_series_obtained(
|
|
228
|
+
logger,
|
|
229
|
+
log_sampled,
|
|
230
|
+
lock_keys,
|
|
231
|
+
queue_ttl,
|
|
232
|
+
acquirer_id,
|
|
233
|
+
host_id,
|
|
234
|
+
acq_time,
|
|
235
|
+
access_strategy,
|
|
236
|
+
rw_mode,
|
|
237
|
+
priority,
|
|
238
|
+
reentrant_lock_keys
|
|
239
|
+
)
|
|
240
|
+
return unless log_sampled
|
|
241
|
+
|
|
242
|
+
logger.debug do
|
|
243
|
+
"[redis_queued_locks.lock_series_obtained] " \
|
|
244
|
+
"lock_keys => '#{lock_keys.inspect}' " \
|
|
245
|
+
"queue_ttl => #{queue_ttl} " \
|
|
246
|
+
"acq_id => '#{acquirer_id}' " \
|
|
247
|
+
"hst_id => '#{host_id}' " \
|
|
248
|
+
"acs_strat => '#{access_strategy}' " \
|
|
249
|
+
"rw_mode => '#{rw_mode}' " \
|
|
250
|
+
"priority => #{priority} " \
|
|
251
|
+
"acq_time => #{acq_time} (ms) " \
|
|
252
|
+
"reentrant_lock_keys => '#{reentrant_lock_keys.inspect}'"
|
|
253
|
+
end rescue nil
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# @param logger [::Logger,#debug]
|
|
257
|
+
# @param log_sampled [Boolean]
|
|
258
|
+
# @param lock_key [String] The lock that blocks the series.
|
|
259
|
+
# @param lock_keys [Array<String>] All locks of the series.
|
|
260
|
+
# @param queue_ttl [Integer]
|
|
261
|
+
# @param acquirer_id [String]
|
|
262
|
+
# @param host_id [String]
|
|
263
|
+
# @param acq_time [Numeric]
|
|
264
|
+
# @param access_strategy [Symbol]
|
|
265
|
+
# @param rw_mode [Symbol] The mode of the lock series (`:read` or `:write`).
|
|
266
|
+
# @param priority [Integer] The lock request priority of the series.
|
|
267
|
+
# @param result [Symbol] The failure result (`:timeout_reached`, `:retry_limit_reached`, ...).
|
|
268
|
+
# @return [void]
|
|
269
|
+
#
|
|
270
|
+
# @api private
|
|
271
|
+
# @since 1.23.0
|
|
272
|
+
def lock_series_lock_obtaining_failed(
|
|
273
|
+
logger,
|
|
274
|
+
log_sampled,
|
|
275
|
+
lock_key,
|
|
276
|
+
lock_keys,
|
|
277
|
+
queue_ttl,
|
|
278
|
+
acquirer_id,
|
|
279
|
+
host_id,
|
|
280
|
+
acq_time,
|
|
281
|
+
access_strategy,
|
|
282
|
+
rw_mode,
|
|
283
|
+
priority,
|
|
284
|
+
result
|
|
285
|
+
)
|
|
286
|
+
return unless log_sampled
|
|
287
|
+
|
|
288
|
+
logger.debug do
|
|
289
|
+
"[redis_queued_locks.lock_series_lock_obtaining_failed] " \
|
|
290
|
+
"lock_key => '#{lock_key}' " \
|
|
291
|
+
"lock_keys => '#{lock_keys.inspect}' " \
|
|
292
|
+
"queue_ttl => #{queue_ttl} " \
|
|
293
|
+
"acq_id => '#{acquirer_id}' " \
|
|
294
|
+
"hst_id => '#{host_id}' " \
|
|
295
|
+
"acs_strat => '#{access_strategy}' " \
|
|
296
|
+
"rw_mode => '#{rw_mode}' " \
|
|
297
|
+
"priority => #{priority} " \
|
|
298
|
+
"acq_time => #{acq_time} (ms) " \
|
|
299
|
+
"result => '#{result}'"
|
|
300
|
+
end rescue nil
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# @param logger [::Logger,#debug]
|
|
304
|
+
# @param log_sampled [Boolean]
|
|
305
|
+
# @param lock_keys [Array<String>]
|
|
306
|
+
# @param queue_ttl [Integer]
|
|
307
|
+
# @param acquirer_id [String]
|
|
308
|
+
# @param host_id [String]
|
|
309
|
+
# @param acq_time [Numeric]
|
|
310
|
+
# @param access_strategy [Symbol]
|
|
311
|
+
# @param rw_mode [Symbol] The mode of the lock series (`:read` or `:write`).
|
|
312
|
+
# @param priority [Integer] The lock request priority of the series.
|
|
313
|
+
# @param result [Symbol] The failure result (`:timeout_reached`, `:retry_limit_reached`, ...).
|
|
314
|
+
# @param failed_lock_key [String,NilClass]
|
|
315
|
+
# The lock of the series that blocks the series (`nil`: unknown, an attempt was not finished
|
|
316
|
+
# or some lock state was changed during the attempt).
|
|
317
|
+
# @return [void]
|
|
318
|
+
#
|
|
319
|
+
# @api private
|
|
320
|
+
# @since 1.23.0
|
|
321
|
+
def lock_series_obtaining_failed(
|
|
322
|
+
logger,
|
|
323
|
+
log_sampled,
|
|
324
|
+
lock_keys,
|
|
325
|
+
queue_ttl,
|
|
326
|
+
acquirer_id,
|
|
327
|
+
host_id,
|
|
328
|
+
acq_time,
|
|
329
|
+
access_strategy,
|
|
330
|
+
rw_mode,
|
|
331
|
+
priority,
|
|
332
|
+
result,
|
|
333
|
+
failed_lock_key
|
|
334
|
+
)
|
|
335
|
+
return unless log_sampled
|
|
336
|
+
|
|
337
|
+
logger.debug do
|
|
338
|
+
"[redis_queued_locks.lock_series_obtaining_failed] " \
|
|
339
|
+
"lock_keys => '#{lock_keys.inspect}' " \
|
|
340
|
+
"queue_ttl => #{queue_ttl} " \
|
|
341
|
+
"acq_id => '#{acquirer_id}' " \
|
|
342
|
+
"hst_id => '#{host_id}' " \
|
|
343
|
+
"acs_strat => '#{access_strategy}' " \
|
|
344
|
+
"rw_mode => '#{rw_mode}' " \
|
|
345
|
+
"priority => #{priority} " \
|
|
346
|
+
"acq_time => #{acq_time} (ms) " \
|
|
347
|
+
"result => '#{result}' " \
|
|
348
|
+
"failed_lock_key => '#{failed_lock_key}'"
|
|
349
|
+
end rescue nil
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# @param logger [::Logger,#debug]
|
|
353
|
+
# @param log_sampled [Boolean]
|
|
354
|
+
# @param lock_keys [Array<String>]
|
|
355
|
+
# @param queue_ttl [Integer]
|
|
356
|
+
# @param acquirer_id [String]
|
|
357
|
+
# @param host_id [String]
|
|
358
|
+
# @param access_strategy [Symbol]
|
|
359
|
+
# @param rw_mode [Symbol] The mode of the lock series (`:read` or `:write`).
|
|
360
|
+
# @param priority [Integer] The lock request priority of the series.
|
|
361
|
+
# @return [void]
|
|
362
|
+
#
|
|
363
|
+
# @api private
|
|
364
|
+
# @since 1.23.0
|
|
365
|
+
def dequeue_from_lock_series_queues(
|
|
366
|
+
logger,
|
|
367
|
+
log_sampled,
|
|
368
|
+
lock_keys,
|
|
369
|
+
queue_ttl,
|
|
370
|
+
acquirer_id,
|
|
371
|
+
host_id,
|
|
372
|
+
access_strategy,
|
|
373
|
+
rw_mode,
|
|
374
|
+
priority
|
|
375
|
+
)
|
|
376
|
+
return unless log_sampled
|
|
377
|
+
|
|
378
|
+
logger.debug do
|
|
379
|
+
"[redis_queued_locks.dequeue_from_lock_series_queues] " \
|
|
380
|
+
"lock_keys => '#{lock_keys.inspect}' " \
|
|
381
|
+
"queue_ttl => #{queue_ttl} " \
|
|
382
|
+
"acq_id => '#{acquirer_id}' " \
|
|
383
|
+
"hst_id => '#{host_id}' " \
|
|
384
|
+
"acs_strat => '#{access_strategy}' " \
|
|
385
|
+
"rw_mode => '#{rw_mode}' " \
|
|
386
|
+
"priority => #{priority}"
|
|
387
|
+
end rescue nil
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
# @param logger [::Logger,#debug]
|
|
391
|
+
# @param log_sampled [Boolean]
|
|
392
|
+
# @param lock_keys [Array<String>]
|
|
393
|
+
# @param queue_ttl [Integer]
|
|
394
|
+
# @param acquirer_id [String]
|
|
395
|
+
# @param host_id [String]
|
|
396
|
+
# @param access_strategy [Symbol]
|
|
397
|
+
# @param rw_mode [Symbol] The mode of the lock series (`:read` or `:write`).
|
|
398
|
+
# @param priority [Integer] The lock request priority of the series.
|
|
399
|
+
# @return [void]
|
|
400
|
+
#
|
|
401
|
+
# @api private
|
|
402
|
+
# @since 1.23.0
|
|
403
|
+
def expire_lock_series(
|
|
404
|
+
logger,
|
|
405
|
+
log_sampled,
|
|
406
|
+
lock_keys,
|
|
407
|
+
queue_ttl,
|
|
408
|
+
acquirer_id,
|
|
409
|
+
host_id,
|
|
410
|
+
access_strategy,
|
|
411
|
+
rw_mode,
|
|
412
|
+
priority
|
|
413
|
+
)
|
|
414
|
+
return unless log_sampled
|
|
415
|
+
|
|
416
|
+
logger.debug do
|
|
417
|
+
"[redis_queued_locks.expire_lock_series] " \
|
|
418
|
+
"lock_keys => '#{lock_keys.inspect}' " \
|
|
419
|
+
"queue_ttl => #{queue_ttl} " \
|
|
420
|
+
"acq_id => '#{acquirer_id}' " \
|
|
421
|
+
"hst_id => '#{host_id}' " \
|
|
422
|
+
"acs_strat => '#{access_strategy}' " \
|
|
423
|
+
"rw_mode => '#{rw_mode}' " \
|
|
424
|
+
"priority => #{priority}"
|
|
425
|
+
end rescue nil
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
# @param logger [::Logger,#debug]
|
|
429
|
+
# @param log_sampled [Boolean]
|
|
430
|
+
# @param lock_keys [Array<String>]
|
|
431
|
+
# The extended reentrant locks of the series (`:extendable_work_through`).
|
|
432
|
+
# @param decreased_ttl [Integer] In milliseconds.
|
|
433
|
+
# @param queue_ttl [Integer]
|
|
434
|
+
# @param acquirer_id [String]
|
|
435
|
+
# @param host_id [String]
|
|
436
|
+
# @param access_strategy [Symbol]
|
|
437
|
+
# @param rw_mode [Symbol] The mode of the lock series (`:read` or `:write`).
|
|
438
|
+
# @param priority [Integer] The lock request priority of the series.
|
|
439
|
+
# @return [void]
|
|
440
|
+
#
|
|
441
|
+
# @api private
|
|
442
|
+
# @since 1.23.0
|
|
443
|
+
def decrease_lock_series(
|
|
444
|
+
logger,
|
|
445
|
+
log_sampled,
|
|
446
|
+
lock_keys,
|
|
447
|
+
decreased_ttl,
|
|
448
|
+
queue_ttl,
|
|
449
|
+
acquirer_id,
|
|
450
|
+
host_id,
|
|
451
|
+
access_strategy,
|
|
452
|
+
rw_mode,
|
|
453
|
+
priority
|
|
454
|
+
)
|
|
455
|
+
return unless log_sampled
|
|
456
|
+
|
|
457
|
+
logger.debug do
|
|
458
|
+
"[redis_queued_locks.decrease_lock_series] " \
|
|
459
|
+
"lock_keys => '#{lock_keys.inspect}' " \
|
|
460
|
+
"decreased_ttl => #{decreased_ttl} " \
|
|
461
|
+
"queue_ttl => #{queue_ttl} " \
|
|
462
|
+
"acq_id => '#{acquirer_id}' " \
|
|
463
|
+
"hst_id => '#{host_id}' " \
|
|
464
|
+
"acs_strat => '#{access_strategy}' " \
|
|
465
|
+
"rw_mode => '#{rw_mode}' " \
|
|
466
|
+
"priority => #{priority}"
|
|
467
|
+
end rescue nil
|
|
468
|
+
end
|
|
469
|
+
end
|
|
470
|
+
# rubocop:enable Metrics/ClassLength
|
|
471
|
+
end
|
|
472
|
+
# rubocop:enable Metrics/ModuleLength
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Releases the lock series: only the locks that are obtained by the series itself and only if they
|
|
4
|
+
# are still owned by the current acquirer; the reentrant locks (obtained by the outer logic of
|
|
5
|
+
# the current acquirer) are kept and the extended ones get their ttl back.
|
|
6
|
+
#
|
|
7
|
+
# @api private
|
|
8
|
+
# @since 1.23.0
|
|
9
|
+
module RedisQueuedLocks::Acquirer::LockSeries::ReleaseLockSeries
|
|
10
|
+
# @param redis [RedisClient]
|
|
11
|
+
# @param lock_names [Array<String>]
|
|
12
|
+
# @param lock_processes [Array<Symbol,NilClass>]
|
|
13
|
+
# - The way each lock of the series is held (the order of `lock_names`):
|
|
14
|
+
# - `:lock_obtaining` - obtained by the series itself (released);
|
|
15
|
+
# - `:extendable_conflict_work_through` - extended reentrant lock (its ttl is decreased);
|
|
16
|
+
# - `:conflict_work_through` - reentrant lock (kept as is);
|
|
17
|
+
# @param held_rw_modes [Array<Symbol>]
|
|
18
|
+
# The mode of the lock that is really held by the acquirer (the order of `lock_names`).
|
|
19
|
+
# @param acquirer_id [String]
|
|
20
|
+
# @param decreased_ttl [Integer]
|
|
21
|
+
# The ttl to take back from the extended reentrant locks (in milliseconds): the extension
|
|
22
|
+
# without the time spent under the locks; a non-positive value keeps these locks as is.
|
|
23
|
+
# @return [void]
|
|
24
|
+
#
|
|
25
|
+
# @api private
|
|
26
|
+
# @since 1.23.0
|
|
27
|
+
def release_lock_series(
|
|
28
|
+
redis,
|
|
29
|
+
lock_names,
|
|
30
|
+
lock_processes,
|
|
31
|
+
held_rw_modes,
|
|
32
|
+
acquirer_id,
|
|
33
|
+
decreased_ttl
|
|
34
|
+
)
|
|
35
|
+
# @type var write_lock_names: Array[String]
|
|
36
|
+
write_lock_names = []
|
|
37
|
+
# @type var read_lock_names: Array[String]
|
|
38
|
+
read_lock_names = []
|
|
39
|
+
# @type var extended_write_lock_names: Array[String]
|
|
40
|
+
extended_write_lock_names = []
|
|
41
|
+
# @type var extended_read_lock_names: Array[String]
|
|
42
|
+
extended_read_lock_names = []
|
|
43
|
+
|
|
44
|
+
lock_names.each_with_index do |lock_name, index|
|
|
45
|
+
read_lock = held_rw_modes[index] == :read
|
|
46
|
+
|
|
47
|
+
case lock_processes[index]
|
|
48
|
+
when :lock_obtaining
|
|
49
|
+
(read_lock ? read_lock_names : write_lock_names) << lock_name
|
|
50
|
+
when :extendable_conflict_work_through
|
|
51
|
+
(read_lock ? extended_read_lock_names : extended_write_lock_names) << lock_name
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# NOTE: a non-positive decreased ttl: the spent time exceeds the extension
|
|
56
|
+
if decreased_ttl <= 0
|
|
57
|
+
extended_read_lock_names.clear
|
|
58
|
+
extended_write_lock_names.clear
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
release_write_lock_series(
|
|
62
|
+
redis, write_lock_names, extended_write_lock_names, acquirer_id, decreased_ttl
|
|
63
|
+
)
|
|
64
|
+
release_read_lock_series(
|
|
65
|
+
redis, read_lock_names, extended_read_lock_names, acquirer_id, decreased_ttl
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
# Releases the write locks (and decreases the ttl of the extended ones) of the given acquirer
|
|
72
|
+
# (compare-and-delete: the lock can be expired and obtained by another acquirer when the series
|
|
73
|
+
# logic exceeds the lock ttl) in one transaction for the whole series (three round trips: WATCH,
|
|
74
|
+
# lock owners, MULTI/EXEC):
|
|
75
|
+
# - an aborted transaction (one of the watched locks is changed) is retried with the locks
|
|
76
|
+
# that were still owned by the acquirer at the last check: the locks of other acquirers
|
|
77
|
+
# are neither touched nor watched, so the retries converge;
|
|
78
|
+
#
|
|
79
|
+
# @param redis [RedisClient]
|
|
80
|
+
# @param lock_names [Array<String>] The locks to release.
|
|
81
|
+
# @param extended_lock_names [Array<String>] The extended reentrant locks.
|
|
82
|
+
# @param acquirer_id [String]
|
|
83
|
+
# @param decreased_ttl [Integer] In milliseconds.
|
|
84
|
+
# @return [void]
|
|
85
|
+
#
|
|
86
|
+
# @api private
|
|
87
|
+
# @since 1.23.0
|
|
88
|
+
# rubocop:disable Metrics/MethodLength
|
|
89
|
+
def release_write_lock_series(redis, lock_names, extended_lock_names, acquirer_id, decreased_ttl)
|
|
90
|
+
return if lock_names.empty? && extended_lock_names.empty?
|
|
91
|
+
|
|
92
|
+
own_lock_keys = lock_names.map do |lock_name|
|
|
93
|
+
RedisQueuedLocks::Resource.prepare_lock_key(lock_name)
|
|
94
|
+
end
|
|
95
|
+
own_extended_lock_keys = extended_lock_names.map do |lock_name|
|
|
96
|
+
RedisQueuedLocks::Resource.prepare_lock_key(lock_name)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
redis.with do |conn|
|
|
100
|
+
until own_lock_keys.empty? && own_extended_lock_keys.empty?
|
|
101
|
+
released = conn.multi(watch: own_lock_keys + own_extended_lock_keys) do |transaction|
|
|
102
|
+
# NOTE: (pipeline layout) one reply (lock owner) per lock: the released locks first
|
|
103
|
+
# @type var lock_owners: Array[String?]
|
|
104
|
+
lock_owners = conn.pipelined do |pipeline|
|
|
105
|
+
own_lock_keys.each { |lock_key| pipeline.call('HGET', lock_key, 'acq_id') }
|
|
106
|
+
own_extended_lock_keys.each { |lock_key| pipeline.call('HGET', lock_key, 'acq_id') }
|
|
107
|
+
end
|
|
108
|
+
extended_locks_at = own_lock_keys.size
|
|
109
|
+
own_lock_keys = own_lock_keys.select.with_index do |_lock_key, index|
|
|
110
|
+
lock_owners[index] == acquirer_id
|
|
111
|
+
end
|
|
112
|
+
own_extended_lock_keys = own_extended_lock_keys.select.with_index do |_lock_key, index|
|
|
113
|
+
lock_owners[extended_locks_at + index] == acquirer_id
|
|
114
|
+
end
|
|
115
|
+
own_lock_keys.each { |lock_key| transaction.call('DEL', lock_key) }
|
|
116
|
+
# NOTE: EVAL signature -> <lua script>, (number of keys), *(keys), *(arguments)
|
|
117
|
+
own_extended_lock_keys.each do |lock_key|
|
|
118
|
+
transaction.call(
|
|
119
|
+
'EVAL',
|
|
120
|
+
RedisQueuedLocks::Acquirer::AcquireLock::YieldExpire::DECREASE_LOCK_PTTL,
|
|
121
|
+
1,
|
|
122
|
+
lock_key,
|
|
123
|
+
decreased_ttl
|
|
124
|
+
)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
# NOTE: `nil` => the transaction is aborted (a watched lock is changed) => retry
|
|
128
|
+
break unless released == nil
|
|
129
|
+
end
|
|
130
|
+
rescue RedisQueuedLocks::LockAcquirementIntermediateTimeoutError,
|
|
131
|
+
RedisQueuedLocks::TimedLockIntermediateTimeoutError,
|
|
132
|
+
::Timeout::ExitException
|
|
133
|
+
# NOTE: (timeouts) an interrupted WATCH block (the internal timeouts and the user's
|
|
134
|
+
# `Timeout.timeout` without an exception class): the closed connection drops the WATCH
|
|
135
|
+
# state (redis-client sends `UNWATCH` only on `StandardError`);
|
|
136
|
+
conn.close
|
|
137
|
+
raise
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
# rubocop:enable Metrics/MethodLength
|
|
141
|
+
|
|
142
|
+
# Releases the read locks (and decreases the extended ones) of the given acquirer in one
|
|
143
|
+
# transaction (other readers are not affected).
|
|
144
|
+
#
|
|
145
|
+
# @param redis [RedisClient]
|
|
146
|
+
# @param lock_names [Array<String>] The locks to release.
|
|
147
|
+
# @param extended_lock_names [Array<String>] The extended reentrant locks.
|
|
148
|
+
# @param acquirer_id [String]
|
|
149
|
+
# @param decreased_ttl [Integer] In milliseconds.
|
|
150
|
+
# @return [void]
|
|
151
|
+
#
|
|
152
|
+
# @api private
|
|
153
|
+
# @since 1.23.0
|
|
154
|
+
def release_read_lock_series(redis, lock_names, extended_lock_names, acquirer_id, decreased_ttl)
|
|
155
|
+
return if lock_names.empty? && extended_lock_names.empty?
|
|
156
|
+
|
|
157
|
+
redis.multi do |transaction|
|
|
158
|
+
lock_names.each do |lock_name|
|
|
159
|
+
transaction.call(
|
|
160
|
+
'ZREM', RedisQueuedLocks::Resource.prepare_lock_readers(lock_name), acquirer_id
|
|
161
|
+
)
|
|
162
|
+
transaction.call(
|
|
163
|
+
'DEL', RedisQueuedLocks::Resource.prepare_read_lock_key(lock_name, acquirer_id)
|
|
164
|
+
)
|
|
165
|
+
end
|
|
166
|
+
# NOTE:
|
|
167
|
+
# - XX => do nothing if the read lock is already expired and removed from the registry;
|
|
168
|
+
# - the read lock data TTL is not decreased (it is not used without the read lock);
|
|
169
|
+
extended_lock_names.each do |lock_name|
|
|
170
|
+
transaction.call(
|
|
171
|
+
'ZADD',
|
|
172
|
+
RedisQueuedLocks::Resource.prepare_lock_readers(lock_name),
|
|
173
|
+
'XX',
|
|
174
|
+
'INCR',
|
|
175
|
+
-decreased_ttl,
|
|
176
|
+
acquirer_id
|
|
177
|
+
)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|