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
@@ -21,6 +21,18 @@ module RedisQueuedLocks::Resource
|
|
21
21
|
# @since 1.0.0
|
22
22
|
LOCK_QUEUE_PATTERN = 'rql:lock_queue:*'
|
23
23
|
|
24
|
+
# @return [String]
|
25
|
+
#
|
26
|
+
# @api private
|
27
|
+
# @since ?.?.?
|
28
|
+
READ_LOCK_QUEUE_PATTERN = 'rql:lock_queue:*:read'
|
29
|
+
|
30
|
+
# @return [String]
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
# @since ?.?.?
|
34
|
+
WRITE_LOCK_QUEUE_PATTERN = 'rql:lock_queue:*:write'
|
35
|
+
|
24
36
|
# @return [String]
|
25
37
|
#
|
26
38
|
# @api private
|
@@ -55,7 +67,7 @@ module RedisQueuedLocks::Resource
|
|
55
67
|
#
|
56
68
|
# @api private
|
57
69
|
# @since 1.0.0
|
58
|
-
def
|
70
|
+
def acquirer_identifier(process_id, thread_id, fiber_id, ractor_id, identity)
|
59
71
|
"rql:acq:#{process_id}/#{thread_id}/#{fiber_id}/#{ractor_id}/#{identity}"
|
60
72
|
end
|
61
73
|
|
@@ -93,20 +105,38 @@ module RedisQueuedLocks::Resource
|
|
93
105
|
"rql:lock_queue:#{lock_name}"
|
94
106
|
end
|
95
107
|
|
108
|
+
# @param lock_name [String]
|
109
|
+
# @return [String]
|
110
|
+
#
|
111
|
+
# @api private
|
112
|
+
# @api ?.?.?
|
113
|
+
def prepare_read_lock_queue(lock_name)
|
114
|
+
"rql:lock_queue:#{lock_name}:read"
|
115
|
+
end
|
116
|
+
|
117
|
+
# @param lock_name [String]
|
118
|
+
# @return [String]
|
119
|
+
#
|
120
|
+
# @api private
|
121
|
+
# @api ?.?.?
|
122
|
+
def prepare_write_lock_queue(lock_name)
|
123
|
+
"rql:lock_queue:#{lock_name}:write"
|
124
|
+
end
|
125
|
+
|
96
126
|
# @return [Float] Redis's <Set> score that is calculated from the time (epoch) as a float.
|
97
127
|
#
|
98
128
|
# @api private
|
99
129
|
# @since 1.0.0
|
100
|
-
def
|
130
|
+
def calc_initial_acquirer_position
|
101
131
|
Time.now.to_f
|
102
132
|
end
|
103
133
|
|
104
|
-
# @param queue_ttl [
|
105
|
-
# @return [Float] Redis's <Set> score barrier for
|
134
|
+
# @param queue_ttl [Numeric] In seconds
|
135
|
+
# @return [Float] Redis's <Set> score barrier for acquirers that should be removed from queue.
|
106
136
|
#
|
107
137
|
# @api private
|
108
138
|
# @since 1.0.0
|
109
|
-
def
|
139
|
+
def acquirer_dead_score(queue_ttl)
|
110
140
|
Time.now.to_f - queue_ttl
|
111
141
|
end
|
112
142
|
|
@@ -119,9 +149,9 @@ module RedisQueuedLocks::Resource
|
|
119
149
|
Time.now.to_f - zombie_ttl
|
120
150
|
end
|
121
151
|
|
122
|
-
# @param
|
152
|
+
# @param acquirer_position [Float]
|
123
153
|
# A time (epoch, seconds.milliseconds) that represents
|
124
|
-
# the
|
154
|
+
# the acquirer position in lock request queue.
|
125
155
|
# @parma queue_ttl [Integer]
|
126
156
|
# In second.
|
127
157
|
# @return [Boolean]
|
@@ -129,8 +159,8 @@ module RedisQueuedLocks::Resource
|
|
129
159
|
#
|
130
160
|
# @api private
|
131
161
|
# @since 1.0.0
|
132
|
-
def dead_score_reached?(
|
133
|
-
(
|
162
|
+
def dead_score_reached?(acquirer_position, queue_ttl)
|
163
|
+
(acquirer_position + queue_ttl) < Time.now.to_f
|
134
164
|
end
|
135
165
|
|
136
166
|
# @param lock_queue [String]
|
@@ -186,13 +216,20 @@ module RedisQueuedLocks::Resource
|
|
186
216
|
# NOTE №2: we have no any approach to count Fiber objects in the current process without
|
187
217
|
# object space API (or super memory-expensive) so host identification works without fibers;
|
188
218
|
# NOTE №3: we still can extract thread objects via Thread.list API;
|
219
|
+
|
220
|
+
# @type var current_process_id: Integer
|
189
221
|
current_process_id = get_process_id
|
222
|
+
# @type var current_threads: Array[Thread]
|
190
223
|
current_threads = ::Thread.list
|
224
|
+
# @type var current_ractor_id: Integer
|
191
225
|
current_ractor_id = get_ractor_id
|
192
226
|
|
193
|
-
[]
|
227
|
+
# NOTE: steep can't resolve a type of dynamic `[]` literal mutated via inline tap;
|
228
|
+
# steep:ignore:start
|
229
|
+
[].tap do |acquirers|
|
230
|
+
# @type var acquirers: Array[String]
|
194
231
|
current_threads.each do |thread|
|
195
|
-
|
232
|
+
acquirers << host_identifier(
|
196
233
|
current_process_id,
|
197
234
|
thread.object_id,
|
198
235
|
current_ractor_id,
|
@@ -200,6 +237,7 @@ module RedisQueuedLocks::Resource
|
|
200
237
|
)
|
201
238
|
end
|
202
239
|
end
|
240
|
+
# steep:ignore:end
|
203
241
|
end
|
204
242
|
end
|
205
243
|
end
|
@@ -5,35 +5,36 @@
|
|
5
5
|
module RedisQueuedLocks::Swarm::Acquirers
|
6
6
|
class << self
|
7
7
|
# Returns the list of swarm acquirers stored as HASH.
|
8
|
-
# Format:
|
9
|
-
# {
|
10
|
-
# <acquirer id #1> => {
|
11
|
-
# zombie: <Boolean>,
|
12
|
-
# last_probe_time: <Time>,
|
13
|
-
# last_probe_score: <Numeric>
|
14
|
-
# },
|
15
|
-
# <acquirer id #2> => {
|
16
|
-
# zombie: <Boolean>,
|
17
|
-
# last_probe_time: <Time>,
|
18
|
-
# last_probe_score: <Numeric>
|
19
|
-
# },
|
20
|
-
# ...
|
21
|
-
# }
|
22
8
|
# Liveness probe time is represented as a float value (Time.now.to_f initially).
|
23
9
|
#
|
24
10
|
# @param redis_client [RedisClient]
|
25
11
|
# @param zombie_ttl [Integer]
|
26
|
-
# @return [Hash<String,Hash<Symbol,Float|Time>>]
|
12
|
+
# @return [Hash<String,Hash<Symbol,Float|Time>>] Format:
|
13
|
+
# {
|
14
|
+
# <acquirer id #1> => {
|
15
|
+
# zombie: <Boolean>,
|
16
|
+
# last_probe_time: <Time>,
|
17
|
+
# last_probe_score: <Numeric>
|
18
|
+
# },
|
19
|
+
# <acquirer id #2> => {
|
20
|
+
# zombie: <Boolean>,
|
21
|
+
# last_probe_time: <Time>,
|
22
|
+
# last_probe_score: <Numeric>
|
23
|
+
# },
|
24
|
+
# ...
|
25
|
+
# }
|
27
26
|
#
|
28
27
|
# @api private
|
29
28
|
# @since 1.9.0
|
30
29
|
def acquirers(redis_client, zombie_ttl)
|
31
30
|
redis_client.with do |rconn|
|
32
31
|
rconn.call('HGETALL', RedisQueuedLocks::Resource::SWARM_KEY).tap do |swarm_acqs|
|
32
|
+
# @type var swarm_acqs: Hash[String,untyped]
|
33
33
|
swarm_acqs.transform_values! do |last_probe|
|
34
|
+
# @type var last_probe: String
|
34
35
|
last_probe_score = last_probe.to_f
|
35
36
|
last_probe_time = Time.at(last_probe_score)
|
36
|
-
zombie_score = RedisQueuedLocks::Resource.calc_zombie_score(zombie_ttl / 1_000)
|
37
|
+
zombie_score = RedisQueuedLocks::Resource.calc_zombie_score(zombie_ttl / 1_000.0)
|
37
38
|
is_zombie = last_probe_score < zombie_score
|
38
39
|
{ zombie: is_zombie, last_probe_time:, last_probe_score: }
|
39
40
|
end
|
@@ -8,14 +8,13 @@ class RedisQueuedLocks::Swarm::FlushZombies < RedisQueuedLocks::Swarm::SwarmElem
|
|
8
8
|
# @parma zombie_ttl [Integer]
|
9
9
|
# @param lock_scan_size [Integer]
|
10
10
|
# @param queue_scan_size [Integer]
|
11
|
-
# @return [
|
12
|
-
#
|
11
|
+
# @return [Hash<Symbol,Boolean|Set<String>]] Format:
|
12
|
+
# {
|
13
13
|
# ok: <Boolean>,
|
14
14
|
# deleted_zombie_hosts: <Set<String>>,
|
15
|
-
#
|
15
|
+
# deleted_zombie_acquirers: <Set<String>>,
|
16
16
|
# deleted_zombie_locks: <Set<String>>
|
17
|
-
#
|
18
|
-
# ]
|
17
|
+
# }
|
19
18
|
#
|
20
19
|
# @api private
|
21
20
|
# @since 1.9.0
|
@@ -30,7 +29,7 @@ class RedisQueuedLocks::Swarm::FlushZombies < RedisQueuedLocks::Swarm::SwarmElem
|
|
30
29
|
# Step 1:
|
31
30
|
# calculate zombie score (the time marker that shows acquirers that
|
32
31
|
# have not announced live probes for a long time)
|
33
|
-
zombie_score = RedisQueuedLocks::Resource.calc_zombie_score(zombie_ttl / 1_000)
|
32
|
+
zombie_score = RedisQueuedLocks::Resource.calc_zombie_score(zombie_ttl / 1_000.0)
|
34
33
|
|
35
34
|
# Step 2: extract zombie acquirers from the swarm list
|
36
35
|
zombie_hosts = rconn.call('HGETALL', RedisQueuedLocks::Resource::SWARM_KEY)
|
@@ -39,30 +38,32 @@ class RedisQueuedLocks::Swarm::FlushZombies < RedisQueuedLocks::Swarm::SwarmElem
|
|
39
38
|
end
|
40
39
|
|
41
40
|
# Step X: exit if we have no any zombie acquirer
|
42
|
-
next
|
41
|
+
next {
|
43
42
|
ok: true,
|
44
43
|
deleted_zombie_hosts: Set.new,
|
45
44
|
deleted_zombie_acquirers: Set.new,
|
46
|
-
deleted_zombie_locks: Set.new
|
47
|
-
|
45
|
+
deleted_zombie_locks: Set.new
|
46
|
+
} if zombie_hosts.empty?
|
48
47
|
|
49
48
|
# Step 3: find zombie locks held by zombies and delete them
|
50
49
|
# TODO: indexing (in order to prevent full database scan);
|
51
50
|
# NOTE: original redis does not support indexing so we need to use
|
52
51
|
# internal data structers to simulate data indexing (such as sorted sets or lists);
|
53
|
-
zombie_locks = Set.new
|
54
|
-
|
52
|
+
zombie_locks = Set.new #: Set[String]
|
53
|
+
zombie_acquirers = Set.new #: Set[String]
|
55
54
|
|
56
55
|
rconn.scan(
|
57
56
|
'MATCH', RedisQueuedLocks::Resource::LOCK_PATTERN, count: lock_scan_size
|
58
57
|
) do |lock_key|
|
59
|
-
|
58
|
+
acquirer_id, host_id = rconn.call('HMGET', lock_key, 'acq_id', 'hst_id')
|
60
59
|
if zombie_hosts.include?(host_id)
|
61
60
|
zombie_locks << lock_key
|
62
|
-
|
61
|
+
zombie_acquirers << acquirer_id
|
63
62
|
end
|
64
63
|
end
|
65
|
-
|
64
|
+
|
65
|
+
# NOTE: (steep) steep can't use <Set>s for splats
|
66
|
+
rconn.call('DEL', *zombie_locks) if zombie_locks.any? # steep:ignore
|
66
67
|
|
67
68
|
# Step 4: find zombie requests => and drop them
|
68
69
|
# TODO: indexing (in order to prevent full database scan);
|
@@ -71,8 +72,8 @@ class RedisQueuedLocks::Swarm::FlushZombies < RedisQueuedLocks::Swarm::SwarmElem
|
|
71
72
|
rconn.scan(
|
72
73
|
'MATCH', RedisQueuedLocks::Resource::LOCK_QUEUE_PATTERN, count: queue_scan_size
|
73
74
|
) do |lock_queue|
|
74
|
-
|
75
|
-
rconn.call('ZREM', lock_queue,
|
75
|
+
zombie_acquirers.each do |zombie_acquirer|
|
76
|
+
rconn.call('ZREM', lock_queue, zombie_acquirer)
|
76
77
|
end
|
77
78
|
end
|
78
79
|
|
@@ -80,12 +81,12 @@ class RedisQueuedLocks::Swarm::FlushZombies < RedisQueuedLocks::Swarm::SwarmElem
|
|
80
81
|
rconn.call('HDEL', RedisQueuedLocks::Resource::SWARM_KEY, *zombie_hosts)
|
81
82
|
|
82
83
|
# Step 6: inform about deleted zombies
|
83
|
-
|
84
|
+
{
|
84
85
|
ok: true,
|
85
86
|
deleted_zombie_hosts: zombie_hosts,
|
86
|
-
|
87
|
+
deleted_zombie_acquirers: zombie_acquirers,
|
87
88
|
deleted_zombie_locks: zombie_locks
|
88
|
-
|
89
|
+
}
|
89
90
|
end
|
90
91
|
end
|
91
92
|
# rubocop:enable Metrics/MethodLength
|
@@ -96,7 +97,7 @@ class RedisQueuedLocks::Swarm::FlushZombies < RedisQueuedLocks::Swarm::SwarmElem
|
|
96
97
|
# @api private
|
97
98
|
# @since 1.9.0
|
98
99
|
def enabled?
|
99
|
-
rql_client.config[
|
100
|
+
rql_client.config['swarm.flush_zombies.enabled_for_swarm']
|
100
101
|
end
|
101
102
|
|
102
103
|
# @return [void]
|
@@ -105,11 +106,11 @@ class RedisQueuedLocks::Swarm::FlushZombies < RedisQueuedLocks::Swarm::SwarmElem
|
|
105
106
|
# @since 1.9.0
|
106
107
|
def swarm!
|
107
108
|
@swarm_element = Ractor.new(
|
108
|
-
rql_client.config.
|
109
|
-
rql_client.config[
|
110
|
-
rql_client.config[
|
111
|
-
rql_client.config[
|
112
|
-
rql_client.config[
|
109
|
+
rql_client.config.slice('swarm.flush_zombies.redis_config'),
|
110
|
+
rql_client.config['swarm.flush_zombies.zombie_ttl'],
|
111
|
+
rql_client.config['swarm.flush_zombies.zombie_lock_scan_size'],
|
112
|
+
rql_client.config['swarm.flush_zombies.zombie_queue_scan_size'],
|
113
|
+
rql_client.config['swarm.flush_zombies.zombie_flush_period']
|
113
114
|
) do |rc, z_ttl, z_lss, z_qss, z_fl_prd|
|
114
115
|
RedisQueuedLocks::Swarm::FlushZombies.swarm_loop do
|
115
116
|
Thread.new do
|
@@ -4,24 +4,25 @@
|
|
4
4
|
# @since 1.9.0
|
5
5
|
class RedisQueuedLocks::Swarm::ProbeHosts < RedisQueuedLocks::Swarm::SwarmElement::Threaded
|
6
6
|
class << self
|
7
|
+
# Returns a list of living hosts as a element command result. Result example:
|
8
|
+
# {
|
9
|
+
# ok: <Boolean>,
|
10
|
+
# result: {
|
11
|
+
# host_id1 <String> => score1 <String>,
|
12
|
+
# host_id2 <String> => score2 <String>,
|
13
|
+
# etc...
|
14
|
+
# }
|
15
|
+
# }
|
16
|
+
#
|
7
17
|
# @param redis_client [RedisClient]
|
8
18
|
# @param uniq_identity [String]
|
9
|
-
# @return [
|
10
|
-
# RedisQueuedLocks::Data[
|
11
|
-
# ok: <Boolean>,
|
12
|
-
# result: {
|
13
|
-
# host_id1 <String> => score1 <String>,
|
14
|
-
# host_id2 <String> => score2 <String>,
|
15
|
-
# etc...
|
16
|
-
# }
|
17
|
-
# ]
|
18
|
-
# ]
|
19
|
+
# @return [::Hash]
|
19
20
|
#
|
20
21
|
# @api private
|
21
22
|
# @since 1.9.0
|
22
23
|
def probe_hosts(redis_client, uniq_identity)
|
23
24
|
possible_hosts = RedisQueuedLocks::Resource.possible_host_identifiers(uniq_identity)
|
24
|
-
probed_hosts = {}
|
25
|
+
probed_hosts = {} #: Hash[String,Float]
|
25
26
|
|
26
27
|
redis_client.with do |rconn|
|
27
28
|
possible_hosts.each do |host_id|
|
@@ -33,9 +34,9 @@ class RedisQueuedLocks::Swarm::ProbeHosts < RedisQueuedLocks::Swarm::SwarmElemen
|
|
33
34
|
)
|
34
35
|
probed_hosts[host_id] = probe_score
|
35
36
|
end
|
36
|
-
|
37
|
-
RedisQueuedLocks::Data[ok: true, result: probed_hosts]
|
38
37
|
end
|
38
|
+
|
39
|
+
{ ok: true, result: probed_hosts }
|
39
40
|
end
|
40
41
|
end
|
41
42
|
|
@@ -44,7 +45,7 @@ class RedisQueuedLocks::Swarm::ProbeHosts < RedisQueuedLocks::Swarm::SwarmElemen
|
|
44
45
|
# @api private
|
45
46
|
# @since 1.9.0
|
46
47
|
def enabled?
|
47
|
-
rql_client.config[
|
48
|
+
rql_client.config['swarm.probe_hosts.enabled_for_swarm']
|
48
49
|
end
|
49
50
|
|
50
51
|
# @return [Thread]
|
@@ -54,15 +55,15 @@ class RedisQueuedLocks::Swarm::ProbeHosts < RedisQueuedLocks::Swarm::SwarmElemen
|
|
54
55
|
def spawn_main_loop!
|
55
56
|
Thread.new do
|
56
57
|
redis_client = RedisQueuedLocks::Swarm::RedisClientBuilder.build(
|
57
|
-
pooled: rql_client.config[
|
58
|
-
sentinel: rql_client.config[
|
59
|
-
config: rql_client.config[
|
60
|
-
pool_config: rql_client.config[
|
58
|
+
pooled: rql_client.config['swarm.probe_hosts.redis_config.pooled'],
|
59
|
+
sentinel: rql_client.config['swarm.probe_hosts.redis_config.sentinel'],
|
60
|
+
config: rql_client.config['swarm.probe_hosts.redis_config.config'],
|
61
|
+
pool_config: rql_client.config['swarm.probe_hosts.redis_config.pool_config']
|
61
62
|
)
|
62
63
|
|
63
64
|
loop do
|
64
65
|
RedisQueuedLocks::Swarm::ProbeHosts.probe_hosts(redis_client, rql_client.uniq_identity)
|
65
|
-
sleep(rql_client.config[
|
66
|
+
sleep(rql_client.config['swarm.probe_hosts.probe_period'])
|
66
67
|
end
|
67
68
|
end
|
68
69
|
end
|
@@ -7,7 +7,7 @@ module RedisQueuedLocks::Swarm::RedisClientBuilder
|
|
7
7
|
# @option pooled [Boolean]
|
8
8
|
# @option sentinel [Boolean]
|
9
9
|
# @option config [Hash]
|
10
|
-
# @return [RedisClient]
|
10
|
+
# @return [RedisClient|RedisClient::Pooled]
|
11
11
|
#
|
12
12
|
# @api private
|
13
13
|
# @since 1.9.0
|
@@ -28,7 +28,7 @@ module RedisQueuedLocks::Swarm::RedisClientBuilder
|
|
28
28
|
private
|
29
29
|
|
30
30
|
# @param config [Hash]
|
31
|
-
# @return [RedisClient::
|
31
|
+
# @return [RedisClient::SentinelConfig]
|
32
32
|
#
|
33
33
|
# @api private
|
34
34
|
# @since 1.9.0
|
@@ -47,7 +47,7 @@ module RedisQueuedLocks::Swarm::RedisClientBuilder
|
|
47
47
|
|
48
48
|
# @param redis_config [ReidsClient::Config]
|
49
49
|
# @param pool_config [Hash]
|
50
|
-
# @return [RedisClient]
|
50
|
+
# @return [RedisClient::Pooled]
|
51
51
|
#
|
52
52
|
# @api private
|
53
53
|
# @since 1.9.0
|
@@ -43,11 +43,11 @@ class RedisQueuedLocks::Swarm::Supervisor
|
|
43
43
|
@observable = observable
|
44
44
|
@visor = Thread.new do
|
45
45
|
loop do
|
46
|
-
yield rescue nil # TODO
|
47
|
-
sleep(rql_client.config[
|
46
|
+
yield rescue nil # TODO: (CHECK): may be we need to process exceptions here
|
47
|
+
sleep(rql_client.config['swarm.supervisor.liveness_probing_period'])
|
48
48
|
end
|
49
49
|
end
|
50
|
-
# NOTE: need to give a timespot to initialize visor thread;
|
50
|
+
# NOTE: need to give a timespot to initialize a visor thread;
|
51
51
|
sleep(0.1)
|
52
52
|
end
|
53
53
|
|
@@ -56,7 +56,11 @@ class RedisQueuedLocks::Swarm::Supervisor
|
|
56
56
|
# @api private
|
57
57
|
# @since 1.9.0
|
58
58
|
def running?
|
59
|
-
|
59
|
+
# NOTE:
|
60
|
+
# steep can not understand that visor.alive? is invoked on
|
61
|
+
# `::Thread` here (not on `::Thread | nil` after the `nil`-check);
|
62
|
+
# so we need to ignore this check temporary and wait the best future :)
|
63
|
+
visor != nil && visor.alive? # steep:ignore
|
60
64
|
end
|
61
65
|
|
62
66
|
# @return [void]
|
@@ -64,7 +68,11 @@ class RedisQueuedLocks::Swarm::Supervisor
|
|
64
68
|
# @api private
|
65
69
|
# @since 1.9.0
|
66
70
|
def stop!
|
67
|
-
|
71
|
+
# NOTE:
|
72
|
+
# steep can not understand that visor.kill is invoked on
|
73
|
+
# `::Thread` here (not on `::Thread | nil` after the `nil`-check);
|
74
|
+
# so we need to ignore this check temporary and wait the best future :)
|
75
|
+
visor.kill if running? # steep:ignore
|
68
76
|
@visor = nil
|
69
77
|
@observable = nil
|
70
78
|
end
|
@@ -74,9 +82,14 @@ class RedisQueuedLocks::Swarm::Supervisor
|
|
74
82
|
# @api private
|
75
83
|
# @since 1.9.0
|
76
84
|
def status
|
85
|
+
# NOTE:
|
86
|
+
# steep can not understand that thread_state(visor) is invoked on
|
87
|
+
# `::Thread` here (not on `::Thread | nil` after the `nil`-check);
|
88
|
+
# so we need to ignore this check temporary and wait the best future :)
|
89
|
+
|
77
90
|
{
|
78
91
|
running: running?,
|
79
|
-
state: (visor == nil) ? 'non_initialized' : thread_state(visor),
|
92
|
+
state: (visor == nil) ? 'non_initialized' : thread_state(visor), # steep:ignore
|
80
93
|
observable: (observable == nil) ? 'non_initialized' : 'initialized'
|
81
94
|
}
|
82
95
|
end
|
@@ -25,6 +25,7 @@ class RedisQueuedLocks::Swarm::SwarmElement::Isolated
|
|
25
25
|
# @since 1.9.0
|
26
26
|
attr_reader :sync
|
27
27
|
|
28
|
+
# @param rql_client [RedisQueuedLocks::Client]
|
28
29
|
# @return [void]
|
29
30
|
#
|
30
31
|
# @api private
|
@@ -80,12 +81,12 @@ class RedisQueuedLocks::Swarm::SwarmElement::Isolated
|
|
80
81
|
#
|
81
82
|
# @api private
|
82
83
|
# @since 1.9.0
|
83
|
-
def enabled?
|
84
|
+
def enabled? # steep:ignore
|
84
85
|
# NOTE: provide an <is enabled> logic here by analyzing the redis queued locks config.
|
85
86
|
end
|
86
87
|
|
87
|
-
# @return [Hash<Symbol,Boolean|Hash<Symbol,String|Boolean>>]
|
88
|
-
#
|
88
|
+
# @return [Hash<Symbol,Boolean|Hash<Symbol,String|Boolean>>] Format:
|
89
|
+
# {
|
89
90
|
# enabled: <Boolean>,
|
90
91
|
# ractor: {
|
91
92
|
# running: <Boolean>,
|
@@ -102,16 +103,16 @@ class RedisQueuedLocks::Swarm::SwarmElement::Isolated
|
|
102
103
|
def status
|
103
104
|
sync.synchronize do
|
104
105
|
ractor_running = swarmed__alive?
|
105
|
-
ractor_state = swarmed? ? ractor_status(swarm_element) : 'non_initialized'
|
106
|
+
ractor_state = swarmed? ? ractor_status(swarm_element) : 'non_initialized' # steep:ignore
|
106
107
|
|
107
|
-
main_loop_running = nil
|
108
|
-
main_loop_state = nil
|
109
108
|
begin
|
110
109
|
main_loop_running = swarmed__running?
|
110
|
+
# steep:ignore:start
|
111
111
|
main_loop_state =
|
112
112
|
main_loop_running ? swarm_loop__status[:main_loop][:state] : 'non_initialized'
|
113
|
+
# steep:ignore:end
|
113
114
|
rescue Ractor::ClosedError
|
114
|
-
# NOTE: it can happend when you
|
115
|
+
# NOTE: it can happend when you running RedisQueuedLocks::Swarm#deswarm!
|
115
116
|
main_loop_running = false
|
116
117
|
main_loop_state = 'non_initialized'
|
117
118
|
end
|
@@ -160,6 +161,7 @@ class RedisQueuedLocks::Swarm::SwarmElement::Isolated
|
|
160
161
|
# to provide better code readability (it is placed next to the method inside
|
161
162
|
# wich it should be called (see #swarm!)). That's why some rubocop cops are disabled.
|
162
163
|
|
164
|
+
# @type var main_loop: Thread?
|
163
165
|
main_loop = nil
|
164
166
|
|
165
167
|
loop do
|
@@ -167,24 +169,24 @@ class RedisQueuedLocks::Swarm::SwarmElement::Isolated
|
|
167
169
|
|
168
170
|
case command
|
169
171
|
when :status
|
170
|
-
main_loop_alive = main_loop != nil && main_loop.alive?
|
172
|
+
main_loop_alive = main_loop != nil && main_loop.alive? # steep:ignore
|
171
173
|
main_loop_state =
|
172
174
|
if main_loop == nil
|
173
175
|
'non_initialized'
|
174
176
|
else
|
175
|
-
#
|
177
|
+
# @type var main_loop: Thread
|
176
178
|
RedisQueuedLocks::Utilities.thread_state(main_loop)
|
177
179
|
end
|
178
180
|
Ractor.yield({ main_loop: { alive: main_loop_alive, state: main_loop_state } })
|
179
181
|
when :is_active
|
180
|
-
Ractor.yield(main_loop != nil && main_loop.alive?)
|
182
|
+
Ractor.yield(main_loop != nil && main_loop.alive?) # steep:ignore
|
181
183
|
when :start
|
182
|
-
main_loop.kill if main_loop != nil
|
184
|
+
main_loop.kill if main_loop != nil # steep:ignore
|
183
185
|
main_loop = yield # REFERENCE: `main_loop_spawner.call`
|
184
186
|
when :stop
|
185
|
-
main_loop.kill if main_loop != nil
|
187
|
+
main_loop.kill if main_loop != nil # steep:ignore
|
186
188
|
when :kill
|
187
|
-
main_loop.kill if main_loop != nil
|
189
|
+
main_loop.kill if main_loop != nil # steep:ignore
|
188
190
|
exit
|
189
191
|
end
|
190
192
|
end
|
@@ -212,7 +214,7 @@ class RedisQueuedLocks::Swarm::SwarmElement::Isolated
|
|
212
214
|
# @api private
|
213
215
|
# @since 1.9.0
|
214
216
|
def swarmed__alive?
|
215
|
-
swarm_element != nil && ractor_alive?(swarm_element)
|
217
|
+
swarm_element != nil && ractor_alive?(swarm_element) # steep:ignore
|
216
218
|
end
|
217
219
|
|
218
220
|
# @return [Boolean]
|
@@ -220,7 +222,7 @@ class RedisQueuedLocks::Swarm::SwarmElement::Isolated
|
|
220
222
|
# @api private
|
221
223
|
# @since 1.9.0
|
222
224
|
def swarmed__dead?
|
223
|
-
swarm_element != nil && !ractor_alive?(swarm_element)
|
225
|
+
swarm_element != nil && !ractor_alive?(swarm_element) # steep:ignore
|
224
226
|
end
|
225
227
|
|
226
228
|
# @return [Boolean]
|
@@ -228,7 +230,7 @@ class RedisQueuedLocks::Swarm::SwarmElement::Isolated
|
|
228
230
|
# @api private
|
229
231
|
# @since 1.9.0
|
230
232
|
def swarmed__running?
|
231
|
-
swarm_element != nil && ractor_alive?(swarm_element) && swarm_loop__is_active
|
233
|
+
swarm_element != nil && ractor_alive?(swarm_element) && swarm_loop__is_active # steep:ignore
|
232
234
|
end
|
233
235
|
|
234
236
|
# @return [Boolean]
|
@@ -236,7 +238,7 @@ class RedisQueuedLocks::Swarm::SwarmElement::Isolated
|
|
236
238
|
# @api private
|
237
239
|
# @since 1.9.0
|
238
240
|
def swarmed__stopped?
|
239
|
-
swarm_element != nil && ractor_alive?(swarm_element) && !swarm_loop__is_active
|
241
|
+
swarm_element != nil && ractor_alive?(swarm_element) && !swarm_loop__is_active # steep:ignore
|
240
242
|
end
|
241
243
|
|
242
244
|
# @return [Boolean]
|
@@ -244,7 +246,7 @@ class RedisQueuedLocks::Swarm::SwarmElement::Isolated
|
|
244
246
|
# @api private
|
245
247
|
# @since 1.9.0
|
246
248
|
def swarm_loop__is_active
|
247
|
-
return if idle? || swarmed__dead?
|
249
|
+
return false if idle? || swarmed__dead?
|
248
250
|
sync.synchronize { swarm_element.send(:is_active).take }
|
249
251
|
end
|
250
252
|
|