sidekiq-priority_queue 1.0.5 → 1.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/sidekiq/priority_queue/reliable_fetch.rb +88 -21
- data/sidekiq-priority_queue.gemspec +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e649f9936ca3989853559d9254ce6072229a700e5b5d4f17efc1dbf723c0cbe
|
4
|
+
data.tar.gz: d53da6399ee5f1405598de900b753ebbe3cb3b7b45e7ca2991b915c76ac1a3a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9cf8bc3aca671561f792ce90592296655dba58541e0506c362c73ed3c54b42a8978235423c327f714b6f4749f9585ad29a942547ac5378621cfa1441bb215ef2
|
7
|
+
data.tar.gz: 8be1d8ad3d836104c19bc3e2473b6d77459ea3938557bd4672e72b92b3dc10a0e1cb2c2d94bc576e3c346c49a27528f94024bd9613603ef2e22de5ff1343d4ba
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'sidekiq'
|
3
4
|
require 'sidekiq/util'
|
4
5
|
|
@@ -7,6 +8,8 @@ module Sidekiq
|
|
7
8
|
class ReliableFetch
|
8
9
|
include Sidekiq::Util
|
9
10
|
|
11
|
+
SUPER_PROCESSES_REGISTRY_KEY = 'super_processes_priority'
|
12
|
+
|
10
13
|
UnitOfWork = Struct.new(:queue, :job, :wip_queue) do
|
11
14
|
def acknowledge
|
12
15
|
Sidekiq.redis do |conn|
|
@@ -37,6 +40,7 @@ module Sidekiq
|
|
37
40
|
end
|
38
41
|
|
39
42
|
def initialize(options)
|
43
|
+
@options = options
|
40
44
|
@strictly_ordered_queues = !!options[:strict]
|
41
45
|
@queues = options[:queues].map { |q| "priority-queue:#{q}" }
|
42
46
|
@queues = @queues.uniq if @strictly_ordered_queues
|
@@ -48,6 +52,7 @@ module Sidekiq
|
|
48
52
|
Sidekiq.on(:startup) do
|
49
53
|
cleanup_the_dead
|
50
54
|
register_myself
|
55
|
+
check
|
51
56
|
end
|
52
57
|
Sidekiq.on(:shutdown) do
|
53
58
|
@done = true
|
@@ -61,8 +66,8 @@ module Sidekiq
|
|
61
66
|
return nil if @done
|
62
67
|
|
63
68
|
work = @queues.detect do |q|
|
64
|
-
job = zpopmin_sadd(q, wip_queue(q))
|
65
|
-
break [q,job] if job
|
69
|
+
job = zpopmin_sadd(q, wip_queue(q))
|
70
|
+
break [q, job] if job
|
66
71
|
end
|
67
72
|
UnitOfWork.new(*work, wip_queue(work.first)) if work
|
68
73
|
end
|
@@ -72,9 +77,9 @@ module Sidekiq
|
|
72
77
|
end
|
73
78
|
|
74
79
|
def zpopmin_sadd(queue, wip_queue)
|
75
|
-
Sidekiq.redis do |
|
76
|
-
@script_sha ||=
|
77
|
-
|
80
|
+
Sidekiq.redis do |conn|
|
81
|
+
@script_sha ||= conn.script(:load, Sidekiq::PriorityQueue::Scripts::ZPOPMIN_SADD)
|
82
|
+
conn.evalsha(@script_sha, [queue, wip_queue])
|
78
83
|
end
|
79
84
|
end
|
80
85
|
|
@@ -92,18 +97,80 @@ module Sidekiq
|
|
92
97
|
|
93
98
|
# Below method is called when we close sidekiq process gracefully
|
94
99
|
def bulk_requeue(_inprogress, _options)
|
95
|
-
Sidekiq.logger.debug {
|
100
|
+
Sidekiq.logger.debug { 'Priority ReliableFetch: Re-queueing terminated jobs' }
|
96
101
|
requeue_wip_jobs
|
97
102
|
unregister_super_process
|
98
103
|
end
|
99
104
|
|
100
105
|
private
|
101
106
|
|
107
|
+
def check
|
108
|
+
check_for_orphans if orphan_check?
|
109
|
+
rescue StandardError => e
|
110
|
+
# orphan check is best effort, we don't want Redis downtime to
|
111
|
+
# break Sidekiq
|
112
|
+
Sidekiq.logger.warn { "Priority ReliableFetch: Failed to do orphan check: #{e.message}" }
|
113
|
+
end
|
114
|
+
|
115
|
+
def orphan_check?
|
116
|
+
delay = @options.fetch(:reliable_fetch_orphan_check, 3600).to_i
|
117
|
+
return false if delay.zero?
|
118
|
+
|
119
|
+
Sidekiq.redis do |conn|
|
120
|
+
conn.set('priority_reliable_fetch_orphan_check', Time.now.to_f, ex: delay, nx: true)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# This method is extra paranoid verification to check Redis for any possible
|
125
|
+
# orphaned queues with jobs. If we change queue names and lose jobs in the meantime,
|
126
|
+
# this will find old queues with jobs and rescue them.
|
127
|
+
def check_for_orphans
|
128
|
+
orphans_count = 0
|
129
|
+
queues_count = 0
|
130
|
+
orphan_queues = Set.new
|
131
|
+
Sidekiq.redis do |conn|
|
132
|
+
ids = conn.smembers(SUPER_PROCESSES_REGISTRY_KEY)
|
133
|
+
Sidekiq.logger.debug("Priority ReliableFetch found #{ids.size} super processes")
|
134
|
+
|
135
|
+
conn.scan_each(match: 'queue:spriorityq|*', count: 100) do |wip_queue|
|
136
|
+
queues_count += 1
|
137
|
+
_, id, original_priority_queue_name = wip_queue.split('|')
|
138
|
+
next if ids.include?(id)
|
139
|
+
|
140
|
+
# Race condition in pulling super_processes and checking queue liveness.
|
141
|
+
# Need to verify in Redis.
|
142
|
+
unless conn.sismember(SUPER_PROCESSES_REGISTRY_KEY, id)
|
143
|
+
orphan_queues << original_priority_queue_name
|
144
|
+
queue_jobs_count = 0
|
145
|
+
loop do
|
146
|
+
break if conn.scard(wip_queue).zero?
|
147
|
+
|
148
|
+
# Here we should wrap below two operations in Lua script
|
149
|
+
item = conn.spop(wip_queue)
|
150
|
+
conn.zadd(original_priority_queue_name, 0, item)
|
151
|
+
orphans_count += 1
|
152
|
+
queue_jobs_count += 1
|
153
|
+
end
|
154
|
+
if queue_jobs_count.positive?
|
155
|
+
Sidekiq::Pro.metrics.increment('jobs.recovered.fetch', by: queue_jobs_count, tags: ["queue:#{original_priority_queue_name}"])
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
if orphans_count.positive?
|
162
|
+
Sidekiq.logger.warn { "Priority ReliableFetch recovered #{orphans_count} orphaned jobs in queues: #{orphan_queues.to_a.inspect}" }
|
163
|
+
elsif queues_count.positive?
|
164
|
+
Sidekiq.logger.info { "Priority ReliableFetch found #{queues_count} working queues with no orphaned jobs" }
|
165
|
+
end
|
166
|
+
orphans_count
|
167
|
+
end
|
168
|
+
|
102
169
|
# Below method is only to make sure we get jobs from incorrectly closed process (for example force killed using kill -9 SIDEKIQ_PID)
|
103
170
|
def cleanup_the_dead
|
104
171
|
overall_moved_count = 0
|
105
172
|
Sidekiq.redis do |conn|
|
106
|
-
conn.sscan_each(
|
173
|
+
conn.sscan_each(SUPER_PROCESSES_REGISTRY_KEY) do |super_process|
|
107
174
|
next if conn.exists?(super_process) # Don't clean up currently running processes
|
108
175
|
|
109
176
|
Sidekiq.logger.debug { "Priority ReliableFetch: Moving job from #{super_process} back to original queues" }
|
@@ -118,7 +185,7 @@ module Sidekiq
|
|
118
185
|
|
119
186
|
Sidekiq.logger.debug { "Priority ReliableFetch: Moving job from #{previously_handled_queue} back to original queue: #{original_priority_queue_name}" }
|
120
187
|
loop do
|
121
|
-
break if conn.scard(previously_handled_queue)
|
188
|
+
break if conn.scard(previously_handled_queue).zero?
|
122
189
|
|
123
190
|
# Here we should wrap below two operations in Lua script
|
124
191
|
item = conn.spop(previously_handled_queue)
|
@@ -127,19 +194,19 @@ module Sidekiq
|
|
127
194
|
overall_moved_count += 1
|
128
195
|
end
|
129
196
|
# Below we simply remove old WIP queue
|
130
|
-
conn.del(previously_handled_queue) if conn.scard(previously_handled_queue)
|
131
|
-
Sidekiq.logger.debug { "Priority ReliableFetch: Moved #{queue_moved_size} jobs from ##{previously_handled_queue} back to original_queue: #{original_priority_queue_name} "}
|
197
|
+
conn.del(previously_handled_queue) if conn.scard(previously_handled_queue).zero?
|
198
|
+
Sidekiq.logger.debug { "Priority ReliableFetch: Moved #{queue_moved_size} jobs from ##{previously_handled_queue} back to original_queue: #{original_priority_queue_name} " }
|
132
199
|
end
|
133
200
|
|
134
201
|
Sidekiq.logger.debug { "Priority ReliableFetch: Unregistering super process #{super_process}" }
|
135
202
|
conn.del("#{super_process}:super_priority_queues")
|
136
|
-
conn.srem(
|
203
|
+
conn.srem(SUPER_PROCESSES_REGISTRY_KEY, super_process)
|
137
204
|
end
|
138
205
|
end
|
139
206
|
Sidekiq.logger.debug { "Priority ReliableFetch: Moved overall #{overall_moved_count} jobs from WIP queues" }
|
140
|
-
rescue =>
|
207
|
+
rescue StandardError => e
|
141
208
|
# best effort, ignore Redis network errors
|
142
|
-
Sidekiq.logger.warn { "Priority ReliableFetch: Failed to requeue: #{
|
209
|
+
Sidekiq.logger.warn { "Priority ReliableFetch: Failed to requeue: #{e.message}" }
|
143
210
|
end
|
144
211
|
|
145
212
|
def requeue_wip_jobs
|
@@ -149,22 +216,22 @@ module Sidekiq
|
|
149
216
|
wip_queue_name = wip_queue(q)
|
150
217
|
jobs_to_requeue[q] = []
|
151
218
|
|
152
|
-
while job = conn.spop(wip_queue_name)
|
219
|
+
while job = conn.spop(wip_queue_name)
|
153
220
|
jobs_to_requeue[q] << job
|
154
221
|
end
|
155
222
|
end
|
156
223
|
|
157
224
|
conn.pipelined do
|
158
225
|
jobs_to_requeue.each do |queue, jobs|
|
159
|
-
next if jobs.
|
226
|
+
next if jobs.empty? # ZADD doesn't work with empty arrays
|
160
227
|
|
161
|
-
conn.zadd(queue, jobs.map {|j| [0, j] })
|
228
|
+
conn.zadd(queue, jobs.map { |j| [0, j] })
|
162
229
|
end
|
163
230
|
end
|
164
231
|
end
|
165
|
-
Sidekiq.logger.info("Priority ReliableFetch: Pushed #{
|
166
|
-
rescue =>
|
167
|
-
Sidekiq.logger.warn("Priority ReliableFetch: Failed to requeue #{
|
232
|
+
Sidekiq.logger.info("Priority ReliableFetch: Pushed #{jobs_to_requeue.values.flatten.size} jobs back to Redis")
|
233
|
+
rescue StandardError => e
|
234
|
+
Sidekiq.logger.warn("Priority ReliableFetch: Failed to requeue #{jobs_to_requeue.values.flatten.size} jobs: #{e.message}")
|
168
235
|
end
|
169
236
|
|
170
237
|
def register_myself
|
@@ -176,7 +243,7 @@ module Sidekiq
|
|
176
243
|
|
177
244
|
Sidekiq.redis do |conn|
|
178
245
|
conn.multi do
|
179
|
-
conn.sadd(
|
246
|
+
conn.sadd(SUPER_PROCESSES_REGISTRY_KEY, id)
|
180
247
|
conn.sadd("#{id}:super_priority_queues", super_process_wip_queues)
|
181
248
|
end
|
182
249
|
end
|
@@ -187,7 +254,7 @@ module Sidekiq
|
|
187
254
|
Sidekiq.logger.debug { "Priority ReliableFetch: Unregistering super process #{id}" }
|
188
255
|
Sidekiq.redis do |conn|
|
189
256
|
conn.multi do
|
190
|
-
conn.srem(
|
257
|
+
conn.srem(SUPER_PROCESSES_REGISTRY_KEY, id)
|
191
258
|
conn.del("#{id}:super_priority_queues")
|
192
259
|
end
|
193
260
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'sidekiq-priority_queue'
|
3
|
-
s.version = '1.0.
|
3
|
+
s.version = '1.0.6'
|
4
4
|
s.date = '2018-07-31'
|
5
5
|
s.summary = "Priority Queuing for Sidekiq"
|
6
6
|
s.description = "An extension for Sidekiq allowing jobs in a single queue to be executed by a priority score rather than FIFO"
|