redis_queued_locks 1.7.0 → 1.9.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/.ruby-version +1 -1
- data/CHANGELOG.md +60 -1
- data/README.md +485 -46
- data/lib/redis_queued_locks/acquier/acquire_lock/dequeue_from_lock_queue/log_visitor.rb +4 -0
- data/lib/redis_queued_locks/acquier/acquire_lock/dequeue_from_lock_queue.rb +4 -1
- data/lib/redis_queued_locks/acquier/acquire_lock/instr_visitor.rb +20 -5
- data/lib/redis_queued_locks/acquier/acquire_lock/log_visitor.rb +24 -0
- data/lib/redis_queued_locks/acquier/acquire_lock/try_to_lock/log_visitor.rb +56 -0
- data/lib/redis_queued_locks/acquier/acquire_lock/try_to_lock.rb +37 -30
- data/lib/redis_queued_locks/acquier/acquire_lock/with_acq_timeout.rb +41 -7
- data/lib/redis_queued_locks/acquier/acquire_lock/yield_expire/log_visitor.rb +8 -0
- data/lib/redis_queued_locks/acquier/acquire_lock/yield_expire.rb +21 -9
- data/lib/redis_queued_locks/acquier/acquire_lock.rb +61 -22
- data/lib/redis_queued_locks/acquier/clear_dead_requests.rb +5 -1
- data/lib/redis_queued_locks/acquier/extend_lock_ttl.rb +5 -1
- data/lib/redis_queued_locks/acquier/lock_info.rb +4 -3
- data/lib/redis_queued_locks/acquier/locks.rb +2 -2
- data/lib/redis_queued_locks/acquier/queue_info.rb +2 -2
- data/lib/redis_queued_locks/acquier/release_all_locks.rb +12 -2
- data/lib/redis_queued_locks/acquier/release_lock.rb +12 -2
- data/lib/redis_queued_locks/client.rb +320 -10
- data/lib/redis_queued_locks/errors.rb +8 -0
- data/lib/redis_queued_locks/instrument.rb +8 -1
- data/lib/redis_queued_locks/logging.rb +8 -1
- data/lib/redis_queued_locks/resource.rb +59 -1
- data/lib/redis_queued_locks/swarm/acquirers.rb +44 -0
- data/lib/redis_queued_locks/swarm/flush_zombies.rb +133 -0
- data/lib/redis_queued_locks/swarm/probe_hosts.rb +69 -0
- data/lib/redis_queued_locks/swarm/redis_client_builder.rb +67 -0
- data/lib/redis_queued_locks/swarm/supervisor.rb +83 -0
- data/lib/redis_queued_locks/swarm/swarm_element/isolated.rb +287 -0
- data/lib/redis_queued_locks/swarm/swarm_element/threaded.rb +351 -0
- data/lib/redis_queued_locks/swarm/swarm_element.rb +8 -0
- data/lib/redis_queued_locks/swarm/zombie_info.rb +145 -0
- data/lib/redis_queued_locks/swarm.rb +241 -0
- data/lib/redis_queued_locks/utilities/lock.rb +22 -0
- data/lib/redis_queued_locks/utilities.rb +75 -0
- data/lib/redis_queued_locks/version.rb +2 -2
- data/lib/redis_queued_locks.rb +2 -0
- data/redis_queued_locks.gemspec +6 -10
- metadata +24 -6
- data/lib/redis_queued_locks/watcher.rb +0 -1
@@ -3,29 +3,63 @@
|
|
3
3
|
# @api private
|
4
4
|
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Acquier::AcquireLock::WithAcqTimeout
|
6
|
+
# @param redis [RedisClient]
|
7
|
+
# Redis connection manager required for additional data extraction for error message.
|
6
8
|
# @param timeout [NilClass,Integer]
|
7
9
|
# Time period after which the logic will fail with timeout error.
|
8
10
|
# @param lock_key [String]
|
9
|
-
# Lock name.
|
11
|
+
# Lock name in RQL notation (rql:lock:some-lock-name).
|
12
|
+
# @param lock_name [String]
|
13
|
+
# Original lock name passed by the businessl logic (without RQL notaiton parts).
|
10
14
|
# @param raise_errors [Boolean]
|
11
15
|
# Raise erros on exceptional cases.
|
16
|
+
# @param detailed_acq_timeout_error [Boolean]
|
17
|
+
# Add additional error data about lock queue and required lock to the timeout error or not.
|
12
18
|
# @option on_timeout [Proc,NilClass]
|
13
19
|
# Callback invoked on Timeout::Error.
|
14
20
|
# @return [Any]
|
15
21
|
#
|
16
22
|
# @api private
|
17
23
|
# @since 1.0.0
|
18
|
-
|
24
|
+
# @version 1.8.0
|
25
|
+
def with_acq_timeout(
|
26
|
+
redis,
|
27
|
+
timeout,
|
28
|
+
lock_key,
|
29
|
+
lock_name,
|
30
|
+
raise_errors,
|
31
|
+
detailed_acq_timeout_error,
|
32
|
+
on_timeout: nil,
|
33
|
+
&block
|
34
|
+
)
|
19
35
|
::Timeout.timeout(timeout, &block)
|
20
36
|
rescue ::Timeout::Error
|
21
37
|
on_timeout.call unless on_timeout == nil
|
22
38
|
|
23
39
|
if raise_errors
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
40
|
+
if detailed_acq_timeout_error
|
41
|
+
# TODO: rewrite these invocations to separated inner-AcquireLock-related modules
|
42
|
+
# in order to remove any dependencies from the other public RQL commands cuz
|
43
|
+
# all AcquireLock logic elements should be fully independent from others as a core;
|
44
|
+
lock_info = RedisQueuedLocks::Acquier::LockInfo.lock_info(redis, lock_name)
|
45
|
+
queue_info = RedisQueuedLocks::Acquier::QueueInfo.queue_info(redis, lock_name)
|
46
|
+
|
47
|
+
# rubocop:disable Metrics/BlockNesting
|
48
|
+
raise(
|
49
|
+
RedisQueuedLocks::LockAcquiermentTimeoutError,
|
50
|
+
"Failed to acquire the lock \"#{lock_key}\" " \
|
51
|
+
"for the given <#{timeout} seconds> timeout. Details: " \
|
52
|
+
"<Lock Data> => #{lock_info ? lock_info.inspect : '<no_data>'}; " \
|
53
|
+
"<Queue Data> => #{queue_info ? queue_info.inspect : '<no_data>'};"
|
54
|
+
)
|
55
|
+
# rubocop:enable Metrics/BlockNesting
|
56
|
+
else
|
57
|
+
raise(
|
58
|
+
RedisQueuedLocks::LockAcquiermentTimeoutError,
|
59
|
+
"Failed to acquire the lock \"#{lock_key}\" " \
|
60
|
+
"for the given <#{timeout} seconds> timeout."
|
61
|
+
)
|
62
|
+
end
|
29
63
|
end
|
30
64
|
end
|
31
65
|
end
|
@@ -10,17 +10,20 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldExpire::LogVisitor
|
|
10
10
|
# @param lock_key [String]
|
11
11
|
# @param queue_ttl [Integer]
|
12
12
|
# @param acquier_id [String]
|
13
|
+
# @param host_id [String]
|
13
14
|
# @param access_strategy [Symbol]
|
14
15
|
# @return [void]
|
15
16
|
#
|
16
17
|
# @api private
|
17
18
|
# @since 1.7.0
|
19
|
+
# @version 1.9.0
|
18
20
|
def expire_lock(
|
19
21
|
logger,
|
20
22
|
log_sampled,
|
21
23
|
lock_key,
|
22
24
|
queue_ttl,
|
23
25
|
acquier_id,
|
26
|
+
host_id,
|
24
27
|
access_strategy
|
25
28
|
)
|
26
29
|
return unless log_sampled
|
@@ -30,6 +33,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldExpire::LogVisitor
|
|
30
33
|
"lock_key => '#{lock_key}' " \
|
31
34
|
"queue_ttl => #{queue_ttl} " \
|
32
35
|
"acq_id => '#{acquier_id}' " \
|
36
|
+
"hst_id => '#{host_id}' " \
|
33
37
|
"acs_strat => '#{access_strategy}'"
|
34
38
|
end rescue nil
|
35
39
|
end
|
@@ -40,11 +44,13 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldExpire::LogVisitor
|
|
40
44
|
# @param decreased_ttl [Integer]
|
41
45
|
# @param queue_ttl [Integer]
|
42
46
|
# @param acquier_id [String]
|
47
|
+
# @param host_id [String]
|
43
48
|
# @param access_strategy [Symbol]
|
44
49
|
# @return [void]
|
45
50
|
#
|
46
51
|
# @api private
|
47
52
|
# @since 1.7.0
|
53
|
+
# @version 1.9.0
|
48
54
|
def decrease_lock(
|
49
55
|
logger,
|
50
56
|
log_sampled,
|
@@ -52,6 +58,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldExpire::LogVisitor
|
|
52
58
|
decreased_ttl,
|
53
59
|
queue_ttl,
|
54
60
|
acquier_id,
|
61
|
+
host_id,
|
55
62
|
access_strategy
|
56
63
|
)
|
57
64
|
return unless log_sampled
|
@@ -62,6 +69,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldExpire::LogVisitor
|
|
62
69
|
"decreased_ttl => #{decreased_ttl} " \
|
63
70
|
"queue_ttl => #{queue_ttl} " \
|
64
71
|
"acq_id => '#{acquier_id}' " \
|
72
|
+
"hst_id => '#{host_id}' " \
|
65
73
|
"acs_strat => '#{access_strategy}'"
|
66
74
|
end rescue nil
|
67
75
|
end
|
@@ -19,12 +19,14 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldExpire
|
|
19
19
|
# @param logger [::Logger,#debug] Logger object.
|
20
20
|
# @param lock_key [String] Obtained lock key that should be expired.
|
21
21
|
# @param acquier_id [String] Acquier identifier.
|
22
|
+
# @param host_id [String] Host identifier.
|
22
23
|
# @param access_strategy [Symbol] Lock obtaining strategy.
|
23
24
|
# @param timed [Boolean] Should the lock be wrapped by Timeout with with lock's ttl
|
24
25
|
# @param ttl_shift [Float] Lock's TTL shifting. Should affect block's ttl. In millisecodns.
|
25
26
|
# @param ttl [Integer,NilClass] Lock's time to live (in ms). Nil means "without timeout".
|
26
27
|
# @param queue_ttl [Integer] Lock request lifetime.
|
27
28
|
# @param block [Block] Custom logic that should be invoked unter the obtained lock.
|
29
|
+
# @param meta [NilClass,Hash<String|Symbol,Any>] Custom metadata wich is passed to the lock data;
|
28
30
|
# @param log_sampled [Boolean] Should the logic be logged or not.
|
29
31
|
# @param instr_sampled [Boolean] Should the logic be instrumented or not.
|
30
32
|
# @param should_expire [Boolean] Should the lock be expired after the block invocation.
|
@@ -35,18 +37,20 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldExpire
|
|
35
37
|
#
|
36
38
|
# @api private
|
37
39
|
# @since 1.3.0
|
38
|
-
# @version 1.
|
40
|
+
# @version 1.9.0
|
39
41
|
# rubocop:disable Metrics/MethodLength
|
40
42
|
def yield_expire(
|
41
43
|
redis,
|
42
44
|
logger,
|
43
45
|
lock_key,
|
44
46
|
acquier_id,
|
47
|
+
host_id,
|
45
48
|
access_strategy,
|
46
49
|
timed,
|
47
50
|
ttl_shift,
|
48
51
|
ttl,
|
49
52
|
queue_ttl,
|
53
|
+
meta,
|
50
54
|
log_sampled,
|
51
55
|
instr_sampled,
|
52
56
|
should_expire,
|
@@ -62,7 +66,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldExpire
|
|
62
66
|
end
|
63
67
|
|
64
68
|
if timed && ttl != nil
|
65
|
-
yield_with_timeout(timeout, lock_key, ttl, &block)
|
69
|
+
yield_with_timeout(timeout, lock_key, ttl, acquier_id, host_id, meta, &block)
|
66
70
|
else
|
67
71
|
yield
|
68
72
|
end
|
@@ -70,8 +74,8 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldExpire
|
|
70
74
|
ensure
|
71
75
|
if should_expire
|
72
76
|
LogVisitor.expire_lock(
|
73
|
-
logger, log_sampled,
|
74
|
-
|
77
|
+
logger, log_sampled, lock_key,
|
78
|
+
queue_ttl, acquier_id, host_id, access_strategy
|
75
79
|
)
|
76
80
|
redis.call('EXPIRE', lock_key, '0')
|
77
81
|
elsif should_decrease
|
@@ -81,8 +85,8 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldExpire
|
|
81
85
|
|
82
86
|
if decreased_ttl > 0
|
83
87
|
LogVisitor.decrease_lock(
|
84
|
-
logger, log_sampled,
|
85
|
-
|
88
|
+
logger, log_sampled, lock_key,
|
89
|
+
decreased_ttl, queue_ttl, acquier_id, host_id, access_strategy
|
86
90
|
)
|
87
91
|
# NOTE:# NOTE: EVAL signature -> <lua script>, (number of keys), *(keys), *(arguments)
|
88
92
|
redis.call('EVAL', DECREASE_LOCK_PTTL, 1, lock_key, decreased_ttl)
|
@@ -97,18 +101,26 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldExpire
|
|
97
101
|
# @param timeout [Float]
|
98
102
|
# @parma lock_key [String]
|
99
103
|
# @param lock_ttl [Integer,NilClass]
|
104
|
+
# @param acquier_id [String]
|
105
|
+
# @param host_id [String]
|
106
|
+
# @param meta [NilClass,Hash<Symbol|String,Any>]
|
100
107
|
# @param block [Blcok]
|
101
108
|
# @return [Any]
|
102
109
|
#
|
103
110
|
# @api private
|
104
111
|
# @since 1.3.0
|
105
|
-
|
112
|
+
# @version 1.9.0
|
113
|
+
def yield_with_timeout(timeout, lock_key, lock_ttl, acquier_id, host_id, meta, &block)
|
106
114
|
::Timeout.timeout(timeout, &block)
|
107
115
|
rescue ::Timeout::Error
|
108
116
|
raise(
|
109
117
|
RedisQueuedLocks::TimedLockTimeoutError,
|
110
|
-
"Passed <timed> block of code exceeded " \
|
111
|
-
"
|
118
|
+
"Passed <timed> block of code exceeded the lock TTL " \
|
119
|
+
"(lock: \"#{lock_key}\", " \
|
120
|
+
"ttl: #{lock_ttl}, " \
|
121
|
+
"meta: #{meta ? meta.inspect : '<no-meta>'}, " \
|
122
|
+
"acq_id: \"#{acquier_id}\", " \
|
123
|
+
"hst_id: \"#{host_id}\")"
|
112
124
|
)
|
113
125
|
end
|
114
126
|
end
|
@@ -69,6 +69,10 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
69
69
|
# @option meta [NilClass,Hash<String|Symbol,Any>]
|
70
70
|
# - A custom metadata wich will be passed to the lock data in addition to the existing data;
|
71
71
|
# - Metadata can not contain reserved lock data keys;
|
72
|
+
# @option detailed_acq_timeout_error [Boolean]
|
73
|
+
# - Add additional data to the acquirement timeout error such as the current lock queue state
|
74
|
+
# and the required lock state;
|
75
|
+
# - See `config[:detailed_acq_timeout_error]` for details;
|
72
76
|
# @option logger [::Logger,#debug]
|
73
77
|
# - Logger object used from the configuration layer (see config[:logger]);
|
74
78
|
# - See `RedisQueuedLocks::Logging::VoidLogger` for example;
|
@@ -117,6 +121,9 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
117
121
|
# - you can provide your own log sampler with bettter algorithm that should realize
|
118
122
|
# `sampling_happened?(percent) => boolean` interface
|
119
123
|
# (see `RedisQueuedLocks::Logging::Sampler` for example);
|
124
|
+
# @option log_sample_this [Boolean]
|
125
|
+
# - marks the method that everything should be logged despite the enabled log sampling;
|
126
|
+
# - makes sense when log sampling is enabled;
|
120
127
|
# @option instr_sampling_enabled [Boolean]
|
121
128
|
# - enables <instrumentaion sampling>: only the configured percent
|
122
129
|
# of RQL cases will be instrumented;
|
@@ -135,6 +142,10 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
135
142
|
# - you can provide your own log sampler with bettter algorithm that should realize
|
136
143
|
# `sampling_happened?(percent) => boolean` interface
|
137
144
|
# (see `RedisQueuedLocks::Instrument::Sampler` for example);
|
145
|
+
# @option instr_sample_this [Boolean]
|
146
|
+
# - marks the method that everything should be instrumneted
|
147
|
+
# despite the enabled instrumentation sampling;
|
148
|
+
# - makes sense when instrumentation sampling is enabled;
|
138
149
|
# @param [Block]
|
139
150
|
# A block of code that should be executed after the successfully acquired lock.
|
140
151
|
# @return [RedisQueuedLocks::Data,Hash<Symbol,Any>,yield]
|
@@ -143,7 +154,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
143
154
|
#
|
144
155
|
# @api private
|
145
156
|
# @since 1.0.0
|
146
|
-
# @version 1.
|
157
|
+
# @version 1.9.0
|
147
158
|
def acquire_lock(
|
148
159
|
redis,
|
149
160
|
lock_name,
|
@@ -163,6 +174,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
163
174
|
identity:,
|
164
175
|
fail_fast:,
|
165
176
|
meta:,
|
177
|
+
detailed_acq_timeout_error:,
|
166
178
|
instrument:,
|
167
179
|
logger:,
|
168
180
|
log_lock_try:,
|
@@ -171,9 +183,11 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
171
183
|
log_sampling_enabled:,
|
172
184
|
log_sampling_percent:,
|
173
185
|
log_sampler:,
|
186
|
+
log_sample_this:,
|
174
187
|
instr_sampling_enabled:,
|
175
188
|
instr_sampling_percent:,
|
176
189
|
instr_sampler:,
|
190
|
+
instr_sample_this:,
|
177
191
|
&block
|
178
192
|
)
|
179
193
|
# Step 0: Prevent argument type incompatabilities
|
@@ -190,6 +204,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
190
204
|
# Step 0.2: prevent :meta incompatabiltiies (structure)
|
191
205
|
if meta.is_a?(::Hash) && (meta.any? do |key, _value|
|
192
206
|
key == 'acq_id' ||
|
207
|
+
key == 'hst_id' ||
|
193
208
|
key == 'ts' ||
|
194
209
|
key == 'ini_ttl' ||
|
195
210
|
key == 'lock_key' ||
|
@@ -203,7 +218,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
203
218
|
raise(
|
204
219
|
RedisQueuedLocks::ArgumentError,
|
205
220
|
'`:meta` keys can not overlap reserved lock data keys ' \
|
206
|
-
'"acq_id", "ts", "ini_ttl", "lock_key", "rem_ttl", "spc_cnt", ' \
|
221
|
+
'"acq_id", "hst_id", "ts", "ini_ttl", "lock_key", "rem_ttl", "spc_cnt", ' \
|
207
222
|
'"spc_ext_ttl", "l_spc_ext_ini_ttl", "l_spc_ext_ts", "l_spc_ts"'
|
208
223
|
)
|
209
224
|
end
|
@@ -216,6 +231,12 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
216
231
|
ractor_id,
|
217
232
|
identity
|
218
233
|
)
|
234
|
+
host_id = RedisQueuedLocks::Resource.host_identifier(
|
235
|
+
process_id,
|
236
|
+
thread_id,
|
237
|
+
ractor_id,
|
238
|
+
identity
|
239
|
+
)
|
219
240
|
lock_ttl = ttl
|
220
241
|
lock_key = RedisQueuedLocks::Resource.prepare_lock_key(lock_name)
|
221
242
|
lock_key_queue = RedisQueuedLocks::Resource.prepare_lock_queue(lock_name)
|
@@ -223,11 +244,13 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
223
244
|
|
224
245
|
log_sampled = RedisQueuedLocks::Logging.should_log?(
|
225
246
|
log_sampling_enabled,
|
247
|
+
log_sample_this,
|
226
248
|
log_sampling_percent,
|
227
249
|
log_sampler
|
228
250
|
)
|
229
251
|
instr_sampled = RedisQueuedLocks::Instrument.should_instrument?(
|
230
252
|
instr_sampling_enabled,
|
253
|
+
instr_sample_this,
|
231
254
|
instr_sampling_percent,
|
232
255
|
instr_sampler
|
233
256
|
)
|
@@ -246,11 +269,13 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
246
269
|
|
247
270
|
acq_dequeue = proc do
|
248
271
|
dequeue_from_lock_queue(
|
249
|
-
redis,
|
272
|
+
redis,
|
273
|
+
logger,
|
250
274
|
lock_key,
|
251
275
|
lock_key_queue,
|
252
276
|
queue_ttl,
|
253
277
|
acquier_id,
|
278
|
+
host_id,
|
254
279
|
access_strategy,
|
255
280
|
log_sampled,
|
256
281
|
instr_sampled
|
@@ -258,20 +283,28 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
258
283
|
end
|
259
284
|
|
260
285
|
LogVisitor.start_lock_obtaining(
|
261
|
-
logger, log_sampled,
|
262
|
-
|
286
|
+
logger, log_sampled, lock_key,
|
287
|
+
queue_ttl, acquier_id, host_id, access_strategy
|
263
288
|
)
|
264
289
|
|
265
290
|
# Step 2: try to lock with timeout
|
266
|
-
with_acq_timeout(
|
291
|
+
with_acq_timeout(
|
292
|
+
redis,
|
293
|
+
timeout,
|
294
|
+
lock_key,
|
295
|
+
lock_name,
|
296
|
+
raise_errors,
|
297
|
+
detailed_acq_timeout_error,
|
298
|
+
on_timeout: acq_dequeue
|
299
|
+
) do
|
267
300
|
acq_start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
|
268
301
|
|
269
302
|
# Step 2.1: cyclically try to obtain the lock
|
270
303
|
while acq_process[:should_try]
|
271
304
|
|
272
305
|
LogVisitor.start_try_to_lock_cycle(
|
273
|
-
logger, log_sampled,
|
274
|
-
|
306
|
+
logger, log_sampled, lock_key,
|
307
|
+
queue_ttl, acquier_id, host_id, access_strategy
|
275
308
|
)
|
276
309
|
|
277
310
|
# Step 2.X: check the actual score: is it in queue ttl limit or not?
|
@@ -280,8 +313,8 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
280
313
|
acquier_position = RedisQueuedLocks::Resource.calc_initial_acquier_position
|
281
314
|
|
282
315
|
LogVisitor.dead_score_reached__reset_acquier_position(
|
283
|
-
logger, log_sampled,
|
284
|
-
|
316
|
+
logger, log_sampled, lock_key,
|
317
|
+
queue_ttl, acquier_id, host_id, access_strategy
|
285
318
|
)
|
286
319
|
end
|
287
320
|
|
@@ -292,6 +325,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
292
325
|
lock_key,
|
293
326
|
lock_key_queue,
|
294
327
|
acquier_id,
|
328
|
+
host_id,
|
295
329
|
acquier_position,
|
296
330
|
lock_ttl,
|
297
331
|
queue_ttl,
|
@@ -316,35 +350,35 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
316
350
|
if acq_process[:result][:process] == :extendable_conflict_work_through
|
317
351
|
# instrumetnation: (reentrant lock with ttl extension)
|
318
352
|
LogVisitor.extendable_reentrant_lock_obtained(
|
319
|
-
logger, log_sampled,
|
320
|
-
|
353
|
+
logger, log_sampled, result[:lock_key],
|
354
|
+
queue_ttl, acquier_id, host_id, acq_time, access_strategy
|
321
355
|
)
|
322
356
|
InstrVisitor.extendable_reentrant_lock_obtained(
|
323
|
-
instrumenter, instr_sampled,
|
324
|
-
result[:
|
357
|
+
instrumenter, instr_sampled, result[:lock_key],
|
358
|
+
result[:ttl], result[:acq_id], result[:hst_id], result[:ts], acq_time,
|
325
359
|
instrument
|
326
360
|
)
|
327
361
|
elsif acq_process[:result][:process] == :conflict_work_through
|
328
362
|
# instrumetnation: (reentrant lock without ttl extension)
|
329
363
|
LogVisitor.reentrant_lock_obtained(
|
330
|
-
logger, log_sampled,
|
331
|
-
|
364
|
+
logger, log_sampled, result[:lock_key],
|
365
|
+
queue_ttl, acquier_id, host_id, acq_time, access_strategy
|
332
366
|
)
|
333
367
|
InstrVisitor.reentrant_lock_obtained(
|
334
|
-
instrumenter, instr_sampled,
|
335
|
-
result[:
|
368
|
+
instrumenter, instr_sampled, result[:lock_key],
|
369
|
+
result[:ttl], result[:acq_id], result[:hst_id], result[:ts], acq_time,
|
336
370
|
instrument
|
337
371
|
)
|
338
372
|
else
|
339
373
|
# instrumentation: (classic lock obtain)
|
340
374
|
# NOTE: classic is: acq_process[:result][:process] == :lock_obtaining
|
341
375
|
LogVisitor.lock_obtained(
|
342
|
-
logger, log_sampled,
|
343
|
-
|
376
|
+
logger, log_sampled, result[:lock_key],
|
377
|
+
queue_ttl, acquier_id, host_id, acq_time, access_strategy
|
344
378
|
)
|
345
379
|
InstrVisitor.lock_obtained(
|
346
|
-
instrumenter, instr_sampled,
|
347
|
-
result[:
|
380
|
+
instrumenter, instr_sampled, result[:lock_key],
|
381
|
+
result[:ttl], result[:acq_id], result[:hst_id], result[:ts], acq_time,
|
348
382
|
instrument
|
349
383
|
)
|
350
384
|
end
|
@@ -353,6 +387,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
353
387
|
acq_process[:lock_info] = {
|
354
388
|
lock_key: result[:lock_key],
|
355
389
|
acq_id: result[:acq_id],
|
390
|
+
hst_id: result[:hst_id],
|
356
391
|
ts: result[:ts],
|
357
392
|
ttl: result[:ttl],
|
358
393
|
process: result[:process]
|
@@ -450,11 +485,13 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
450
485
|
logger,
|
451
486
|
lock_key,
|
452
487
|
acquier_id,
|
488
|
+
host_id,
|
453
489
|
access_strategy,
|
454
490
|
timed,
|
455
491
|
ttl_shift,
|
456
492
|
ttl,
|
457
493
|
queue_ttl,
|
494
|
+
meta,
|
458
495
|
log_sampled,
|
459
496
|
instr_sampled,
|
460
497
|
should_expire, # NOTE: should expire the lock after the block execution
|
@@ -478,6 +515,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
478
515
|
acq_process[:lock_info][:lock_key],
|
479
516
|
acq_process[:lock_info][:ttl],
|
480
517
|
acq_process[:lock_info][:acq_id],
|
518
|
+
acq_process[:lock_info][:hst_id],
|
481
519
|
acq_process[:lock_info][:ts],
|
482
520
|
acq_process[:acq_time],
|
483
521
|
acq_process[:hold_time],
|
@@ -491,6 +529,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
491
529
|
acq_process[:lock_info][:lock_key],
|
492
530
|
acq_process[:lock_info][:ttl],
|
493
531
|
acq_process[:lock_info][:acq_id],
|
532
|
+
acq_process[:lock_info][:hst_id],
|
494
533
|
acq_process[:lock_info][:ts],
|
495
534
|
acq_process[:lock_info][:lock_key],
|
496
535
|
acq_process[:acq_time],
|
@@ -13,9 +13,11 @@ module RedisQueuedLocks::Acquier::ClearDeadRequests
|
|
13
13
|
# @param log_sampling_enabled [Boolean]
|
14
14
|
# @param log_sampling_percent [Integer]
|
15
15
|
# @param log_sampler [#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]
|
16
|
+
# @param log_sample_this [Boolean]
|
16
17
|
# @param instr_sampling_enabled [Boolean]
|
17
18
|
# @param instr_sampling_percent [Integer]
|
18
19
|
# @param instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
|
20
|
+
# @param instr_sample_this [Boolean]
|
19
21
|
# @return [Hash<Symbol,Boolean|Hash<Symbol,Set<String>>>]
|
20
22
|
#
|
21
23
|
# @api private
|
@@ -31,9 +33,11 @@ module RedisQueuedLocks::Acquier::ClearDeadRequests
|
|
31
33
|
log_sampling_enabled,
|
32
34
|
log_sampling_percent,
|
33
35
|
log_sampler,
|
36
|
+
log_sample_this,
|
34
37
|
instr_sampling_enabled,
|
35
38
|
instr_sampling_percent,
|
36
|
-
instr_sampler
|
39
|
+
instr_sampler,
|
40
|
+
instr_sample_this
|
37
41
|
)
|
38
42
|
dead_score = RedisQueuedLocks::Resource.acquier_dead_score(dead_ttl / 1_000.0)
|
39
43
|
|
@@ -22,9 +22,11 @@ module RedisQueuedLocks::Acquier::ExtendLockTTL
|
|
22
22
|
# @param log_sampling_enabled [Boolean]
|
23
23
|
# @param log_sampling_percent [Integer]
|
24
24
|
# @param log_sampler [#sampling_happened?,Module<RedisQueuedLocks::Logging::Sampler>]
|
25
|
+
# @param log_sample_this [Boolean]
|
25
26
|
# @param instr_sampling_enabled [Boolean]
|
26
27
|
# @param instr_sampling_percent [Integer]
|
27
28
|
# @param instr_sampler [#sampling_happened?,Module<RedisQueuedLocks::Instrument::Sampler>]
|
29
|
+
# @param instr_sample_this [Boolean]
|
28
30
|
# @return [Hash<Symbol,Boolean|Symbol>]
|
29
31
|
#
|
30
32
|
# @api private
|
@@ -40,9 +42,11 @@ module RedisQueuedLocks::Acquier::ExtendLockTTL
|
|
40
42
|
log_sampling_enabled,
|
41
43
|
log_sampling_percent,
|
42
44
|
log_sampler,
|
45
|
+
log_sample_this,
|
43
46
|
instr_sampling_enabled,
|
44
47
|
instr_sampling_percent,
|
45
|
-
instr_sampler
|
48
|
+
instr_sampler,
|
49
|
+
instr_sample_this
|
46
50
|
)
|
47
51
|
lock_key = RedisQueuedLocks::Resource.prepare_lock_key(lock_name)
|
48
52
|
|
@@ -10,7 +10,8 @@ module RedisQueuedLocks::Acquier::LockInfo
|
|
10
10
|
# - `nil` is returned when lock key does not exist or expired;
|
11
11
|
# - result format: {
|
12
12
|
# 'lock_key' => "rql:lock:your_lockname", # acquired lock key
|
13
|
-
# 'acq_id' => "rql:acq:
|
13
|
+
# 'acq_id' => "rql:acq:123/456/789/987/uniqstring", # lock acquier identifier
|
14
|
+
# 'hst_id' => "rql:hst:123/456/987/uniqstring", # lock host identifier
|
14
15
|
# 'ts' => 123456789.2649841, # <locked at> time stamp (epoch, seconds.microseconds)
|
15
16
|
# 'ini_ttl' => 123456789, # initial lock key ttl (milliseconds)
|
16
17
|
# 'rem_ttl' => 123456789, # remaining lock key ttl (milliseconds)
|
@@ -24,7 +25,7 @@ module RedisQueuedLocks::Acquier::LockInfo
|
|
24
25
|
#
|
25
26
|
# @api private
|
26
27
|
# @since 1.0.0
|
27
|
-
# @version 1.
|
28
|
+
# @version 1.9.0
|
28
29
|
# rubocop:disable Metrics/MethodLength
|
29
30
|
def lock_info(redis_client, lock_name)
|
30
31
|
lock_key = RedisQueuedLocks::Resource.prepare_lock_key(lock_name)
|
@@ -55,7 +56,7 @@ module RedisQueuedLocks::Acquier::LockInfo
|
|
55
56
|
lock_data['lock_key'] = lock_key
|
56
57
|
lock_data['ts'] = Float(lock_data['ts'])
|
57
58
|
lock_data['ini_ttl'] = Integer(lock_data['ini_ttl'])
|
58
|
-
lock_data['rem_ttl'] = ((pttl_cmd_res == -1) ?
|
59
|
+
lock_data['rem_ttl'] = ((pttl_cmd_res == -1) ? Float::INFINITY : pttl_cmd_res)
|
59
60
|
lock_data['spc_cnt'] = Integer(lock_data['spc_cnt']) if lock_data['spc_cnt']
|
60
61
|
lock_data['l_spc_ts'] = Float(lock_data['l_spc_ts']) if lock_data['l_spc_ts']
|
61
62
|
lock_data['spc_ext_ttl'] = Integer(lock_data['spc_ext_ttl']) if lock_data['spc_ext_ttl']
|
@@ -45,7 +45,7 @@ module RedisQueuedLocks::Acquier::Locks
|
|
45
45
|
#
|
46
46
|
# @api private
|
47
47
|
# @since 1.0.0
|
48
|
-
# @version 1.
|
48
|
+
# @version 1.9.0
|
49
49
|
# rubocop:disable Metrics/MethodLength
|
50
50
|
def extract_locks_info(redis_client, lock_keys)
|
51
51
|
# TODO: refactor with RedisQueuedLocks::Acquier::LockInfo
|
@@ -72,7 +72,7 @@ module RedisQueuedLocks::Acquier::Locks
|
|
72
72
|
hget_cmd_res.tap do |lock_data|
|
73
73
|
lock_data['ts'] = Float(lock_data['ts'])
|
74
74
|
lock_data['ini_ttl'] = Integer(lock_data['ini_ttl'])
|
75
|
-
lock_data['rem_ttl'] = ((pttl_cmd_res == -1) ?
|
75
|
+
lock_data['rem_ttl'] = ((pttl_cmd_res == -1) ? Float::INFINITY : pttl_cmd_res)
|
76
76
|
lock_data['spc_cnt'] = Integer(lock_data['spc_cnt']) if lock_data['spc_cnt']
|
77
77
|
lock_data['l_spc_ts'] = Float(lock_data['l_spc_ts']) if lock_data['l_spc_ts']
|
78
78
|
lock_data['spc_ext_ttl'] =
|
@@ -17,8 +17,8 @@ module RedisQueuedLocks::Acquier::QueueInfo
|
|
17
17
|
# - result format: {
|
18
18
|
# "lock_queue" => "rql:lock_queue:your_lock_name", # lock queue key in redis,
|
19
19
|
# queue: [
|
20
|
-
# { "acq_id" => "rql:acq:
|
21
|
-
# { "acq_id" => "rql:acq:
|
20
|
+
# { "acq_id" => "rql:acq:123/456/789/987/identity", "score" => 123 },
|
21
|
+
# { "acq_id" => "rql:acq:123/686/789/987/identity", "score" => 456 },
|
22
22
|
# ] # ordered set (by score) with information about an acquier and their position in queue
|
23
23
|
# }
|
24
24
|
#
|
@@ -40,6 +40,9 @@ module RedisQueuedLocks::Acquier::ReleaseAllLocks
|
|
40
40
|
# - you can provide your own log sampler with bettter algorithm that should realize
|
41
41
|
# `sampling_happened?(percent) => boolean` interface
|
42
42
|
# (see `RedisQueuedLocks::Logging::Sampler` for example);
|
43
|
+
# @param log_sample_this [Boolean]
|
44
|
+
# - marks the method that everything should be logged despite the enabled log sampling;
|
45
|
+
# - makes sense when log sampling is enabled;
|
43
46
|
# @param instr_sampling_enabled [Boolean]
|
44
47
|
# - enables <instrumentaion sampling>: only the configured percent
|
45
48
|
# of RQL cases will be instrumented;
|
@@ -58,6 +61,10 @@ module RedisQueuedLocks::Acquier::ReleaseAllLocks
|
|
58
61
|
# - you can provide your own log sampler with bettter algorithm that should realize
|
59
62
|
# `sampling_happened?(percent) => boolean` interface
|
60
63
|
# (see `RedisQueuedLocks::Instrument::Sampler` for example);
|
64
|
+
# @param instr_sample_this [Boolean]
|
65
|
+
# - marks the method that everything should be instrumneted
|
66
|
+
# despite the enabled instrumentation sampling;
|
67
|
+
# - makes sense when instrumentation sampling is enabled;
|
61
68
|
# @return [RedisQueuedLocks::Data,Hash<Symbol,Any>]
|
62
69
|
# Format: { ok: true, result: Hash<Symbol,Numeric> }
|
63
70
|
#
|
@@ -73,18 +80,21 @@ module RedisQueuedLocks::Acquier::ReleaseAllLocks
|
|
73
80
|
log_sampling_enabled,
|
74
81
|
log_sampling_percent,
|
75
82
|
log_sampler,
|
83
|
+
log_sample_this,
|
76
84
|
instr_sampling_enabled,
|
77
85
|
instr_sampling_percent,
|
78
|
-
instr_sampler
|
86
|
+
instr_sampler,
|
87
|
+
instr_sample_this
|
79
88
|
)
|
80
89
|
rel_start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
|
81
90
|
fully_release_all_locks(redis, batch_size) => { ok:, result: }
|
82
91
|
time_at = Time.now.to_f
|
83
92
|
rel_end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
|
84
|
-
rel_time = ((rel_end_time - rel_start_time) / 1_000).ceil(2)
|
93
|
+
rel_time = ((rel_end_time - rel_start_time) / 1_000.0).ceil(2)
|
85
94
|
|
86
95
|
instr_sampled = RedisQueuedLocks::Instrument.should_instrument?(
|
87
96
|
instr_sampling_enabled,
|
97
|
+
instr_sample_this,
|
88
98
|
instr_sampling_percent,
|
89
99
|
instr_sampler
|
90
100
|
)
|