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 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