sidekiq-priority_queue 1.0.3 → 1.0.4

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: 1d7a0cfd84d18380c50133959601b743de11149791f887badcc8436f7d015a88
4
- data.tar.gz: 135fd23d2e9da9b325767059fed2fa227b891ade3d5de9d6fce95fca26fb75f4
3
+ metadata.gz: 041d042d2b08144431bc0e2ea76043e0f5f7e5cec5c28fc1c5a76b17c5390069
4
+ data.tar.gz: e2e2dc2e38375942d02a93740191a4aca744caa8d3462667dbd63780e0585ff6
5
5
  SHA512:
6
- metadata.gz: 9535de17f1caa416733929806815348399e5871c51f1dbe114d164486ed9889379fbba7074ab31bce0e5c9c6d7c76e5824789b8c703c37a550a6c9f4e04660c9
7
- data.tar.gz: a9d5d8f8a1688c6ef79d82fc30c9187f2cbd16658506b9be06a3750a62b2d6fdc5fe35f25eebb7535658f2a83fc369d22734cd8367a26a90eda98ee9857bb8aa
6
+ metadata.gz: b02c99baec57233cc4d315662faeb078d75d4bf887d43ec22cbec313bb4e1b7f2716236c1cbae27284871bd335d9701d26bb7ab9737438212e5448aa0ef2858a
7
+ data.tar.gz: 48e42a6e9eb50b28ae3446a4aacbdb3294fc734b48421fb4cbe86fc3add06b0feaf95c1eed3f60f1ae2a0e0e1b372c979f05097a8dbfbefd3c416143a0716c0c
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sidekiq-priority_queue (1.0.3)
4
+ sidekiq-priority_queue (1.0.4)
5
5
  sidekiq (>= 6.2.2)
6
6
 
7
7
  GEM
@@ -23,7 +23,7 @@ GEM
23
23
  rack-test (1.1.0)
24
24
  rack (>= 1.0, < 3)
25
25
  rake (13.0.3)
26
- redis (4.4.0)
26
+ redis (4.5.1)
27
27
  sidekiq (6.2.2)
28
28
  connection_pool (>= 2.2.2)
29
29
  rack (~> 2.0)
@@ -47,4 +47,4 @@ DEPENDENCIES
47
47
  simplecov
48
48
 
49
49
  BUNDLED WITH
50
- 2.1.4
50
+ 2.2.3
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
  require 'sidekiq'
3
+ require 'sidekiq/util'
3
4
 
4
5
  module Sidekiq
5
6
  module PriorityQueue
6
7
  class ReliableFetch
8
+ include Sidekiq::Util
7
9
 
8
10
  UnitOfWork = Struct.new(:queue, :job, :wip_queue) do
9
11
  def acknowledge
@@ -38,10 +40,26 @@ module Sidekiq
38
40
  @strictly_ordered_queues = !!options[:strict]
39
41
  @queues = options[:queues].map { |q| "priority-queue:#{q}" }
40
42
  @queues = @queues.uniq if @strictly_ordered_queues
43
+ @done = false
41
44
  @process_index = options[:index] || ENV['PROCESS_INDEX']
42
45
  end
43
46
 
47
+ def setup
48
+ Sidekiq.on(:startup) do
49
+ cleanup_the_dead
50
+ register_myself
51
+ end
52
+ Sidekiq.on(:shutdown) do
53
+ @done = true
54
+ end
55
+ Sidekiq.on(:heartbeat) do
56
+ register_myself
57
+ end
58
+ end
59
+
44
60
  def retrieve_work
61
+ return nil if @done
62
+
45
63
  work = @queues.detect do |q|
46
64
  job = zpopmin_sadd(q, wip_queue(q));
47
65
  break [q,job] if job
@@ -50,7 +68,7 @@ module Sidekiq
50
68
  end
51
69
 
52
70
  def wip_queue(q)
53
- "#{q}_#{Socket.gethostname}_#{@process_index}"
71
+ "queue:spriorityq|#{identity}|#{q}"
54
72
  end
55
73
 
56
74
  def zpopmin_sadd(queue, wip_queue)
