redis_queued_locks 1.12.0 → 1.13.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/.rubocop.yml +4 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +42 -5
- data/LICENSE.txt +1 -1
- data/README.md +231 -203
- data/Rakefile +12 -4
- data/Steepfile +16 -0
- data/github_ci/ruby3.3.gemfile +17 -0
- data/github_ci/ruby3.3.gemfile.lock +217 -0
- data/lib/redis_queued_locks/{acquier → acquirer}/acquire_lock/delay_execution.rb +4 -4
- data/lib/redis_queued_locks/acquirer/acquire_lock/dequeue_from_lock_queue/log_visitor.rb +40 -0
- data/lib/redis_queued_locks/{acquier → acquirer}/acquire_lock/dequeue_from_lock_queue.rb +17 -8
- data/lib/redis_queued_locks/acquirer/acquire_lock/instr_visitor.rb +166 -0
- data/lib/redis_queued_locks/acquirer/acquire_lock/log_visitor.rb +218 -0
- data/lib/redis_queued_locks/acquirer/acquire_lock/try_to_lock/log_visitor.rb +543 -0
- data/lib/redis_queued_locks/{acquier → acquirer}/acquire_lock/try_to_lock.rb +126 -92
- data/lib/redis_queued_locks/{acquier → acquirer}/acquire_lock/with_acq_timeout.rb +14 -13
- data/lib/redis_queued_locks/acquirer/acquire_lock/yield_expire/log_visitor.rb +76 -0
- data/lib/redis_queued_locks/{acquier → acquirer}/acquire_lock/yield_expire.rb +43 -20
- data/lib/redis_queued_locks/{acquier → acquirer}/acquire_lock.rb +69 -42
- data/lib/redis_queued_locks/{acquier → acquirer}/clear_dead_requests.rb +5 -3
- data/lib/redis_queued_locks/{acquier → acquirer}/extend_lock_ttl.rb +4 -3
- data/lib/redis_queued_locks/{acquier → acquirer}/is_locked.rb +1 -1
- data/lib/redis_queued_locks/{acquier → acquirer}/is_queued.rb +1 -1
- data/lib/redis_queued_locks/{acquier → acquirer}/keys.rb +5 -5
- data/lib/redis_queued_locks/{acquier → acquirer}/lock_info.rb +9 -5
- data/lib/redis_queued_locks/{acquier → acquirer}/locks.rb +16 -3
- data/lib/redis_queued_locks/{acquier → acquirer}/queue_info.rb +8 -6
- data/lib/redis_queued_locks/{acquier → acquirer}/queues.rb +9 -2
- data/lib/redis_queued_locks/{acquier → acquirer}/release_all_locks.rb +23 -18
- data/lib/redis_queued_locks/{acquier → acquirer}/release_lock.rb +25 -19
- data/lib/redis_queued_locks/acquirer.rb +18 -0
- data/lib/redis_queued_locks/client.rb +164 -254
- data/lib/redis_queued_locks/config/dsl.rb +94 -0
- data/lib/redis_queued_locks/config.rb +231 -0
- data/lib/redis_queued_locks/data.rb +2 -0
- data/lib/redis_queued_locks/errors.rb +27 -11
- data/lib/redis_queued_locks/instrument.rb +11 -4
- data/lib/redis_queued_locks/logging/void_logger.rb +38 -1
- data/lib/redis_queued_locks/logging.rb +20 -5
- data/lib/redis_queued_locks/resource.rb +49 -11
- data/lib/redis_queued_locks/swarm/acquirers.rb +17 -16
- data/lib/redis_queued_locks/swarm/flush_zombies.rb +26 -25
- data/lib/redis_queued_locks/swarm/probe_hosts.rb +20 -19
- data/lib/redis_queued_locks/swarm/redis_client_builder.rb +3 -3
- data/lib/redis_queued_locks/swarm/supervisor.rb +19 -6
- data/lib/redis_queued_locks/swarm/swarm_element/isolated.rb +20 -18
- data/lib/redis_queued_locks/swarm/swarm_element/threaded.rb +35 -27
- data/lib/redis_queued_locks/swarm/zombie_info.rb +9 -9
- data/lib/redis_queued_locks/swarm.rb +20 -41
- data/lib/redis_queued_locks/utilities/lock.rb +4 -2
- data/lib/redis_queued_locks/utilities.rb +2 -2
- data/lib/redis_queued_locks/version.rb +2 -2
- data/lib/redis_queued_locks.rb +2 -2
- data/rbs_collection.lock.yaml +40 -0
- data/rbs_collection.yaml +16 -0
- data/redis_queued_locks.gemspec +22 -23
- data/sig/manifest.yml +7 -0
- data/sig/redis_queued_locks/acquier.rbs +4 -0
- data/sig/redis_queued_locks/acquirer/acquire_lock/delay_execution.rbs +9 -0
- data/sig/redis_queued_locks/acquirer/acquire_lock/dequeue_from_lock_queue/log_visitor.rbs +21 -0
- data/sig/redis_queued_locks/acquirer/acquire_lock/dequeue_from_lock_queue.rbs +26 -0
- data/sig/redis_queued_locks/acquirer/acquire_lock/instr_visitor.rbs +71 -0
- data/sig/redis_queued_locks/acquirer/acquire_lock/log_visitor.rbs +72 -0
- data/sig/redis_queued_locks/acquirer/acquire_lock/try_to_lock/log_visitor.rbs +179 -0
- data/sig/redis_queued_locks/acquirer/acquire_lock/try_to_lock.rbs +48 -0
- data/sig/redis_queued_locks/acquirer/acquire_lock/with_acq_timeout.rbs +19 -0
- data/sig/redis_queued_locks/acquirer/acquire_lock/yield_expire.rbs +41 -0
- data/sig/redis_queued_locks/acquirer/acquire_lock/yield_with_expire/log_visitor.rbs +32 -0
- data/sig/redis_queued_locks/acquirer/acquire_lock.rbs +51 -0
- data/sig/redis_queued_locks/acquirer/clear_dead_requests.rbs +28 -0
- data/sig/redis_queued_locks/acquirer/extend_lock_ttl.rbs +28 -0
- data/sig/redis_queued_locks/acquirer/is_locked.rbs +9 -0
- data/sig/redis_queued_locks/acquirer/is_queued.rbs +9 -0
- data/sig/redis_queued_locks/acquirer/keys.rbs +10 -0
- data/sig/redis_queued_locks/acquirer/lock_info.rbs +10 -0
- data/sig/redis_queued_locks/acquirer/locks.rbs +16 -0
- data/sig/redis_queued_locks/acquirer/queue_info.rbs +13 -0
- data/sig/redis_queued_locks/acquirer/queues.rbs +16 -0
- data/sig/redis_queued_locks/acquirer/release_all_locks.rbs +30 -0
- data/sig/redis_queued_locks/acquirer/release_lock.rbs +38 -0
- data/sig/redis_queued_locks/client.rbs +195 -0
- data/sig/redis_queued_locks/config/dsl.rbs +26 -0
- data/sig/redis_queued_locks/config.rbs +23 -0
- data/sig/redis_queued_locks/data.rbs +4 -0
- data/sig/redis_queued_locks/debugger/interface.rbs +9 -0
- data/sig/redis_queued_locks/debugger.rbs +13 -0
- data/sig/redis_queued_locks/errors.rbs +43 -0
- data/sig/redis_queued_locks/instrument/active_support.rbs +7 -0
- data/sig/redis_queued_locks/instrument/sampler.rbs +9 -0
- data/sig/redis_queued_locks/instrument/void_notifier.rbs +7 -0
- data/sig/redis_queued_locks/instrument.rbs +15 -0
- data/sig/redis_queued_locks/logging/sampler.rbs +9 -0
- data/sig/redis_queued_locks/logging/void_logger.rbs +15 -0
- data/sig/redis_queued_locks/logging.rbs +15 -0
- data/sig/redis_queued_locks/resource.rbs +42 -0
- data/sig/redis_queued_locks/swarm/acquirers.rbs +10 -0
- data/sig/redis_queued_locks/swarm/flush_zombies.rbs +13 -0
- data/sig/redis_queued_locks/swarm/probe_hosts.rbs +13 -0
- data/sig/redis_queued_locks/swarm/redis_client_builder.rbs +19 -0
- data/sig/redis_queued_locks/swarm/supervisor.rbs +26 -0
- data/sig/redis_queued_locks/swarm/swarm_element/isolated.rbs +52 -0
- data/sig/redis_queued_locks/swarm/swarm_element/threaded.rbs +61 -0
- data/sig/redis_queued_locks/swarm/swarm_element.rbs +8 -0
- data/sig/redis_queued_locks/swarm/zombie_info.rbs +24 -0
- data/sig/redis_queued_locks/swarm.rbs +41 -0
- data/sig/redis_queued_locks/utilities/lock.rbs +10 -0
- data/sig/redis_queued_locks/utilities.rbs +11 -0
- data/sig/redis_queued_locks/version.rbs +3 -0
- data/sig/redis_queued_locks.rbs +14 -0
- data/sig/vendor/active_support.rbs +9 -0
- data/sig/vendor/redis_client.rbs +39 -0
- data/sig/vendor/semantic_logger.rbs +4 -0
- metadata +96 -54
- data/lib/redis_queued_locks/acquier/acquire_lock/dequeue_from_lock_queue/log_visitor.rb +0 -40
- data/lib/redis_queued_locks/acquier/acquire_lock/instr_visitor.rb +0 -166
- data/lib/redis_queued_locks/acquier/acquire_lock/log_visitor.rb +0 -216
- data/lib/redis_queued_locks/acquier/acquire_lock/try_to_lock/log_visitor.rb +0 -541
- data/lib/redis_queued_locks/acquier/acquire_lock/yield_expire/log_visitor.rb +0 -76
- data/lib/redis_queued_locks/acquier.rb +0 -18
@@ -3,8 +3,8 @@
|
|
3
3
|
# @api private
|
4
4
|
# @since 1.0.0
|
5
5
|
# @version 1.7.0
|
6
|
-
# rubocop:disable Metrics/ModuleLength
|
7
|
-
module RedisQueuedLocks::
|
6
|
+
# rubocop:disable Metrics/ModuleLength
|
7
|
+
module RedisQueuedLocks::Acquirer::AcquireLock::TryToLock
|
8
8
|
require_relative 'try_to_lock/log_visitor'
|
9
9
|
|
10
10
|
# @return [String]
|
@@ -20,10 +20,13 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
20
20
|
# @param logger [::Logger,#debug]
|
21
21
|
# @param log_lock_try [Boolean]
|
22
22
|
# @param lock_key [String]
|
23
|
+
# @param read_write_mode [Symbol]
|
23
24
|
# @param lock_key_queue [String]
|
24
|
-
# @param
|
25
|
+
# @param read_lock_key_queue [String]
|
26
|
+
# @param write_lock_key_queue [String]
|
27
|
+
# @param acquirer_id [String]
|
25
28
|
# @param host_id [String]
|
26
|
-
# @param
|
29
|
+
# @param acquirer_position [Numeric]
|
27
30
|
# @param ttl [Integer]
|
28
31
|
# @param queue_ttl [Integer]
|
29
32
|
# @param fail_fast [Boolean]
|
@@ -36,17 +39,20 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
36
39
|
#
|
37
40
|
# @api private
|
38
41
|
# @since 1.0.0
|
39
|
-
# @version 1.
|
42
|
+
# @version 1.13.0
|
40
43
|
# rubocop:disable Metrics/MethodLength
|
41
44
|
def try_to_lock(
|
42
45
|
redis,
|
43
46
|
logger,
|
44
47
|
log_lock_try,
|
45
48
|
lock_key,
|
49
|
+
read_write_mode,
|
46
50
|
lock_key_queue,
|
47
|
-
|
51
|
+
read_lock_key_queue,
|
52
|
+
write_lock_key_queue,
|
53
|
+
acquirer_id,
|
48
54
|
host_id,
|
49
|
-
|
55
|
+
acquirer_position,
|
50
56
|
ttl,
|
51
57
|
queue_ttl,
|
52
58
|
fail_fast,
|
@@ -57,25 +63,28 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
57
63
|
instr_sampled
|
58
64
|
)
|
59
65
|
# Step X: intermediate invocation results
|
66
|
+
# @type var inter_result: Symbol?
|
60
67
|
inter_result = nil
|
68
|
+
# @type var timestamp: Float?
|
61
69
|
timestamp = nil
|
70
|
+
# @type var spc_processed_timestamp: Float?
|
62
71
|
spc_processed_timestamp = nil
|
63
72
|
|
64
73
|
LogVisitor.start(
|
65
74
|
logger, log_sampled, log_lock_try, lock_key,
|
66
|
-
queue_ttl,
|
75
|
+
queue_ttl, acquirer_id, host_id, access_strategy
|
67
76
|
)
|
68
77
|
|
69
78
|
# Step X: start to work with lock acquiring
|
70
79
|
result = redis.with do |rconn|
|
71
80
|
LogVisitor.rconn_fetched(
|
72
81
|
logger, log_sampled, log_lock_try, lock_key,
|
73
|
-
queue_ttl,
|
82
|
+
queue_ttl, acquirer_id, host_id, access_strategy
|
74
83
|
)
|
75
84
|
|
76
85
|
# Step 0:
|
77
86
|
# watch the lock key changes (and discard acquirement if lock is already
|
78
|
-
# obtained by another
|
87
|
+
# obtained by another acquirer during the current lock acquiremntt)
|
79
88
|
rconn.multi(watch: [lock_key]) do |transact|
|
80
89
|
# SP-Conflict status PREPARING: get the current lock obtainer
|
81
90
|
current_lock_obtainer = rconn.call('HGET', lock_key, 'acq_id')
|
@@ -83,15 +92,14 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
83
92
|
sp_conflict_status = nil
|
84
93
|
|
85
94
|
# SP-Conflict Step X1: calculate the current deadlock status
|
86
|
-
if current_lock_obtainer != nil &&
|
95
|
+
if current_lock_obtainer != nil && acquirer_id == current_lock_obtainer
|
87
96
|
LogVisitor.same_process_conflict_detected(
|
88
97
|
logger, log_sampled, log_lock_try, lock_key,
|
89
|
-
queue_ttl,
|
98
|
+
queue_ttl, acquirer_id, host_id, access_strategy
|
90
99
|
)
|
91
100
|
|
92
101
|
# SP-Conflict Step X2: self-process dead lock moment started.
|
93
102
|
# SP-Conflict CHECK (Step CHECK): check chosen strategy and flag the current status
|
94
|
-
# rubocop:disable Lint/DuplicateBranch
|
95
103
|
case conflict_strategy
|
96
104
|
when :work_through
|
97
105
|
# <SP-Conflict Moment>: work through => exit
|
@@ -112,11 +120,9 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
112
120
|
# but is forgotten to be added here;
|
113
121
|
sp_conflict_status = :conflict_wait_for_lock
|
114
122
|
end
|
115
|
-
# rubocop:enable Lint/DuplicateBranch
|
116
|
-
|
117
123
|
LogVisitor.same_process_conflict_analyzed(
|
118
124
|
logger, log_sampled, log_lock_try, lock_key,
|
119
|
-
queue_ttl,
|
125
|
+
queue_ttl, acquirer_id, host_id, access_strategy, sp_conflict_status
|
120
126
|
)
|
121
127
|
end
|
122
128
|
|
@@ -150,14 +156,16 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
150
156
|
transact.call(
|
151
157
|
'HSET',
|
152
158
|
lock_key,
|
153
|
-
'l_spc_ext_ts',
|
159
|
+
'l_spc_ext_ts', spc_processed_timestamp = Time.now.to_f,
|
154
160
|
'l_spc_ext_ini_ttl', ttl
|
155
161
|
)
|
156
162
|
inter_result = :extendable_conflict_work_through
|
157
163
|
|
164
|
+
# @type var sp_conflict_status: Symbol
|
165
|
+
# @type var spc_processed_timestamp: Float
|
158
166
|
LogVisitor.reentrant_lock__extend_and_work_through(
|
159
167
|
logger, log_sampled, log_lock_try, lock_key,
|
160
|
-
queue_ttl,
|
168
|
+
queue_ttl, acquirer_id, host_id, access_strategy,
|
161
169
|
sp_conflict_status, ttl, spc_processed_timestamp
|
162
170
|
)
|
163
171
|
# SP-Conflict-Step X2: switch to dead lock logic or not
|
@@ -177,12 +185,14 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
177
185
|
transact.call(
|
178
186
|
'HSET',
|
179
187
|
lock_key,
|
180
|
-
'l_spc_ts',
|
188
|
+
'l_spc_ts', spc_processed_timestamp = Time.now.to_f
|
181
189
|
)
|
182
190
|
|
191
|
+
# @type var sp_conflict_status: Symbol
|
192
|
+
# @type var spc_processed_timestamp: Float
|
183
193
|
LogVisitor.reentrant_lock__work_through(
|
184
194
|
logger, log_sampled, log_lock_try, lock_key,
|
185
|
-
queue_ttl,
|
195
|
+
queue_ttl, acquirer_id, host_id, access_strategy,
|
186
196
|
sp_conflict_status, spc_processed_timestamp
|
187
197
|
)
|
188
198
|
# SP-Conflict-Step X2: switch to dead lock logic or not
|
@@ -190,9 +200,11 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
190
200
|
inter_result = :conflict_dead_lock
|
191
201
|
spc_processed_timestamp = Time.now.to_f
|
192
202
|
|
203
|
+
# @type var sp_conflict_status: Symbol
|
204
|
+
# @type var spc_processed_timestamp: Float
|
193
205
|
LogVisitor.single_process_lock_conflict__dead_lock(
|
194
206
|
logger, log_sampled, log_lock_try, lock_key,
|
195
|
-
queue_ttl,
|
207
|
+
queue_ttl, acquirer_id, host_id, access_strategy,
|
196
208
|
sp_conflict_status, spc_processed_timestamp
|
197
209
|
)
|
198
210
|
# Reached the SP-Non-Conflict Mode (NOTE):
|
@@ -202,87 +214,94 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
202
214
|
# Fast-Step X1: lock is already obtained. fail fast leads to "no try".
|
203
215
|
inter_result = :fail_fast_no_try
|
204
216
|
else
|
205
|
-
# Step 1: add an
|
206
|
-
|
217
|
+
# Step 1: add an acquirer to the lock acquirement queue
|
218
|
+
# NOTE:
|
219
|
+
# 'NX' means "Only add new elements. Don't update already existing elements."
|
220
|
+
# that works as:
|
221
|
+
# 1. (enqueue) <<if you are already in the queue - do nothing and wait your time>>
|
222
|
+
# 2. (requeue) or <<add to the right pre-calculated position if you are
|
223
|
+
# not in the queue now>>;
|
224
|
+
rconn.call('ZADD', lock_key_queue, 'NX', acquirer_position, acquirer_id)
|
207
225
|
|
208
226
|
LogVisitor.acq_added_to_queue(
|
209
227
|
logger, log_sampled, log_lock_try, lock_key,
|
210
|
-
queue_ttl,
|
228
|
+
queue_ttl, acquirer_id, host_id, access_strategy
|
211
229
|
)
|
212
230
|
|
213
|
-
# Step 2.1: drop expired
|
231
|
+
# Step 2.1: drop expired acquirers from the lock queue
|
214
232
|
rconn.call(
|
215
233
|
'ZREMRANGEBYSCORE',
|
216
234
|
lock_key_queue,
|
217
235
|
'-inf',
|
218
|
-
RedisQueuedLocks::Resource.
|
236
|
+
RedisQueuedLocks::Resource.acquirer_dead_score(queue_ttl)
|
219
237
|
)
|
220
238
|
|
221
239
|
LogVisitor.remove_expired_acqs(
|
222
240
|
logger, log_sampled, log_lock_try, lock_key,
|
223
|
-
queue_ttl,
|
241
|
+
queue_ttl, acquirer_id, host_id, access_strategy
|
224
242
|
)
|
225
243
|
|
226
|
-
# Step 3: get the actual
|
227
|
-
|
244
|
+
# Step 3: get the actual acquirer waiting in the queue
|
245
|
+
waiting_acquirer = Array(rconn.call('ZRANGE', lock_key_queue, '0', '0')).first
|
228
246
|
|
229
247
|
LogVisitor.get_first_from_queue(
|
230
248
|
logger, log_sampled, log_lock_try, lock_key,
|
231
|
-
queue_ttl,
|
249
|
+
queue_ttl, acquirer_id, host_id, access_strategy, waiting_acquirer
|
232
250
|
)
|
233
251
|
|
234
252
|
# Step PRE-4.x: check if the request time limit is reached
|
235
253
|
# (when the current try self-removes itself from queue (queue ttl has come))
|
236
|
-
if
|
254
|
+
if waiting_acquirer == nil
|
237
255
|
LogVisitor.exit__queue_ttl_reached(
|
238
256
|
logger, log_sampled, log_lock_try, lock_key,
|
239
|
-
queue_ttl,
|
257
|
+
queue_ttl, acquirer_id, host_id, access_strategy
|
240
258
|
)
|
241
259
|
|
242
260
|
inter_result = :dead_score_reached
|
243
261
|
# Step STRATEGY: check the stragegy and corresponding preventing factor
|
244
|
-
# Step STRATEGY (queued): check the actual
|
245
|
-
elsif access_strategy == :queued &&
|
262
|
+
# Step STRATEGY (queued): check the actual acquirer: is it ours? are we aready to lock?
|
263
|
+
elsif access_strategy == :queued && waiting_acquirer != acquirer_id
|
246
264
|
# Step ROLLBACK 1.1: our time hasn't come yet. retry!
|
265
|
+
|
247
266
|
LogVisitor.exit__no_first(
|
248
267
|
logger, log_sampled, log_lock_try, lock_key,
|
249
|
-
queue_ttl,
|
268
|
+
queue_ttl, acquirer_id, host_id, access_strategy, waiting_acquirer,
|
250
269
|
rconn.call('HGETALL', lock_key).to_h
|
251
270
|
)
|
252
|
-
inter_result = :
|
271
|
+
inter_result = :acquirer_is_not_first_in_queue
|
253
272
|
# Step STRAGEY: successfull (:queued OR :random)
|
254
|
-
elsif (access_strategy == :queued &&
|
273
|
+
elsif (access_strategy == :queued && waiting_acquirer == acquirer_id) ||
|
255
274
|
(access_strategy == :random)
|
256
275
|
# NOTE: our time has come! let's try to acquire the lock!
|
257
276
|
|
258
277
|
# Step 5: find the lock -> check if the our lock is already acquired
|
259
|
-
|
278
|
+
locked_by_acquirer = rconn.call('HGET', lock_key, 'acq_id')
|
260
279
|
|
261
|
-
if
|
280
|
+
if locked_by_acquirer
|
262
281
|
# Step ROLLBACK 2: required lock is stil acquired. retry!
|
263
282
|
|
264
283
|
LogVisitor.exit__lock_still_obtained(
|
265
284
|
logger, log_sampled, log_lock_try, lock_key,
|
266
|
-
queue_ttl,
|
267
|
-
|
285
|
+
queue_ttl, acquirer_id, host_id, access_strategy,
|
286
|
+
waiting_acquirer, locked_by_acquirer,
|
268
287
|
rconn.call('HGETALL', lock_key).to_h
|
269
288
|
)
|
270
289
|
inter_result = :lock_is_still_acquired
|
271
290
|
else
|
272
291
|
# NOTE: required lock is free and ready to be acquired! acquire!
|
273
292
|
|
274
|
-
# Step 6.1: remove our
|
275
|
-
transact.call('ZREM', lock_key_queue,
|
293
|
+
# Step 6.1: remove our acquirer from waiting queue
|
294
|
+
transact.call('ZREM', lock_key_queue, acquirer_id)
|
276
295
|
|
277
|
-
# Step 6.2: acquire a lock and store an info about the
|
296
|
+
# Step 6.2: acquire a lock and store an info about the acquirer and host
|
278
297
|
transact.call(
|
279
298
|
'HSET',
|
280
299
|
lock_key,
|
281
|
-
'acq_id',
|
300
|
+
'acq_id', acquirer_id,
|
282
301
|
'hst_id', host_id,
|
283
|
-
'ts',
|
302
|
+
'ts', timestamp = Time.now.to_f,
|
284
303
|
'ini_ttl', ttl,
|
285
|
-
*(meta.to_a if meta != nil)
|
304
|
+
*(meta.to_a if meta != nil) # steep:ignore
|
286
305
|
)
|
287
306
|
|
288
307
|
# Step 6.3: set the lock expiration time in order to prevent "infinite locks"
|
@@ -290,7 +309,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
290
309
|
|
291
310
|
LogVisitor.obtain__free_to_acquire(
|
292
311
|
logger, log_sampled, log_lock_try, lock_key,
|
293
|
-
queue_ttl,
|
312
|
+
queue_ttl, acquirer_id, host_id, access_strategy
|
294
313
|
)
|
295
314
|
end
|
296
315
|
end
|
@@ -316,62 +335,74 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
316
335
|
# 3. HINCRBY (increased spc count) (OK for != nil)
|
317
336
|
# 4. HSET (store the last spc time and ttl data) (OK for == 2 or != nil)
|
318
337
|
if result[0] != nil && result[1] != nil && result[2] != nil && result[3] != nil
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
338
|
+
{
|
339
|
+
ok: true,
|
340
|
+
result: {
|
341
|
+
process: :extendable_conflict_work_through,
|
342
|
+
lock_key: lock_key,
|
343
|
+
acq_id: acquirer_id,
|
344
|
+
hst_id: host_id,
|
345
|
+
ts: spc_processed_timestamp,
|
346
|
+
ttl: ttl
|
347
|
+
}
|
348
|
+
}
|
327
349
|
elsif result[0] != nil
|
328
350
|
# NOTE: that is enough to the fact that the lock is extended but <TODO>
|
329
351
|
# TODO: add detalized overview (log? some in-line code clarifications?) of the result
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
352
|
+
{
|
353
|
+
ok: true,
|
354
|
+
result: {
|
355
|
+
process: :extendable_conflict_work_through,
|
356
|
+
lock_key: lock_key,
|
357
|
+
acq_id: acquirer_id,
|
358
|
+
hst_id: host_id,
|
359
|
+
ts: spc_processed_timestamp,
|
360
|
+
ttl: ttl
|
361
|
+
}
|
362
|
+
}
|
338
363
|
else
|
339
364
|
# NOTE: unknown behaviour :thinking:
|
340
|
-
|
365
|
+
{ ok: false, result: :unknown }
|
341
366
|
end
|
342
367
|
elsif result == nil || (result.is_a?(::Array) && result.empty?)
|
343
368
|
# NOTE: the lock key was changed durign an SPC logic execution
|
344
|
-
|
369
|
+
{ ok: false, result: :lock_is_acquired_during_acquire_race }
|
345
370
|
else
|
346
371
|
# NOTE: unknown behaviour :thinking:. this part is not reachable at the moment.
|
347
|
-
|
372
|
+
{ ok: false, result: :unknown }
|
348
373
|
end
|
349
374
|
when inter_result == :conflict_work_through
|
350
375
|
# Step 7.same_process_conflict.B:
|
351
376
|
# - conflict_work_through case => yield <block> without lock realesing/extending
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
377
|
+
{
|
378
|
+
ok: true,
|
379
|
+
result: {
|
380
|
+
process: :conflict_work_through,
|
381
|
+
lock_key: lock_key,
|
382
|
+
acq_id: acquirer_id,
|
383
|
+
hst_id: host_id,
|
384
|
+
ts: spc_processed_timestamp,
|
385
|
+
ttl: ttl
|
386
|
+
}
|
387
|
+
}
|
360
388
|
when inter_result == :conflict_dead_lock
|
361
389
|
# Step 7.same_process_conflict.C:
|
362
390
|
# - deadlock. should fail in acquirement logic;
|
363
|
-
|
391
|
+
{ ok: false, result: :conflict_dead_lock }
|
364
392
|
when fail_fast && inter_result == :fail_fast_no_try
|
365
393
|
# Step 7.a: lock is still acquired and we should exit from the logic as soon as possible
|
366
|
-
|
394
|
+
{ ok: false, result: :fail_fast_no_try }
|
367
395
|
when inter_result == :dead_score_reached
|
368
|
-
|
369
|
-
when inter_result == :lock_is_still_acquired
|
396
|
+
{ ok: false, result: :dead_score_reached }
|
397
|
+
when inter_result == :lock_is_still_acquired
|
370
398
|
# Step 7.b: lock is still acquired by another process => failed to acquire
|
371
|
-
|
399
|
+
{ ok: false, result: :lock_is_still_acquired }
|
400
|
+
when inter_result == :acquirer_is_not_first_in_queue
|
401
|
+
# Step 7.c: lock is still acquired by another process => failed to acquire
|
402
|
+
{ ok: false, result: :acquirer_is_not_first_in_queue }
|
372
403
|
when result == nil || (result.is_a?(::Array) && result.empty?)
|
373
|
-
# Step 7.
|
374
|
-
|
404
|
+
# Step 7.d: lock is already acquired durign the acquire race => failed to acquire
|
405
|
+
{ ok: false, result: :lock_is_acquired_during_acquire_race }
|
375
406
|
when result.is_a?(::Array) && result.size == 3 # NOTE: 3 is a count of redis lock commands
|
376
407
|
# TODO:
|
377
408
|
# => (!) analyze the command result and do actions with the depending on it;
|
@@ -383,20 +414,23 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
383
414
|
# 3. pexpire should return 1 (expiration time is successfully applied)
|
384
415
|
|
385
416
|
# Step 7.d: locked! :) let's go! => successfully acquired
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
417
|
+
{
|
418
|
+
ok: true,
|
419
|
+
result: {
|
420
|
+
process: :lock_obtaining,
|
421
|
+
lock_key: lock_key,
|
422
|
+
acq_id: acquirer_id,
|
423
|
+
hst_id: host_id,
|
424
|
+
ts: timestamp,
|
425
|
+
ttl: ttl
|
426
|
+
}
|
427
|
+
}
|
394
428
|
else
|
395
429
|
# Ste 7.3: unknown behaviour :thinking:
|
396
|
-
|
430
|
+
{ ok: false, result: :unknown }
|
397
431
|
end
|
398
432
|
# rubocop:enable Lint/DuplicateBranch
|
399
433
|
end
|
400
434
|
# rubocop:enable Metrics/MethodLength, Metrics/PerceivedComplexity
|
401
435
|
end
|
402
|
-
# rubocop:enable Metrics/ModuleLength
|
436
|
+
# rubocop:enable Metrics/ModuleLength
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
# @since 1.0.0
|
5
|
-
module RedisQueuedLocks::
|
5
|
+
module RedisQueuedLocks::Acquirer::AcquireLock::WithAcqTimeout
|
6
6
|
# @param redis [RedisClient]
|
7
7
|
# Redis connection manager required for additional data extraction for error message.
|
8
8
|
# @param timeout [NilClass,Integer]
|
@@ -17,9 +17,11 @@ module RedisQueuedLocks::Acquier::AcquireLock::WithAcqTimeout
|
|
17
17
|
# Add additional error data about lock queue and required lock to the timeout error or not.
|
18
18
|
# @option on_timeout [Proc,NilClass]
|
19
19
|
# Callback invoked on Timeout::Error.
|
20
|
+
# @param block [Block] Custom logic that should be invoked under the obtained lock.
|
20
21
|
# @return [Any]
|
21
22
|
#
|
22
|
-
# @raise [RedisQueuedLocks::
|
23
|
+
# @raise [RedisQueuedLocks::LockAcquirementIntermediateTimeoutError]
|
24
|
+
# @raise [RedisQueuedLocks::LockAcquirementTimeoutError]
|
23
25
|
#
|
24
26
|
# @api private
|
25
27
|
# @since 1.0.0
|
@@ -34,25 +36,24 @@ module RedisQueuedLocks::Acquier::AcquireLock::WithAcqTimeout
|
|
34
36
|
on_timeout: nil,
|
35
37
|
&block
|
36
38
|
)
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
on_timeout.call unless on_timeout == nil
|
39
|
+
::Timeout.timeout(timeout, RedisQueuedLocks::LockAcquirementIntermediateTimeoutError, &block)
|
40
|
+
rescue RedisQueuedLocks::LockAcquirementIntermediateTimeoutError
|
41
|
+
if on_timeout != nil
|
42
|
+
# @type var on_timeout: Proc
|
43
|
+
on_timeout.call
|
44
|
+
end
|
44
45
|
|
45
46
|
if raise_errors
|
46
47
|
if detailed_acq_timeout_error
|
47
48
|
# TODO: rewrite these invocations to separated inner-AcquireLock-related modules
|
48
49
|
# in order to remove any dependencies from the other public RQL commands cuz
|
49
50
|
# all AcquireLock logic elements should be fully independent from others as a core;
|
50
|
-
lock_info = RedisQueuedLocks::
|
51
|
-
queue_info = RedisQueuedLocks::
|
51
|
+
lock_info = RedisQueuedLocks::Acquirer::LockInfo.lock_info(redis, lock_name)
|
52
|
+
queue_info = RedisQueuedLocks::Acquirer::QueueInfo.queue_info(redis, lock_name)
|
52
53
|
|
53
54
|
# rubocop:disable Metrics/BlockNesting
|
54
55
|
raise(
|
55
|
-
RedisQueuedLocks::
|
56
|
+
RedisQueuedLocks::LockAcquirementTimeoutError,
|
56
57
|
"Failed to acquire the lock \"#{lock_key}\" " \
|
57
58
|
"for the given <#{timeout} seconds> timeout. Details: " \
|
58
59
|
"<Lock Data> => #{lock_info ? lock_info.inspect : '<no_data>'}; " \
|
@@ -61,7 +62,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::WithAcqTimeout
|
|
61
62
|
# rubocop:enable Metrics/BlockNesting
|
62
63
|
else
|
63
64
|
raise(
|
64
|
-
RedisQueuedLocks::
|
65
|
+
RedisQueuedLocks::LockAcquirementTimeoutError,
|
65
66
|
"Failed to acquire the lock \"#{lock_key}\" " \
|
66
67
|
"for the given <#{timeout} seconds> timeout."
|
67
68
|
)
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 1.7.0
|
5
|
+
module RedisQueuedLocks::Acquirer::AcquireLock::YieldExpire::LogVisitor
|
6
|
+
class << self
|
7
|
+
# @param logger [::Logger,#debug]
|
8
|
+
# @param log_sampled [Boolean]
|
9
|
+
# @param lock_key [String]
|
10
|
+
# @param queue_ttl [Integer]
|
11
|
+
# @param acquirer_id [String]
|
12
|
+
# @param host_id [String]
|
13
|
+
# @param access_strategy [Symbol]
|
14
|
+
# @return [void]
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
# @since 1.7.0
|
18
|
+
# @version 1.9.0
|
19
|
+
def expire_lock(
|
20
|
+
logger,
|
21
|
+
log_sampled,
|
22
|
+
lock_key,
|
23
|
+
queue_ttl,
|
24
|
+
acquirer_id,
|
25
|
+
host_id,
|
26
|
+
access_strategy
|
27
|
+
)
|
28
|
+
return unless log_sampled
|
29
|
+
|
30
|
+
logger.debug do
|
31
|
+
"[redis_queued_locks.expire_lock] " \
|
32
|
+
"lock_key => '#{lock_key}' " \
|
33
|
+
"queue_ttl => #{queue_ttl} " \
|
34
|
+
"acq_id => '#{acquirer_id}' " \
|
35
|
+
"hst_id => '#{host_id}' " \
|
36
|
+
"acs_strat => '#{access_strategy}'"
|
37
|
+
end rescue nil
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param logger [::Logger,#debug]
|
41
|
+
# @param log_sampled [Boolean]
|
42
|
+
# @param lock_key [String]
|
43
|
+
# @param decreased_ttl [Integer]
|
44
|
+
# @param queue_ttl [Integer]
|
45
|
+
# @param acquirer_id [String]
|
46
|
+
# @param host_id [String]
|
47
|
+
# @param access_strategy [Symbol]
|
48
|
+
# @return [void]
|
49
|
+
#
|
50
|
+
# @api private
|
51
|
+
# @since 1.7.0
|
52
|
+
# @version 1.9.0
|
53
|
+
def decrease_lock(
|
54
|
+
logger,
|
55
|
+
log_sampled,
|
56
|
+
lock_key,
|
57
|
+
decreased_ttl,
|
58
|
+
queue_ttl,
|
59
|
+
acquirer_id,
|
60
|
+
host_id,
|
61
|
+
access_strategy
|
62
|
+
)
|
63
|
+
return unless log_sampled
|
64
|
+
|
65
|
+
logger.debug do
|
66
|
+
"[redis_queued_locks.decrease_lock] " \
|
67
|
+
"lock_key => '#{lock_key}' " \
|
68
|
+
"decreased_ttl => #{decreased_ttl} " \
|
69
|
+
"queue_ttl => #{queue_ttl} " \
|
70
|
+
"acq_id => '#{acquirer_id}' " \
|
71
|
+
"hst_id => '#{host_id}' " \
|
72
|
+
"acs_strat => '#{access_strategy}'"
|
73
|
+
end rescue nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|