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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e6f96814c578dd2c2009fc89c069aa41df566f1fb81bc2b058b9d2fc8247fc9c
4
- data.tar.gz: f55d5611495b8d61969508ab89124e0027685b182cd5ebdb8c72595417e534df
3
+ metadata.gz: 0e649f9936ca3989853559d9254ce6072229a700e5b5d4f17efc1dbf723c0cbe
4
+ data.tar.gz: d53da6399ee5f1405598de900b753ebbe3cb3b7b45e7ca2991b915c76ac1a3a8
5
5
  SHA512:
6
- metadata.gz: '084d4acf2bff8504fadeefb141ca6617cbfa7ed8241cc1fbb93367d2ab844c1acaec11b771b89b933165b391400929b21176ffcc053ca1f5a4bc09807936f6ac'
7
- data.tar.gz: c6628032befb4f253463df37ea0335d76434938b230891c51a779124e9f7bf5828e5e30b9de2c19c2a9ed76a02bf567059f737d797f335f3ca7b591e331354d6
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 |con|
76
- @script_sha ||= con.script(:load, Sidekiq::PriorityQueue::Scripts::ZPOPMIN_SADD)
77
- con.evalsha(@script_sha, [queue, wip_queue])
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 { "Priority ReliableFetch: Re-queueing terminated jobs" }
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("super_processes_priority") do |super_process|
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) == 0
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) == 0
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("super_processes_priority", super_process)
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 => ex
207
+ rescue StandardError => e
141
208
  # best effort, ignore Redis network errors
142
- Sidekiq.logger.warn { "Priority ReliableFetch: Failed to requeue: #{ex.message}" }
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) do
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.size == 0 # ZADD doesn't work with empty arrays
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 #{ jobs_to_requeue.values.flatten.size } jobs back to Redis")
166
- rescue => ex
167
- Sidekiq.logger.warn("Priority ReliableFetch: Failed to requeue #{ jobs_to_requeue.values.flatten.size } jobs: #{ex.message}")
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("super_processes_priority", id)
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("super_processes_priority", id)
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.5'
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"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-priority_queue
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jacob Matthews