@@ -61,7 +79,7 @@ module Sidekiq
61
79
  end
62
80
 
63
81
  def spop(wip_queue)
64
- Sidekiq.redis{ |con| con.spop(wip_queue) }
82
+ Sidekiq.redis { |con| con.spop(wip_queue) }
65
83
  end
66
84
 
67
85
  def queues_cmd
@@ -72,55 +90,107 @@ module Sidekiq
72
90
  end
73
91
  end
74
92
 
75
- def bulk_requeue(_inprogress, options)
76
- Sidekiq.logger.debug { "Re-queueing terminated jobs" }
77
- process_index = options[:index] || ENV['PROCESS_INDEX']
78
- self.class.requeue_wip_jobs(options[:queues], process_index)
93
+ # Below method is called when we close sidekiq process gracefully
94
+ def bulk_requeue(_inprogress, _options)
95
+ Sidekiq.logger.debug { "Priority ReliableFetch: Re-queueing terminated jobs" }
96
+ requeue_wip_jobs
97
+ unregister_super_process
79
98
  end
80
99
 
81
- def self.resume_wip_jobs(queues, process_index)
82
- Sidekiq.logger.debug { "Re-queueing WIP jobs" }
83
- process_index ||= ENV['PROCESS_INDEX']
84
- requeue_wip_jobs(queues, process_index)
85
- end
100
+ private
86
101
 
87
- Sidekiq.configure_server do |config|
88
- config.on(:startup) do
89
- if reliable_fetch_active?(config)
90
- Sidekiq::PriorityQueue::ReliableFetch.resume_wip_jobs(config.options[:queues], config.options[:index])
102
+ # Below method is only to make sure we get jobs from incorrectly closed process (for example force killed using kill -9 SIDEKIQ_PID)
103
+ def cleanup_the_dead
104
+ overall_moved_count = 0
105
+ Sidekiq.redis do |conn|
106
+ conn.sscan_each("super_processes_priority") do |super_process|
107
+ next if conn.exists?(super_process) # Don't clean up currently running processes
108
+
109
+ Sidekiq.logger.debug { "Priority ReliableFetch: Moving job from #{super_process} back to original queues" }
110
+
111
+ # We need to pushback any leftover jobs still in WIP
112
+ previously_handled_queues = conn.smembers("#{super_process}:super_priority_queues")
113
+
114
+ # Below previously_handled_queues are simply WIP queues of previous, dead processes
115
+ previously_handled_queues.each do |previously_handled_queue|
116
+ queue_moved_size = 0
117
+ original_priority_queue_name = previously_handled_queue.split('|').last
118
+
119
+ Sidekiq.logger.debug { "Priority ReliableFetch: Moving job from #{previously_handled_queue} back to original queue: #{original_priority_queue_name}" }
120
+ loop do
121
+ break if conn.scard(previously_handled_queue) == 0
122
+
123
+ # Here we should wrap below two operations in Lua script
124
+ item = conn.spop(previously_handled_queue)
125
+ conn.zadd(original_priority_queue_name, 0, item)
126
+ queue_moved_size += 1
127
+ overall_moved_count += 1
128
+ end
129
+ # Below we simply remove old WIP queue
130
+ conn.srem(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} "}
132
+ end
133
+
134
+ Sidekiq.logger.debug { "Priority ReliableFetch: Unregistering super process #{super_process}" }
135
+ conn.del("#{super_process}:super_priority_queues")
136
+ conn.srem("super_processes_priority", super_process)
91
137
  end
92
138
  end
139
+ Sidekiq.logger.debug { "Priority ReliableFetch: Moved overall #{overall_moved_count} jobs from WIP queues" }
140
+ rescue => ex
141
+ # best effort, ignore Redis network errors
142
+ Sidekiq.logger.warn { "Priority ReliableFetch: Failed to requeue: #{ex.message}" }
93
143
  end
94
144
 
