redis_queued_locks 0.0.29 → 0.0.30
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/CHANGELOG.md +12 -1
- data/lib/redis_queued_locks/acquier/acquire_lock/try_to_lock.rb +34 -3
- data/lib/redis_queued_locks/acquier/acquire_lock/yield_with_expire.rb +13 -1
- data/lib/redis_queued_locks/acquier/acquire_lock.rb +38 -2
- data/lib/redis_queued_locks/resource.rb +14 -0
- data/lib/redis_queued_locks/version.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0920957ed856cf515d2867ecd6ed7b1911ba53466822f09d5bd98386a5be4296'
|
4
|
+
data.tar.gz: 587fa64039e85a633fc2c65d5c0b012d7bfb29b9ccd25177e0895deb33850c38
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 388e0d43c66464672bbe019dc0f26e3bac44be0cc3e5dcdb502ccd9b2d0b82931de2608c6b7219b2c190b9024197a0cbf1263a7cc7d4f8ed6568480ce6120930
|
7
|
+
data.tar.gz: 56158aca2424c21fc3fcbeebbd33625559aceca82143c2e18700334c6485e919d4005f4391ba3a07b0839e9122f57b9c5d223ebfd4bc38bdd4f61b58bc94f34f
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,19 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.0.30] - 2024-03-23
|
4
|
+
### Fixed
|
5
|
+
- Re-enqueue problem: fixed a problem when the expired lock requests were infinitly re-added to the lock queue
|
6
|
+
and immediately removed from the lock queue rather than being re-positioned. It happens when the lock request
|
7
|
+
ttl reached the queue ttl, and the new request now had the dead score forever (fix: it's score now will be correctly
|
8
|
+
recalculated from the current time at the dead score time moment);
|
9
|
+
### Added
|
10
|
+
- Logging: more detailed logs to the `RedisQueuedLocks::Acquier::AcquierLock` logic and it's sub-modules:
|
11
|
+
- added new logs;
|
12
|
+
- added `queue_ttl` to each log;
|
13
|
+
|
3
14
|
## [0.0.29] - 2024-03-23
|
4
15
|
### Added
|
5
|
-
- Logging: more detailed logs to `RedisQueuedLocks::Acquier::AcquireLock::TryToLock`;
|
16
|
+
- Logging: added more detailed logs to `RedisQueuedLocks::Acquier::AcquireLock::TryToLock`;
|
6
17
|
|
7
18
|
## [0.0.28] - 2024-03-21
|
8
19
|
### Added
|
@@ -43,23 +43,26 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
43
43
|
logger.debug(
|
44
44
|
"[redis_queued_locks.try_lock.start] " \
|
45
45
|
"lock_key => '#{lock_key}' " \
|
46
|
-
"
|
46
|
+
"queue_ttl => #{queue_ttl} " \
|
47
|
+
"acq_id => '#{acquier_id}' "
|
47
48
|
)
|
48
49
|
end
|
49
50
|
end
|
50
51
|
|
51
|
-
# Step
|
52
|
+
# Step X: start to work with lock acquiring
|
52
53
|
result = redis.with do |rconn|
|
53
54
|
if log_lock_try
|
54
55
|
run_non_critical do
|
55
56
|
logger.debug(
|
56
57
|
"[redis_queued_locks.try_lock.rconn_fetched] " \
|
57
58
|
"lock_key => '#{lock_key}' " \
|
59
|
+
"queue_ttl => #{queue_ttl} " \
|
58
60
|
"acq_id => '#{acquier_id}'"
|
59
61
|
)
|
60
62
|
end
|
61
63
|
end
|
62
64
|
|
65
|
+
# Step 0: watch the lock key changes (and discard acquirement if lock is already acquired)
|
63
66
|
rconn.multi(watch: [lock_key]) do |transact|
|
64
67
|
# Fast-Step X0: fail-fast check
|
65
68
|
if fail_fast && rconn.call('HGET', lock_key, 'acq_id')
|
@@ -74,6 +77,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
74
77
|
logger.debug(
|
75
78
|
"[redis_queued_locks.try_lock.acq_added_to_queue] " \
|
76
79
|
"lock_key => '#{lock_key}' " \
|
80
|
+
"queue_ttl => #{queue_ttl} " \
|
77
81
|
"acq_id => '#{acquier_id}'"
|
78
82
|
)
|
79
83
|
end
|
@@ -96,6 +100,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
96
100
|
logger.debug(
|
97
101
|
"[redis_queued_locks.try_lock.remove_expired_acqs] " \
|
98
102
|
"lock_key => '#{lock_key}' " \
|
103
|
+
"queue_ttl => #{queue_ttl} " \
|
99
104
|
"acq_id => '#{acquier_id}'"
|
100
105
|
)
|
101
106
|
end
|
@@ -113,6 +118,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
113
118
|
logger.debug(
|
114
119
|
"[redis_queued_locks.try_lock.get_first_from_queue] " \
|
115
120
|
"lock_key => '#{lock_key}' " \
|
121
|
+
"queue_ttl => #{queue_ttl} " \
|
116
122
|
"acq_id => '#{acquier_id}' " \
|
117
123
|
"first_acq_id_in_queue => '#{waiting_acquier}'"
|
118
124
|
)
|
@@ -124,8 +130,28 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
124
130
|
"[ZRANGE <следующий процесс>: #{waiting_acquier} :: <текущий процесс>: #{acquier_id}]"
|
125
131
|
)
|
126
132
|
|
133
|
+
# Step PRE-4.x: check if the request time limit is reached
|
134
|
+
# (when the current try self-removes itself from queue (queue ttl has come))
|
135
|
+
if waiting_acquier == nil
|
136
|
+
if log_lock_try
|
137
|
+
run_non_critical do
|
138
|
+
logger.debug(
|
139
|
+
"[redis_queued_locks.try_lock.exit__queue_ttl_reached] " \
|
140
|
+
"lock_key => '#{lock_key}' " \
|
141
|
+
"queue_ttl => #{queue_ttl} " \
|
142
|
+
"acq_id => '#{acquier_id}'"
|
143
|
+
)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
RedisQueuedLocks.debug(
|
148
|
+
"Step PRE-ROLLBACK №0: достигли лимита времени эквайра лока (queue ttl). выходим. " \
|
149
|
+
"[Наша позиция: #{acquier_id}. queue_ttl: #{queue_ttl}]"
|
150
|
+
)
|
151
|
+
|
152
|
+
inter_result = :dead_score_reached
|
127
153
|
# Step 4: check the actual acquier: is it ours? are we aready to lock?
|
128
|
-
|
154
|
+
elsif waiting_acquier != acquier_id
|
129
155
|
# Step ROLLBACK 1.1: our time hasn't come yet. retry!
|
130
156
|
|
131
157
|
if log_lock_try
|
@@ -133,6 +159,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
133
159
|
logger.debug(
|
134
160
|
"[redis_queued_locks.try_lock.exit__no_first] " \
|
135
161
|
"lock_key => '#{lock_key}' " \
|
162
|
+
"queue_ttl => #{queue_ttl} " \
|
136
163
|
"acq_id => '#{acquier_id}' " \
|
137
164
|
"first_acq_id_in_queue => '#{waiting_acquier}'"
|
138
165
|
)
|
@@ -167,6 +194,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
167
194
|
logger.debug(
|
168
195
|
"[redis_queued_locks.try_lock.exit__still_obtained] " \
|
169
196
|
"lock_key => '#{lock_key}' " \
|
197
|
+
"queue_ttl => #{queue_ttl} " \
|
170
198
|
"acq_id => '#{acquier_id}' " \
|
171
199
|
"first_acq_id_in_queue => '#{waiting_acquier}' " \
|
172
200
|
"locked_by_acq_id => '#{locked_by_acquier}'"
|
@@ -213,6 +241,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
213
241
|
logger.debug(
|
214
242
|
"[redis_queued_locks.try_lock.run__free_to_acquire] " \
|
215
243
|
"lock_key => '#{lock_key}' " \
|
244
|
+
"queue_ttl => #{queue_ttl} " \
|
216
245
|
"acq_id => '#{acquier_id}'"
|
217
246
|
)
|
218
247
|
end
|
@@ -229,6 +258,8 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
|
|
229
258
|
when fail_fast && inter_result == :fail_fast_no_try
|
230
259
|
# Step 7.a: lock is still acquired and we should exit from the logic as soon as possible
|
231
260
|
RedisQueuedLocks::Data[ok: false, result: inter_result]
|
261
|
+
when inter_result == :dead_score_reached
|
262
|
+
RedisQueuedLocks::Data[ok: false, result: inter_result]
|
232
263
|
when inter_result == :lock_is_still_acquired || inter_result == :acquier_is_not_first_in_queue
|
233
264
|
# Step 7.b: lock is still acquired by another process => failed to acquire
|
234
265
|
RedisQueuedLocks::Data[ok: false, result: inter_result]
|
@@ -13,12 +13,23 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldWithExpire
|
|
13
13
|
# @param timed [Boolean] Should the lock be wrapped by Tiemlout with with lock's ttl
|
14
14
|
# @param ttl_shift [Float] Lock's TTL shifting. Should affect block's ttl. In millisecodns.
|
15
15
|
# @param ttl [Integer,NilClass] Lock's time to live (in ms). Nil means "without timeout".
|
16
|
+
# @param queue_ttl [Integer] Lock request lifetime.
|
16
17
|
# @param block [Block] Custom logic that should be invoked unter the obtained lock.
|
17
18
|
# @return [Any,NilClass] nil is returned no block parametr is provided.
|
18
19
|
#
|
19
20
|
# @api private
|
20
21
|
# @since 0.1.0
|
21
|
-
def yield_with_expire(
|
22
|
+
def yield_with_expire(
|
23
|
+
redis,
|
24
|
+
logger,
|
25
|
+
lock_key,
|
26
|
+
acquier_id,
|
27
|
+
timed,
|
28
|
+
ttl_shift,
|
29
|
+
ttl,
|
30
|
+
queue_ttl,
|
31
|
+
&block
|
32
|
+
)
|
22
33
|
if block_given?
|
23
34
|
if timed && ttl != nil
|
24
35
|
timeout = ((ttl - ttl_shift) / 1000.0).yield_self { |time| (time < 0) ? 0.0 : time }
|
@@ -32,6 +43,7 @@ module RedisQueuedLocks::Acquier::AcquireLock::YieldWithExpire
|
|
32
43
|
logger.debug(
|
33
44
|
"[redis_queued_locks.expire_lock] " \
|
34
45
|
"lock_key => '#{lock_key}' " \
|
46
|
+
"queue_ttl => #{queue_ttl} " \
|
35
47
|
"acq_id => '#{acquier_id}'"
|
36
48
|
)
|
37
49
|
end
|
@@ -140,6 +140,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
140
140
|
logger.debug(
|
141
141
|
"[redis_queued_locks.start_lock_obtaining] " \
|
142
142
|
"lock_key => '#{lock_key}' " \
|
143
|
+
"queue_ttl => #{queue_ttl} " \
|
143
144
|
"acq_id => '#{acquier_id}'"
|
144
145
|
)
|
145
146
|
end
|
@@ -150,6 +151,30 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
150
151
|
|
151
152
|
# Step 2.1: caclically try to obtain the lock
|
152
153
|
while acq_process[:should_try]
|
154
|
+
run_non_critical do
|
155
|
+
logger.debug(
|
156
|
+
"[redis_queued_locks.start_try_to_lock_cycle] " \
|
157
|
+
"lock_key => '#{lock_key}' " \
|
158
|
+
"queue_ttl => #{queue_ttl} " \
|
159
|
+
"acq_id => '{#{acquier_id}'"
|
160
|
+
)
|
161
|
+
end
|
162
|
+
|
163
|
+
# Step 2.X: check the actual score: is it in queue ttl limit or not?
|
164
|
+
if RedisQueuedLocks::Resource.dead_score_reached?(acquier_position, queue_ttl)
|
165
|
+
# Step 2.X.X: dead score reached => re-queue the lock request with the new score;
|
166
|
+
acquier_position = RedisQueuedLocks::Resource.calc_initial_acquier_position
|
167
|
+
|
168
|
+
run_non_critical do
|
169
|
+
logger.debug(
|
170
|
+
"[redis_queued_locks.dead_score_reached__reset_acquier_position] " \
|
171
|
+
"lock_key => '#{lock_key} " \
|
172
|
+
"queue_ttl => #{queue_ttl} " \
|
173
|
+
"acq_id => '#{acquier_id}'"
|
174
|
+
)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
153
178
|
try_to_lock(
|
154
179
|
redis,
|
155
180
|
logger,
|
@@ -176,6 +201,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
176
201
|
logger.debug(
|
177
202
|
"[redis_queued_locks.lock_obtained] " \
|
178
203
|
"lock_key => '#{result[:lock_key]}' " \
|
204
|
+
"queue_ttl => #{queue_ttl} " \
|
179
205
|
"acq_id => '#{acquier_id}' " \
|
180
206
|
"acq_time => #{acq_time} (ms)"
|
181
207
|
)
|
@@ -240,7 +266,7 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
240
266
|
end
|
241
267
|
else
|
242
268
|
# NOTE:
|
243
|
-
# delay the exceution in order to prevent chaotic attempts
|
269
|
+
# delay the exceution in order to prevent chaotic lock-acquire attempts
|
244
270
|
# and to allow other processes and threads to obtain the lock too.
|
245
271
|
delay_execution(retry_delay, retry_jitter)
|
246
272
|
end
|
@@ -255,7 +281,17 @@ module RedisQueuedLocks::Acquier::AcquireLock
|
|
255
281
|
begin
|
256
282
|
yield_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
257
283
|
ttl_shift = ((yield_time - acq_process[:acq_end_time]) * 1000).ceil(2)
|
258
|
-
yield_with_expire(
|
284
|
+
yield_with_expire(
|
285
|
+
redis,
|
286
|
+
logger,
|
287
|
+
lock_key,
|
288
|
+
acquier_id,
|
289
|
+
timed,
|
290
|
+
ttl_shift,
|
291
|
+
ttl,
|
292
|
+
queue_ttl,
|
293
|
+
&block
|
294
|
+
)
|
259
295
|
ensure
|
260
296
|
acq_process[:rel_time] = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
261
297
|
acq_process[:hold_time] = (
|
@@ -82,6 +82,20 @@ module RedisQueuedLocks::Resource
|
|
82
82
|
Time.now.to_f - queue_ttl
|
83
83
|
end
|
84
84
|
|
85
|
+
# @param acquier_position [Float]
|
86
|
+
# A time (epoch, seconds.microseconds) that represents
|
87
|
+
# the acquier position in lock request queue.
|
88
|
+
# @parma queue_ttl [Integer]
|
89
|
+
# In second.
|
90
|
+
# @return [Boolean]
|
91
|
+
# Is the lock request time limit has reached or not.
|
92
|
+
#
|
93
|
+
# @api private
|
94
|
+
# @since 0.1.0
|
95
|
+
def dead_score_reached?(acquier_position, queue_ttl)
|
96
|
+
(acquier_position + queue_ttl) < Time.now.to_f
|
97
|
+
end
|
98
|
+
|
85
99
|
# @param lock_queue [String]
|
86
100
|
# @return [String]
|
87
101
|
#
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis_queued_locks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.30
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rustam Ibragimov
|
@@ -107,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
107
107
|
- !ruby/object:Gem::Version
|
108
108
|
version: '0'
|
109
109
|
requirements: []
|
110
|
-
rubygems_version: 3.
|
110
|
+
rubygems_version: 3.3.7
|
111
111
|
signing_key:
|
112
112
|
specification_version: 4
|
113
113
|
summary: Queued distributed locks based on Redis.
|