resque-fifo-queue 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,9 @@
1
+ module Resque
2
+ module Plugins
3
+ module Fifo
4
+ module Queue
5
+ VERSION = "0.1.1"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,83 @@
1
+ require 'resque/server'
2
+
3
+ module Resque
4
+ module Plugins
5
+ module Fifo
6
+ module Server
7
+ VIEW_PATH = File.join(File.dirname(__FILE__), 'server', 'views')
8
+
9
+ module Helpers
10
+ def poll
11
+ if @polling
12
+ text = "Last Updated: #{Time.now.strftime("%H:%M:%S")}"
13
+ else
14
+ text = "<a href='#{u(request.path_info)}.poll' rel='poll'>Live Poll</a>"
15
+ end
16
+ "<p class='poll'>#{text}</p>"
17
+ end
18
+
19
+ def show_page(page, layout = true)
20
+ response["Cache-Control"] = "max-age=0, private, must-revalidate"
21
+ begin
22
+ erb(File.read(File.join(VIEW_PATH, page)), {:layout => layout})
23
+ rescue Errno::ECONNREFUSED
24
+ erb :error, {:layout => false}, :error => "Can't connect to Redis! (#{Resque.redis_id})"
25
+ end
26
+ end
27
+ end
28
+
29
+ class << self
30
+ def registered(app)
31
+ app.get '/fifo_queues' do
32
+ @refresh_requested = false
33
+ @manager = Resque::Plugins::Fifo::Queue::Manager.new
34
+ @queue_with_slices = @manager.dump_queues_with_slices
35
+ @orphaned_queues = @manager.orphaned_queues
36
+ show_page('fifo_queues.erb')
37
+ end
38
+
39
+ app.get '/fifo_queues.poll' do
40
+ @polling = true
41
+ @refresh_requested = false
42
+ @manager = Resque::Plugins::Fifo::Queue::Manager.new
43
+ @orphaned_queues = @manager.orphaned_queues
44
+ @queue_with_slices = @manager.dump_queues_with_slices
45
+ show_page('fifo_queues.erb', false)
46
+ end
47
+
48
+ app.post '/shared_finder' do
49
+ @manager = Resque::Plugins::Fifo::Queue::Manager.new
50
+ @queue_name = @manager.compute_queue_name(params[:key])
51
+ @worker = @manager.worker_for_queue(@queue_name)
52
+ erb(File.read(File.join(VIEW_PATH, 'shared_finder.erb')))
53
+ end
54
+
55
+ app.post '/request_update' do
56
+ @manager = Resque::Plugins::Fifo::Queue::Manager.new
57
+ @manager.request_refresh
58
+ @refresh_requested = true
59
+ @queue_with_slices = @manager.dump_queues_with_slices
60
+ redirect '/resque/fifo_queues'
61
+ end
62
+
63
+ app.post '/clear_stats' do
64
+ @manager = Resque::Plugins::Fifo::Queue::Manager.new
65
+ @manager.clear_stats
66
+ @refresh_requested = true
67
+ @queue_with_slices = @manager.dump_queues_with_slices
68
+ redirect '/resque/fifo_queues'
69
+ end
70
+ # We have little choice in using this funky name - Resque
71
+ # already has a "Stats" tab, and it doesn't like
72
+ # tab names with spaces in it (it translates the url as job%20stats)
73
+ app.tabs << "FIFO_Queues"
74
+
75
+ app.helpers(Helpers)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ Resque::Server.register Resque::Plugins::Fifo::Server
@@ -0,0 +1,109 @@
1
+ <h1>Resque FIFO Managed Queues version: <%= Resque::Plugins::Fifo::Queue::VERSION %></h1>
2
+ <% if @refresh_requested %>
3
+ <p class="intro">
4
+ Updating worker list... Refresh to view updates.
5
+ </p>
6
+ <% end %>
7
+ <p class="intro">
8
+ This page displays statistics about available FIFO Managed Queues and its partitioning information.
9
+ </p>
10
+ <form method="POST" action="<%=u "/shared_finder" %>" >
11
+ <label for="key">Key</label>
12
+ <input id="key" type='text' name='key'/>
13
+ <input type='submit' name='' value='Find Shard' />
14
+ </form>
15
+ <h1>Perfomance Statistics</h1>
16
+ <p>Contains statistics related to the performance of the FIFO queues</p>
17
+ <form method="POST" action="<%=u "/clear_stats" %>" >
18
+ <input type='submit' name='' value='Reset Performance statistics' />
19
+ </form>
20
+ <table>
21
+ <thead>
22
+ <th>Name</th>
23
+ <th>Value</th>
24
+ <th>Description</th>
25
+ </thead>
26
+ <tbody>
27
+ <tr>
28
+ <td>Max Job Delay</td>
29
+ <td><%= @manager.get_stats_max_delay %></td>
30
+ <td>The worst case in ms that a job had waited before it gets processed.</td>
31
+ </tr>
32
+ <tr>
33
+ <td>Avg Job Delay</td>
34
+ <td><%= @manager.get_stats_avg_delay %></td>
35
+ <td>Average wait time in ms, before a job gets processed</td>
36
+ </tr>
37
+ <tr>
38
+ <td>Total DHT rehash</td>
39
+ <td><%= @manager.dht_times_rehashed %></td>
40
+ <td>Total number of times the hash table was rehashed</td>
41
+ </tr>
42
+ <tr>
43
+ <td>Avg DHT recalculate time</td>
44
+ <td><%= @manager.get_stats_avg_dht_recalc %></td>
45
+ <td>Average time it takes the system to rehash the DHT</td>
46
+ </tr>
47
+ </tbody>
48
+ </table>
49
+ <h1>Pending Queue Information</h1>
50
+ <p> This queue contains jobs that are to be resharded</p>
51
+ <table>
52
+ <thead>
53
+ <th>Queue Name</th>
54
+ <th>Stored Jobs</th>
55
+ </thead>
56
+ <tbody>
57
+ <tr>
58
+ <td><a class="queue" href="<%= u "queues/#{@manager.pending_queue_name}" %>"><%= @manager.pending_queue_name %></a></td>
59
+ <td><%= @manager.pending_total %></td>
60
+ </tbody>
61
+ </table>
62
+ <h1 class='wi'> <%= @queue_with_slices.size %> worker instances</h1>
63
+ <form method="POST" action="<%=u "/request_update" %>" >
64
+ <input type='submit' name='' value='Request Force Update' />
65
+ </form>
66
+ <table>
67
+ <thead>
68
+ <th>Slice (0 -- <%= 2**32 %>)</th>
69
+ <th>Queue Name</th>
70
+ <th>Worker Host</th>
71
+ <th>Status</th>
72
+ <th>Started</th>
73
+ <th>Heartbeat</th>
74
+ <th>Assigned</th>
75
+ <th>Queued</th>
76
+ </thead>
77
+ <tbody>
78
+ <% @queue_with_slices.each do |slice_info| %>
79
+ <tr>
80
+ <td>#<%= slice_info[0] %></td>
81
+ <td><a class="queue" href="<%= u "queues/#{slice_info[1]}" %>"><%= slice_info[1] %></a></td>
82
+ <td><a href="<%=u "workers/#{slice_info[2]}"%>"><%= slice_info[2] %>:<%= slice_info[3] %></a></td>
83
+ <td><%= slice_info[4] %></td>
84
+ <td><%= slice_info[5] %></td>
85
+ <td><%= slice_info[6] %></td>
86
+ <td><%= slice_info[7] %></td>
87
+ <td><%= slice_info[8] %></td>
88
+ </tr>
89
+ <% end %>
90
+ <tr></tr>
91
+ </tbody>
92
+ </table>
93
+ <% if @orphaned_queues.size > 0 %>
94
+ <h1 class="wi">Orphaned Queues</h1>
95
+ <table>
96
+ <thead>
97
+ <th>Queue Name</th>
98
+ </thead>
99
+ <tbody>
100
+ <% @orphaned_queues.each do |queue| %>
101
+ <tr>
102
+ <td><a class="queue" href="<%= u "queues/#{queue}" %>"><%= queue %></a></td>
103
+ </tr>
104
+ <% end %>
105
+ <tr></tr>
106
+ </tbody>
107
+ </table>
108
+ <% end %>
109
+ <%= poll %>
@@ -0,0 +1,21 @@
1
+ <form method="POST" action="<%=u "/shared_finder" %>" >
2
+ <label for="key">Key</label>
3
+ <input id="key" type='text' name='key'/>
4
+ <input type='submit' name='' value='Find Shard' />
5
+ </form>
6
+ <% if @worker %>
7
+ <table>
8
+ <thead>
9
+ <th>Queue Name</th>
10
+ <th>Host Name</th>
11
+ </thead>
12
+ <tbody>
13
+ <tr>
14
+ <td><%= @queue_anme %></td>
15
+ <td><%= @worker.hostname %></td>
16
+ </tr>
17
+ </tbody>
18
+ </table>
19
+ <% else %>
20
+ No worker found
21
+ <% end %>
@@ -0,0 +1,73 @@
1
+ require 'securerandom'
2
+
3
+ module Resque
4
+ module Plugins
5
+ module Fifo
6
+ class Worker < Resque::Worker
7
+ UPDATE_DELAY = 10
8
+ attr_accessor :main_queue_name
9
+
10
+ def queues=(queues)
11
+ queues = queues.empty? ? (ENV["QUEUES"] || ENV['QUEUE']).to_s.split(',') : queues
12
+ @main_queue_name = "#{manager.queue_prefix}-#{SecureRandom.hex(10)}"
13
+
14
+ @queues = ([:fifo_refresh, main_queue_name] + queues).map { |queue| queue.to_s.strip }
15
+ unless ['*', '?', '{', '}', '[', ']'].any? { |char| @queues.join.include?(char) }
16
+ @static_queues = @queues.flatten.uniq
17
+ end
18
+ validate_queues
19
+ end
20
+
21
+ # Attempts to grab a job off one of the provided queues. Returns
22
+ # nil if no job can be found.
23
+ def reserve
24
+ queues.each do |queue|
25
+ log_with_severity :debug, "Checking #{queue}"
26
+ if job = Resque.reserve(queue)
27
+ log_with_severity :debug, "Found job on #{queue}"
28
+
29
+ if job.payload['enqueue_ts']
30
+ delay_ts = Time.now.to_i - job.payload['enqueue_ts'].to_i
31
+ max_delay = Resque.redis.get("fifo-stats-max-delay") || 0
32
+ Resque.redis.incrby("fifo-stats-accumulated-delay", delay_ts)
33
+ Resque.redis.incr("fifo-stats-accumulated-count")
34
+ if (delay_ts > max_delay.to_i)
35
+ Resque.redis.set("fifo-stats-max-delay", max_delay)
36
+ end
37
+ end
38
+ return job
39
+ end
40
+ end
41
+
42
+ nil
43
+ rescue Exception => e
44
+ log_with_severity :error, "Error reserving job: #{e.inspect}"
45
+ log_with_severity :error, e.backtrace.join("\n")
46
+ raise e
47
+ end
48
+
49
+ # Registers ourself as a worker. Useful when entering the worker
50
+ # lifecycle on startup.
51
+ def register_worker
52
+ super
53
+
54
+ puts "Fifo Startup - Updating worker list"
55
+ manager.request_refresh
56
+ end
57
+
58
+ def unregister_worker(exception = nil)
59
+ super(exception)
60
+
61
+ puts "Fifo Shutdown - Updating worker list"
62
+ manager.request_refresh
63
+ end
64
+
65
+ private
66
+
67
+ def manager
68
+ @manager ||= Resque::Plugins::Fifo::Queue::Manager.new
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,4 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../../lib'
2
+
3
+ require 'resque/fifo/queue'
4
+ require 'resque/fifo/tasks'
@@ -0,0 +1,45 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'resque/plugins/fifo/queue/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "resque-fifo-queue"
8
+ spec.version = Resque::Plugins::Fifo::Queue::VERSION
9
+ spec.authors = ["Joseph Emmanuel Dayo"]
10
+ spec.email = ["joseph.dayo@gmail.com"]
11
+
12
+ spec.summary = %q{Implements a federated fifo queue with consistent hashing on top of resque}
13
+ spec.description = %q{Implements a federated fifo queue with consistent hashing on top of resque}
14
+ spec.homepage = "https://github.com/jedld/resque-fifo-queue"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
21
+ else
22
+ raise "RubyGems 2.0 or newer is required to protect against " \
23
+ "public gem pushes."
24
+ end
25
+
26
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
+ f.match(%r{^(test|spec|features)/})
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ["lib"]
32
+
33
+ spec.add_development_dependency "bundler", "~> 1.14"
34
+ spec.add_development_dependency "rake", "~> 10.0"
35
+ spec.add_development_dependency "rspec", "~> 3.0"
36
+ spec.add_development_dependency "timecop"
37
+ spec.add_development_dependency "pry-byebug"
38
+ spec.add_dependency "redis"
39
+ spec.add_dependency "resque", "~> 1.27"
40
+ spec.add_dependency "resque-scheduler"
41
+ spec.add_dependency "resque-pause"
42
+ spec.add_dependency "resque_solo"
43
+ spec.add_dependency "xxhash"
44
+ spec.add_dependency "redlock"
45
+ end
metadata ADDED
@@ -0,0 +1,237 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: resque-fifo-queue
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Joseph Emmanuel Dayo
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-11-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: timecop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry-byebug
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: redis
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: resque
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.27'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.27'
111
+ - !ruby/object:Gem::Dependency
112
+ name: resque-scheduler
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: resque-pause
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: resque_solo
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: xxhash
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :runtime
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: redlock
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ description: Implements a federated fifo queue with consistent hashing on top of resque
182
+ email:
183
+ - joseph.dayo@gmail.com
184
+ executables: []
185
+ extensions: []
186
+ extra_rdoc_files: []
187
+ files:
188
+ - ".gitignore"
189
+ - ".rspec"
190
+ - ".travis.yml"
191
+ - CODE_OF_CONDUCT.md
192
+ - Gemfile
193
+ - LICENSE.txt
194
+ - README.md
195
+ - Rakefile
196
+ - bin/console
197
+ - bin/setup
198
+ - init.rb
199
+ - lib/resque/fifo/constants.rb
200
+ - lib/resque/fifo/queue.rb
201
+ - lib/resque/fifo/tasks.rb
202
+ - lib/resque/plugins/fifo/extensions.rb
203
+ - lib/resque/plugins/fifo/queue/drain_worker.rb
204
+ - lib/resque/plugins/fifo/queue/manager.rb
205
+ - lib/resque/plugins/fifo/queue/version.rb
206
+ - lib/resque/plugins/fifo/server.rb
207
+ - lib/resque/plugins/fifo/server/views/fifo_queues.erb
208
+ - lib/resque/plugins/fifo/server/views/shared_finder.erb
209
+ - lib/resque/plugins/fifo/worker.rb
210
+ - lib/tasks/resque_fifo.rake
211
+ - resque-fifo-queue.gemspec
212
+ homepage: https://github.com/jedld/resque-fifo-queue
213
+ licenses:
214
+ - MIT
215
+ metadata:
216
+ allowed_push_host: https://rubygems.org
217
+ post_install_message:
218
+ rdoc_options: []
219
+ require_paths:
220
+ - lib
221
+ required_ruby_version: !ruby/object:Gem::Requirement
222
+ requirements:
223
+ - - ">="
224
+ - !ruby/object:Gem::Version
225
+ version: '0'
226
+ required_rubygems_version: !ruby/object:Gem::Requirement
227
+ requirements:
228
+ - - ">="
229
+ - !ruby/object:Gem::Version
230
+ version: '0'
231
+ requirements: []
232
+ rubyforge_project:
233
+ rubygems_version: 2.6.10
234
+ signing_key:
235
+ specification_version: 4
236
+ summary: Implements a federated fifo queue with consistent hashing on top of resque
237
+ test_files: []