95
- private
96
-
97
- def self.reliable_fetch_active?(config)
98
- return true if config.options[:fetch].is_a?(Sidekiq::PriorityQueue::ReliableFetch)
99
- return config.options[:fetch].is_a?(Sidekiq::PriorityQueue::CombinedFetch) &&
100
- config.options[:fetch].fetches.any? { |f| f.is_a?(Sidekiq::PriorityQueue::ReliableFetch) }
101
- end
102
-
103
- def self.requeue_wip_jobs(queues, index)
145
+ def requeue_wip_jobs
104
146
  jobs_to_requeue = {}
105
147
  Sidekiq.redis do |conn|
106
- queues.map { |q| "priority-queue:#{q}" }.each do |q|
107
- wip_queue = "#{q}_#{Socket.gethostname}_#{index}"
148
+ @queues.each do |q|
149
+ wip_queue_name = wip_queue(q)
108
150
  jobs_to_requeue[q] = []
109
- while job = conn.spop(wip_queue) do
151
+
152
+ while job = conn.spop(wip_queue_name) do
110
153
  jobs_to_requeue[q] << job
111
154
  end
112
155
  end
113
156
 
114
157
  conn.pipelined do
115
158
  jobs_to_requeue.each do |queue, jobs|
116
- return unless jobs.size > 0
117
- conn.zadd(queue, jobs.map{|j| [0,j] })
159
+ next if jobs.size == 0 # ZADD doesn't work with empty arrays
160
+
161
+ conn.zadd(queue, jobs.map {|j| [0, j] })
118
162
  end
119
163
  end
120
164
  end
121
- Sidekiq.logger.info("Pushed #{ jobs_to_requeue.map{|q| q.size }.reduce(:+) } jobs back to Redis")
165
+ Sidekiq.logger.info("Priority ReliableFetch: Pushed #{ jobs_to_requeue.values.flatten.size } jobs back to Redis")
122
166
  rescue => ex
123
- Sidekiq.logger.warn("Failed to requeue #{ jobs_to_requeue.map{|q| q.size }.reduce(:+) } jobs: #{ex.message}")
167
+ Sidekiq.logger.warn("Priority ReliableFetch: Failed to requeue #{ jobs_to_requeue.values.flatten.size } jobs: #{ex.message}")
168
+ end
169
+
170
+ def register_myself
171
+ super_process_wip_queues = @queues.map { |q| wip_queue(q) }
172
+ id = identity # This is from standard sidekiq, updated with every heartbeat
173
+
174
+ # This method will run multiple times so seeing this message twice is no problem.
175
+ Sidekiq.logger.debug { "Priority ReliableFetch: Registering super process #{id} with #{super_process_wip_queues}" }
176
+
177
+ Sidekiq.redis do |conn|
178
+ conn.multi do
179
+ conn.sadd("super_processes_priority", id)
180
+ conn.sadd("#{id}:super_priority_queues", super_process_wip_queues)
181
+ end
182
+ end
183
+ end
184
+
185
+ def unregister_super_process
186
+ id = identity
187
+ Sidekiq.logger.debug { "Priority ReliableFetch: Unregistering super process #{id}" }
188
+ Sidekiq.redis do |conn|
189
+ conn.multi do
190
+ conn.srem("super_processes_priority", id)
191
+ conn.del("#{id}:super_priority_queues")
192
+ end
193
+ end
124
194
  end
125
195
  end
126
196
  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'
3
+ s.version = '1.0.4'
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,12 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-priority_queue
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jacob Matthews
8
8
  - Petr Kopac
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
  date: 2018-07-31 00:00:00.000000000 Z
@@ -78,7 +78,7 @@ homepage: https://github.com/chartmogul/sidekiq-priority_queue
78
78
  licenses:
79
79
  - MIT
80
80
  metadata: {}
81
- post_install_message:
81
+ post_install_message:
82
82
  rdoc_options: []
83
83
  require_paths:
84
84
  - lib
@@ -93,8 +93,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
93
93
  - !ruby/object:Gem::Version
94
94
  version: '0'
95
95
  requirements: []
96
- rubygems_version: 3.1.4
97
- signing_key:
96
+ rubygems_version: 3.2.3
97
+ signing_key:
98
98
  specification_version: 4
99
99
  summary: Priority Queuing for Sidekiq
100
100
  test_files: []