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
@@ -0,0 +1,145 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 1.9.0
|
5
|
+
module RedisQueuedLocks::Swarm::ZombieInfo
|
6
|
+
class << self
|
7
|
+
# @param redis_client [RedisClient]
|
8
|
+
# @param zombie_ttl [Integer]
|
9
|
+
# @param lock_scan_size [Integer]
|
10
|
+
# @return [Hash<Symbol,Set<String>>]
|
11
|
+
# Format: {
|
12
|
+
# zombie_hosts: <Set<String>>,
|
13
|
+
# zombie_acquirers: <Set<String>>,
|
14
|
+
# zombie_locks: <Set<String>>
|
15
|
+
# }
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
# @since 1.9.0
|
19
|
+
def zombies_info(redis_client, zombie_ttl, lock_scan_size)
|
20
|
+
redis_client.with do |rconn|
|
21
|
+
extract_all(rconn, zombie_ttl, lock_scan_size)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param redis_client [RedisClient]
|
26
|
+
# @param zombie_ttl [Integer]
|
27
|
+
# @param lock_scan_size [Integer]
|
28
|
+
# @return [Set<String>]
|
29
|
+
#
|
30
|
+
# @api private
|
31
|
+
# @since 1.9.0
|
32
|
+
def zombie_locks(redis_client, zombie_ttl, lock_scan_size)
|
33
|
+
redis_client.with do |rconn|
|
34
|
+
extract_zombie_locks(rconn, zombie_ttl, lock_scan_size)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param redis_client [RedisClient]
|
39
|
+
# @param zombie_ttl [Integer]
|
40
|
+
# @return [Set<String>]
|
41
|
+
#
|
42
|
+
# @api private
|
43
|
+
# @since 1.9.0
|
44
|
+
def zombie_hosts(redis_client, zombie_ttl)
|
45
|
+
redis_client.with do |rconn|
|
46
|
+
extract_zombie_hosts(rconn, zombie_ttl)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# @param redis_client [RedisClient]
|
51
|
+
# @param zombie_ttl [Integer]
|
52
|
+
# @param lock_scan_size [Integer]
|
53
|
+
# @return [Set<String>]
|
54
|
+
#
|
55
|
+
# @api private
|
56
|
+
# @since 1.9.0
|
57
|
+
def zombie_acquiers(redis_client, zombie_ttl, lock_scan_size)
|
58
|
+
redis_client.with do |rconn|
|
59
|
+
extract_zombie_acquiers(rconn, zombie_ttl, lock_scan_size)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# @param rconn [RedisClient] redis connection obtained via `#with` from RedisClient instance;
|
66
|
+
# @param zombie_ttl [Integer]
|
67
|
+
# @return [Set<String>]
|
68
|
+
#
|
69
|
+
# @api private
|
70
|
+
# @since 1.9.0
|
71
|
+
def extract_zombie_hosts(rconn, zombie_ttl)
|
72
|
+
zombie_score = RedisQueuedLocks::Resource.calc_zombie_score(zombie_ttl / 1_000)
|
73
|
+
swarmed_hosts = rconn.call('HGETALL', RedisQueuedLocks::Resource::SWARM_KEY)
|
74
|
+
swarmed_hosts.each_with_object(Set.new) do |(hst_id, ts), zombies|
|
75
|
+
(zombies << hst_id) if (zombie_score > ts.to_f)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# @param rconn [RedisClient] redis connection obtained via `#with` from RedisClient instance;
|
80
|
+
# @param zombie_ttl [Integer]
|
81
|
+
# @param lock_scan_size [Integer]
|
82
|
+
# @return [Set<String>]
|
83
|
+
#
|
84
|
+
# @api private
|
85
|
+
# @since 1.9.0
|
86
|
+
def extract_zombie_locks(rconn, zombie_ttl, lock_scan_size)
|
87
|
+
zombie_hosts = extract_zombie_hosts(rconn, zombie_ttl)
|
88
|
+
zombie_locks = Set.new
|
89
|
+
rconn.scan(
|
90
|
+
'MATCH', RedisQueuedLocks::Resource::LOCK_PATTERN, count: lock_scan_size
|
91
|
+
) do |lock_key|
|
92
|
+
_acquier_id, host_id = rconn.call('HMGET', lock_key, 'acq_id', 'hst_id')
|
93
|
+
zombie_locks << lock_key if zombie_hosts.include?(host_id)
|
94
|
+
end
|
95
|
+
zombie_locks
|
96
|
+
end
|
97
|
+
|
98
|
+
# @param rconn [RedisClient] redis connection obtained via `#with` from RedisClient instance;
|
99
|
+
# @param zombie_ttl [Integer]
|
100
|
+
# @param lock_scan_size [Integer]
|
101
|
+
# @return [Set<String>]
|
102
|
+
#
|
103
|
+
# @api private
|
104
|
+
# @since 1.9.0
|
105
|
+
def extract_zombie_acquiers(rconn, zombie_ttl, lock_scan_size)
|
106
|
+
zombie_hosts = extract_zombie_hosts(rconn, zombie_ttl)
|
107
|
+
zombie_acquirers = Set.new
|
108
|
+
rconn.scan(
|
109
|
+
'MATCH', RedisQueuedLocks::Resource::LOCK_PATTERN, count: lock_scan_size
|
110
|
+
) do |lock_key|
|
111
|
+
acquier_id, host_id = rconn.call('HMGET', lock_key, 'acq_id', 'hst_id')
|
112
|
+
zombie_acquirers << acquier_id if zombie_hosts.include?(host_id)
|
113
|
+
end
|
114
|
+
zombie_acquirers
|
115
|
+
end
|
116
|
+
|
117
|
+
# @param rconn [RedisClient] redis connection obtained via `#with` from RedisClient instance;
|
118
|
+
# @param zombie_ttl [Integer]
|
119
|
+
# @param lock_scan_size [Integer]
|
120
|
+
# @return [Hash<Symbol,<Set<String>>]
|
121
|
+
# Format: {
|
122
|
+
# zombie_hosts: <Set<String>>,
|
123
|
+
# zombie_acquirers: <Set<String>>,
|
124
|
+
# zombie_locks: <Set<String>>
|
125
|
+
# }
|
126
|
+
#
|
127
|
+
# @api private
|
128
|
+
# @since 1.9.0
|
129
|
+
def extract_all(rconn, zombie_ttl, lock_scan_size)
|
130
|
+
zombie_hosts = extract_zombie_hosts(rconn, zombie_ttl)
|
131
|
+
zombie_locks = Set.new
|
132
|
+
zombie_acquirers = Set.new
|
133
|
+
rconn.scan(
|
134
|
+
'MATCH', RedisQueuedLocks::Resource::LOCK_PATTERN, count: lock_scan_size
|
135
|
+
) do |lock_key|
|
136
|
+
acquier_id, host_id = rconn.call('HMGET', lock_key, 'acq_id', 'hst_id')
|
137
|
+
if zombie_hosts.include?(host_id)
|
138
|
+
zombie_acquirers << acquier_id
|
139
|
+
zombie_locks << lock_key
|
140
|
+
end
|
141
|
+
end
|
142
|
+
{ zombie_hosts:, zombie_acquirers:, zombie_locks: }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api public
|
4
|
+
# @since 1.9.0
|
5
|
+
# rubocop:disable Metrics/ClassLength
|
6
|
+
class RedisQueuedLocks::Swarm
|
7
|
+
require_relative 'swarm/redis_client_builder'
|
8
|
+
require_relative 'swarm/supervisor'
|
9
|
+
require_relative 'swarm/acquirers'
|
10
|
+
require_relative 'swarm/zombie_info'
|
11
|
+
require_relative 'swarm/swarm_element'
|
12
|
+
require_relative 'swarm/probe_hosts'
|
13
|
+
require_relative 'swarm/flush_zombies'
|
14
|
+
|
15
|
+
# @return [RedisQueuedLocks::Client]
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
# @since 1.9.0
|
19
|
+
attr_reader :rql_client
|
20
|
+
|
21
|
+
# @return [RedisQueuedLocks::Swarm::Supervisor]
|
22
|
+
#
|
23
|
+
# @api private
|
24
|
+
# @since 1.9.0
|
25
|
+
attr_reader :supervisor
|
26
|
+
|
27
|
+
# @return [RedisQueuedLocks::Swarm::ProbeHosts]
|
28
|
+
#
|
29
|
+
# @api private
|
30
|
+
# @since 1.9.0
|
31
|
+
attr_reader :probe_hosts_element
|
32
|
+
|
33
|
+
# @return [RedisQueuedLocks::Swarm::FlushZombies]
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
# @since 1.9.0
|
37
|
+
attr_reader :flush_zombies_element
|
38
|
+
|
39
|
+
# @return [RedisQueuedLocks::Utilities::Lock]
|
40
|
+
#
|
41
|
+
# @api private
|
42
|
+
# @since 1.9.0
|
43
|
+
attr_reader :sync
|
44
|
+
|
45
|
+
# @param rql_client [RedisQueuedLocks::Client]
|
46
|
+
# @return [void]
|
47
|
+
#
|
48
|
+
# @api private
|
49
|
+
# @since 1.9.0
|
50
|
+
def initialize(rql_client)
|
51
|
+
@rql_client = rql_client
|
52
|
+
@sync = RedisQueuedLocks::Utilities::Lock.new
|
53
|
+
@supervisor = RedisQueuedLocks::Swarm::Supervisor.new(rql_client)
|
54
|
+
@probe_hosts_element = RedisQueuedLocks::Swarm::ProbeHosts.new(rql_client)
|
55
|
+
@flush_zombies_element = RedisQueuedLocks::Swarm::FlushZombies.new(rql_client)
|
56
|
+
end
|
57
|
+
|
58
|
+
# @return [Hash<Symbol,Boolean|<Hash<Symbol,Boolean>>]
|
59
|
+
#
|
60
|
+
# @api public
|
61
|
+
# @since 1.9.0
|
62
|
+
def swarm_status
|
63
|
+
sync.synchronize do
|
64
|
+
{
|
65
|
+
auto_swarm: rql_client.config[:swarm][:auto_swarm],
|
66
|
+
supervisor: supervisor.status,
|
67
|
+
probe_hosts: probe_hosts_element.status,
|
68
|
+
flush_zombies: flush_zombies_element.status
|
69
|
+
}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
alias_method :swarm_state, :swarm_status
|
73
|
+
|
74
|
+
# @option zombie_ttl [Integer]
|
75
|
+
# @return [Hash<String,Hash<Symbol,Float|Time>>]
|
76
|
+
#
|
77
|
+
# @api public
|
78
|
+
# @since 1.9.0
|
79
|
+
def swarm_info(zombie_ttl: rql_client.config[:swarm][:flush_zombies][:zombie_ttl])
|
80
|
+
RedisQueuedLocks::Swarm::Acquirers.acquirers(
|
81
|
+
rql_client.redis_client,
|
82
|
+
zombie_ttl
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
86
|
+
# @return [
|
87
|
+
# RedisQueuedLocks::Data[
|
88
|
+
# ok: <Boolean>,
|
89
|
+
# result: {
|
90
|
+
# host_id1 <String> => score1 <String>,
|
91
|
+
# host_id2 <String> => score2 <String>,
|
92
|
+
# etc...
|
93
|
+
# }
|
94
|
+
# ]
|
95
|
+
# ]
|
96
|
+
#
|
97
|
+
# @api public
|
98
|
+
# @since 1.9.0
|
99
|
+
def probe_hosts
|
100
|
+
RedisQueuedLocks::Swarm::ProbeHosts.probe_hosts(
|
101
|
+
rql_client.redis_client,
|
102
|
+
rql_client.uniq_identity
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
# @option zombie_ttl [Integer]
|
107
|
+
# @option lock_scan_size [Integer]
|
108
|
+
# @option queue_scan_size [Integer]
|
109
|
+
# @return [
|
110
|
+
# RedisQueuedLocks::Data[
|
111
|
+
# ok: <Boolean>,
|
112
|
+
# deleted_zombie_hosts: <Set<String>>,
|
113
|
+
# deleted_zombie_acquiers: <Set<String>>,
|
114
|
+
# deleted_zombie_locks: <Set<String>>
|
115
|
+
# ]
|
116
|
+
# ]
|
117
|
+
#
|
118
|
+
# @api public
|
119
|
+
# @since 1.9.0
|
120
|
+
def flush_zombies(
|
121
|
+
zombie_ttl: rql_client.config[:swarm][:flush_zombies][:zombie_ttl],
|
122
|
+
lock_scan_size: rql_client.config[:swarm][:flush_zombies][:zombie_lock_scan_size],
|
123
|
+
queue_scan_size: rql_client.config[:swarm][:flush_zombies][:zombie_queue_scan_size]
|
124
|
+
)
|
125
|
+
RedisQueuedLocks::Swarm::FlushZombies.flush_zombies(
|
126
|
+
rql_client.redis_client,
|
127
|
+
zombie_ttl,
|
128
|
+
lock_scan_size,
|
129
|
+
queue_scan_size
|
130
|
+
)
|
131
|
+
end
|
132
|
+
|
133
|
+
# @return [Set<String>]
|
134
|
+
#
|
135
|
+
# @api public
|
136
|
+
# @since 1.9.0
|
137
|
+
def zombie_locks(
|
138
|
+
zombie_ttl: rql_client.config[:swarm][:flush_zombies][:zombie_ttl],
|
139
|
+
lock_scan_size: rql_client.config[:swarm][:flush_zombies][:zombie_lock_scan_size]
|
140
|
+
)
|
141
|
+
RedisQueuedLocks::Swarm::ZombieInfo.zombie_locks(
|
142
|
+
rql_client.redis_client,
|
143
|
+
zombie_ttl,
|
144
|
+
lock_scan_size
|
145
|
+
)
|
146
|
+
end
|
147
|
+
|
148
|
+
# @return [Set<String>]
|
149
|
+
#
|
150
|
+
# @api ppublic
|
151
|
+
# @since 1.9.0
|
152
|
+
def zombie_acquiers(
|
153
|
+
zombie_ttl: rql_client.config[:swarm][:flush_zombies][:zombie_ttl],
|
154
|
+
lock_scan_size: rql_client.config[:swarm][:flush_zombies][:zombie_lock_scan_size]
|
155
|
+
)
|
156
|
+
RedisQueuedLocks::Swarm::ZombieInfo.zombie_acquiers(
|
157
|
+
rql_client.redis_client,
|
158
|
+
zombie_ttl,
|
159
|
+
lock_scan_size
|
160
|
+
)
|
161
|
+
end
|
162
|
+
|
163
|
+
# @return [Set<String>]
|
164
|
+
#
|
165
|
+
# @api public
|
166
|
+
# @since 1.9.0
|
167
|
+
def zombie_hosts(zombie_ttl: rql_client.config[:swarm][:flush_zombies][:zombie_ttl])
|
168
|
+
RedisQueuedLocks::Swarm::ZombieInfo.zombie_hosts(rql_client.redis_client, zombie_ttl)
|
169
|
+
end
|
170
|
+
|
171
|
+
# @return [Hash<Symbol,Set<String>>]
|
172
|
+
# Format: {
|
173
|
+
# zombie_hosts: <Set<String>>,
|
174
|
+
# zombie_acquirers: <Set<String>>,
|
175
|
+
# zombie_locks: <Set<String>>
|
176
|
+
# }
|
177
|
+
#
|
178
|
+
# @api public
|
179
|
+
# @since 1.9.0
|
180
|
+
def zombies_info(
|
181
|
+
zombie_ttl: rql_client.config[:swarm][:flush_zombies][:zombie_ttl],
|
182
|
+
lock_scan_size: rql_client.config[:swarm][:flush_zombies][:zombie_lock_scan_size]
|
183
|
+
)
|
184
|
+
RedisQueuedLocks::Swarm::ZombieInfo.zombies_info(
|
185
|
+
rql_client.redis_client,
|
186
|
+
zombie_ttl,
|
187
|
+
lock_scan_size
|
188
|
+
)
|
189
|
+
end
|
190
|
+
|
191
|
+
# @option silently [Boolean]
|
192
|
+
# @return [NilClass,Hash<Symbol,Symbol|Boolean>]
|
193
|
+
#
|
194
|
+
# @api public
|
195
|
+
# @since 1.9.0
|
196
|
+
def swarm!
|
197
|
+
sync.synchronize do
|
198
|
+
# Step 0:
|
199
|
+
# - stop the supervisor (kill internal observer objects if supervisor is alredy running);
|
200
|
+
supervisor.stop!
|
201
|
+
|
202
|
+
# Step 1:
|
203
|
+
# - initialize swarm elements and start their main loops;
|
204
|
+
probe_hosts_element.try_swarm!
|
205
|
+
flush_zombies_element.try_swarm!
|
206
|
+
|
207
|
+
# Step 2:
|
208
|
+
# - run supervisor that should keep running created swarm elements and their main loops;
|
209
|
+
unless supervisor.running?
|
210
|
+
supervisor.observe! do
|
211
|
+
probe_hosts_element.reswarm_if_dead!
|
212
|
+
flush_zombies_element.reswarm_if_dead!
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# NOTE: need to give a little timespot to initialize ractor objects and their main loops;
|
217
|
+
sleep(0.1)
|
218
|
+
|
219
|
+
RedisQueuedLocks::Data[ok: true, result: :swarming]
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# @option silently [Boolean]
|
224
|
+
# @return [NilClass,Hash<Symbol,Symbol|Boolean>]
|
225
|
+
#
|
226
|
+
# @api public
|
227
|
+
# @since 1.9.0
|
228
|
+
def deswarm!
|
229
|
+
sync.synchronize do
|
230
|
+
supervisor.stop!
|
231
|
+
probe_hosts_element.try_kill!
|
232
|
+
flush_zombies_element.try_kill!
|
233
|
+
|
234
|
+
# NOTE: need to give a little timespot to stop ractor objects and their main loops;
|
235
|
+
sleep(0.1)
|
236
|
+
|
237
|
+
RedisQueuedLocks::Data[ok: true, result: :terminating]
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
# rubocop:enable Metrics/ClassLength
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 1.9.0
|
5
|
+
class RedisQueuedLocks::Utilities::Lock
|
6
|
+
# @return [void]
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
# @since 1.9.0
|
10
|
+
def initialize
|
11
|
+
@lock = ::Mutex.new
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param block [Block]
|
15
|
+
# @return [Any]
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
# @since 1.9.0
|
19
|
+
def synchronize(&block)
|
20
|
+
@lock.owned? ? yield : @lock.synchronize(&block)
|
21
|
+
end
|
22
|
+
end
|
@@ -3,8 +3,38 @@
|
|
3
3
|
# @api private
|
4
4
|
# @since 1.0.0
|
5
5
|
module RedisQueuedLocks::Utilities
|
6
|
+
require_relative 'utilities/lock'
|
7
|
+
|
6
8
|
module_function
|
7
9
|
|
10
|
+
# Ractor class has no methods for Ractor object status identification.
|
11
|
+
# The only one approach (without C/FFI extending) to give the status is to check
|
12
|
+
# the status from the `#inspect/#to_s` methods. String representation has the following format:
|
13
|
+
# Format: "#<Ractor:##{id}#{name ? ' '+name : ''}#{loc ? " " + loc : ''} #{status}>"
|
14
|
+
# Example: #<Ractor:#2 (irb):2 terminated>
|
15
|
+
# So we need to parse the `status` part of the inspect method and use it for our logic.
|
16
|
+
# Current regexp object provides the pattern for this.
|
17
|
+
#
|
18
|
+
# @return [Regexp]
|
19
|
+
#
|
20
|
+
# @api private
|
21
|
+
# @since 1.9.0
|
22
|
+
RACTOR_LIVENESS_PATTERN = /\A.*?(created|running|blocking).*?\z/i
|
23
|
+
|
24
|
+
# Ractor status as a string extracted from the object string representation.
|
25
|
+
# This way is used cuz the ractor class has no any detailed status extraction API.
|
26
|
+
# Possible statuses (at the moment of Ruby@3.4):
|
27
|
+
# - "created"
|
28
|
+
# - "blocking"
|
29
|
+
# - "running"
|
30
|
+
# - "terminated"
|
31
|
+
#
|
32
|
+
# @return [Regexp]
|
33
|
+
#
|
34
|
+
# @api private
|
35
|
+
# @since 1.9.0
|
36
|
+
RACTOR_STATUS_PATTERN = /\A.*?\s(?<status>\w+)>\z/i
|
37
|
+
|
8
38
|
# @param block [Block]
|
9
39
|
# @return [Any]
|
10
40
|
#
|
@@ -13,4 +43,49 @@ module RedisQueuedLocks::Utilities
|
|
13
43
|
def run_non_critical(&block)
|
14
44
|
yield rescue nil
|
15
45
|
end
|
46
|
+
|
47
|
+
# Possible statuses (at the moment of Ruby@3.4):
|
48
|
+
# - "created"
|
49
|
+
# - "blocking"
|
50
|
+
# - "running"
|
51
|
+
# - "terminated"
|
52
|
+
#
|
53
|
+
# @param ractor [Ractor]
|
54
|
+
# @return [String]
|
55
|
+
#
|
56
|
+
# @api private
|
57
|
+
# @since 1.9.0
|
58
|
+
def ractor_status(ractor)
|
59
|
+
ractor.to_s.match(RACTOR_STATUS_PATTERN)[:status]
|
60
|
+
end
|
61
|
+
|
62
|
+
# @param ractor [Ractor]
|
63
|
+
# @return [Boolean]
|
64
|
+
#
|
65
|
+
# @api private
|
66
|
+
# @since 1.9.0
|
67
|
+
def ractor_alive?(ractor)
|
68
|
+
ractor.to_s.match?(RACTOR_LIVENESS_PATTERN)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns the status of the passed thread object.
|
72
|
+
# Possible statuses:
|
73
|
+
# - "run" (thread is executing);
|
74
|
+
# - "sleep" (thread is sleeping or waiting on I/O);
|
75
|
+
# - "aborting" (thread is aborting)
|
76
|
+
# - "dead" (thread is terminated normally);
|
77
|
+
# - "failed" (thread is terminated with an exception);
|
78
|
+
# See Thread#status official documentation.
|
79
|
+
#
|
80
|
+
# @param [Thread]
|
81
|
+
# @return [String]
|
82
|
+
#
|
83
|
+
# @api private
|
84
|
+
# @since 1.9.0
|
85
|
+
def thread_state(thread)
|
86
|
+
status = thread.status
|
87
|
+
return 'dead' if status == false
|
88
|
+
return 'failed' if status == nil
|
89
|
+
status
|
90
|
+
end
|
16
91
|
end
|
data/lib/redis_queued_locks.rb
CHANGED
@@ -5,6 +5,7 @@ require 'qonfig'
|
|
5
5
|
require 'timeout'
|
6
6
|
require 'securerandom'
|
7
7
|
require 'logger'
|
8
|
+
require 'objspace'
|
8
9
|
|
9
10
|
# @api public
|
10
11
|
# @since 1.0.0
|
@@ -18,6 +19,7 @@ module RedisQueuedLocks
|
|
18
19
|
require_relative 'redis_queued_locks/resource'
|
19
20
|
require_relative 'redis_queued_locks/acquier'
|
20
21
|
require_relative 'redis_queued_locks/instrument'
|
22
|
+
require_relative 'redis_queued_locks/swarm'
|
21
23
|
require_relative 'redis_queued_locks/client'
|
22
24
|
|
23
25
|
# @since 1.0.0
|
data/redis_queued_locks.gemspec
CHANGED
@@ -15,21 +15,17 @@ Gem::Specification.new do |spec|
|
|
15
15
|
'capabilities based on the Redis Database.'
|
16
16
|
|
17
17
|
spec.description =
|
18
|
-
'Distributed locks with "prioritized lock acquisition queue" capabilities ' \
|
18
|
+
'|> Distributed locks with "prioritized lock acquisition queue" capabilities ' \
|
19
19
|
'based on the Redis Database. ' \
|
20
|
-
|
21
|
-
'
|
22
|
-
"(each lock is hosted by it's own queue separately from other queues) and processed " \
|
20
|
+
'|> Each lock request is put into the request queue ' \
|
21
|
+
'(each lock is hosted by it\'s own queue separately from other queues) and processed ' \
|
23
22
|
'in order of their priority (FIFO). ' \
|
24
|
-
|
25
|
-
'Each lock request lives some period of time (RTTL) ' \
|
23
|
+
'|> Each lock request lives some period of time (RTTL) ' \
|
26
24
|
'(with requeue capabilities) which guarantees the request queue will never be stacked. ' \
|
27
|
-
|
28
|
-
'In addition to the classic `queued` (FIFO) strategy RQL supports ' \
|
25
|
+
'|> In addition to the classic `queued` (FIFO) strategy RQL supports ' \
|
29
26
|
'`random` (RANDOM) lock obtaining strategy when any acquirer from the lock queue ' \
|
30
27
|
'can obtain the lock regardless the position in the queue. ' \
|
31
|
-
|
32
|
-
'Provides flexible invocation flow, parametrized limits ' \
|
28
|
+
'|> Provides flexible invocation flow, parametrized limits ' \
|
33
29
|
'(lock request ttl, lock ttl, queue ttl, lock attempts limit, fast failing, etc), ' \
|
34
30
|
'logging and instrumentation.'
|
35
31
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis_queued_locks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rustam Ibragimov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-07-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis-client
|
@@ -38,8 +38,16 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0.28'
|
41
|
-
description: 'Distributed locks with "prioritized lock acquisition queue" capabilities
|
42
|
-
based on the Redis Database.
|
41
|
+
description: '|> Distributed locks with "prioritized lock acquisition queue" capabilities
|
42
|
+
based on the Redis Database. |> Each lock request is put into the request queue
|
43
|
+
(each lock is hosted by it''s own queue separately from other queues) and processed
|
44
|
+
in order of their priority (FIFO). |> Each lock request lives some period of time
|
45
|
+
(RTTL) (with requeue capabilities) which guarantees the request queue will never
|
46
|
+
be stacked. |> In addition to the classic `queued` (FIFO) strategy RQL supports
|
47
|
+
`random` (RANDOM) lock obtaining strategy when any acquirer from the lock queue
|
48
|
+
can obtain the lock regardless the position in the queue. |> Provides flexible invocation
|
49
|
+
flow, parametrized limits (lock request ttl, lock ttl, queue ttl, lock attempts
|
50
|
+
limit, fast failing, etc), logging and instrumentation.'
|
43
51
|
email:
|
44
52
|
- iamdaiver@gmail.com
|
45
53
|
executables: []
|
@@ -91,9 +99,19 @@ files:
|
|
91
99
|
- lib/redis_queued_locks/logging/sampler.rb
|
92
100
|
- lib/redis_queued_locks/logging/void_logger.rb
|
93
101
|
- lib/redis_queued_locks/resource.rb
|
102
|
+
- lib/redis_queued_locks/swarm.rb
|
103
|
+
- lib/redis_queued_locks/swarm/acquirers.rb
|
104
|
+
- lib/redis_queued_locks/swarm/flush_zombies.rb
|
105
|
+
- lib/redis_queued_locks/swarm/probe_hosts.rb
|
106
|
+
- lib/redis_queued_locks/swarm/redis_client_builder.rb
|
107
|
+
- lib/redis_queued_locks/swarm/supervisor.rb
|
108
|
+
- lib/redis_queued_locks/swarm/swarm_element.rb
|
109
|
+
- lib/redis_queued_locks/swarm/swarm_element/isolated.rb
|
110
|
+
- lib/redis_queued_locks/swarm/swarm_element/threaded.rb
|
111
|
+
- lib/redis_queued_locks/swarm/zombie_info.rb
|
94
112
|
- lib/redis_queued_locks/utilities.rb
|
113
|
+
- lib/redis_queued_locks/utilities/lock.rb
|
95
114
|
- lib/redis_queued_locks/version.rb
|
96
|
-
- lib/redis_queued_locks/watcher.rb
|
97
115
|
- redis_queued_locks.gemspec
|
98
116
|
homepage: https://github.com/0exp/redis_queued_locks
|
99
117
|
licenses:
|
@@ -117,7 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
135
|
- !ruby/object:Gem::Version
|
118
136
|
version: '0'
|
119
137
|
requirements: []
|
120
|
-
rubygems_version: 3.
|
138
|
+
rubygems_version: 3.5.11
|
121
139
|
signing_key:
|
122
140
|
specification_version: 4
|
123
141
|
summary: Distributed locks with "prioritized lock acquisition queue" capabilities
|
@@ -1 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|