sidekiq-priority_queue 1.0.5 → 1.0.6
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/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"
|