qless 0.9.3 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/Gemfile +9 -3
  2. data/README.md +70 -25
  3. data/Rakefile +125 -9
  4. data/exe/install_phantomjs +21 -0
  5. data/lib/qless.rb +115 -76
  6. data/lib/qless/config.rb +11 -9
  7. data/lib/qless/failure_formatter.rb +43 -0
  8. data/lib/qless/job.rb +201 -102
  9. data/lib/qless/job_reservers/ordered.rb +7 -1
  10. data/lib/qless/job_reservers/round_robin.rb +16 -6
  11. data/lib/qless/job_reservers/shuffled_round_robin.rb +9 -2
  12. data/lib/qless/lua/qless-lib.lua +2463 -0
  13. data/lib/qless/lua/qless.lua +2012 -0
  14. data/lib/qless/lua_script.rb +63 -12
  15. data/lib/qless/middleware/memory_usage_monitor.rb +62 -0
  16. data/lib/qless/middleware/metriks.rb +45 -0
  17. data/lib/qless/middleware/redis_reconnect.rb +6 -3
  18. data/lib/qless/middleware/requeue_exceptions.rb +94 -0
  19. data/lib/qless/middleware/retry_exceptions.rb +38 -9
  20. data/lib/qless/middleware/sentry.rb +3 -7
  21. data/lib/qless/middleware/timeout.rb +64 -0
  22. data/lib/qless/queue.rb +90 -55
  23. data/lib/qless/server.rb +177 -130
  24. data/lib/qless/server/views/_job.erb +33 -15
  25. data/lib/qless/server/views/completed.erb +11 -0
  26. data/lib/qless/server/views/layout.erb +70 -11
  27. data/lib/qless/server/views/overview.erb +93 -53
  28. data/lib/qless/server/views/queue.erb +9 -8
  29. data/lib/qless/server/views/queues.erb +18 -1
  30. data/lib/qless/subscriber.rb +37 -22
  31. data/lib/qless/tasks.rb +5 -10
  32. data/lib/qless/test_helpers/worker_helpers.rb +55 -0
  33. data/lib/qless/version.rb +3 -1
  34. data/lib/qless/worker.rb +4 -413
  35. data/lib/qless/worker/base.rb +247 -0
  36. data/lib/qless/worker/forking.rb +245 -0
  37. data/lib/qless/worker/serial.rb +41 -0
  38. metadata +135 -52
  39. data/lib/qless/qless-core/cancel.lua +0 -101
  40. data/lib/qless/qless-core/complete.lua +0 -233
  41. data/lib/qless/qless-core/config.lua +0 -56
  42. data/lib/qless/qless-core/depends.lua +0 -65
  43. data/lib/qless/qless-core/deregister_workers.lua +0 -12
  44. data/lib/qless/qless-core/fail.lua +0 -117
  45. data/lib/qless/qless-core/failed.lua +0 -83
  46. data/lib/qless/qless-core/get.lua +0 -37
  47. data/lib/qless/qless-core/heartbeat.lua +0 -51
  48. data/lib/qless/qless-core/jobs.lua +0 -41
  49. data/lib/qless/qless-core/pause.lua +0 -18
  50. data/lib/qless/qless-core/peek.lua +0 -165
  51. data/lib/qless/qless-core/pop.lua +0 -314
  52. data/lib/qless/qless-core/priority.lua +0 -32
  53. data/lib/qless/qless-core/put.lua +0 -169
  54. data/lib/qless/qless-core/qless-lib.lua +0 -2354
  55. data/lib/qless/qless-core/qless.lua +0 -1862
  56. data/lib/qless/qless-core/queues.lua +0 -58
  57. data/lib/qless/qless-core/recur.lua +0 -190
  58. data/lib/qless/qless-core/retry.lua +0 -73
  59. data/lib/qless/qless-core/stats.lua +0 -92
  60. data/lib/qless/qless-core/tag.lua +0 -100
  61. data/lib/qless/qless-core/track.lua +0 -79
  62. data/lib/qless/qless-core/unfail.lua +0 -54
  63. data/lib/qless/qless-core/unpause.lua +0 -12
  64. data/lib/qless/qless-core/workers.lua +0 -69
  65. data/lib/qless/wait_until.rb +0 -19
@@ -1,43 +1,52 @@
1
- require "qless/job"
2
- require "redis"
3
- require "json"
1
+ # Encoding: utf-8
2
+
3
+ require 'qless/job'
4
+ require 'redis'
5
+ require 'json'
4
6
 
5
7
  module Qless
8
+ # A class for interacting with jobs in different states in a queue. Not meant
9
+ # to be instantiated directly, it's accessed with Queue#jobs
6
10
  class QueueJobs
7
11
  def initialize(name, client)
8
12
  @name = name
9
13
  @client = client
10
14
  end
11
15
 
12
- def running(start=0, count=25)
13
- @client._jobs.call([], ['running', Time.now.to_f, @name, start, count])
16
+ def running(start = 0, count = 25)
17
+ @client.call('jobs', 'running', @name, start, count)
14
18
  end
15
19
 
16
- def stalled(start=0, count=25)
17
- @client._jobs.call([], ['stalled', Time.now.to_f, @name, start, count])
20
+ def stalled(start = 0, count = 25)
21
+ @client.call('jobs', 'stalled', @name, start, count)
18
22
  end
19
23
 
20
- def scheduled(start=0, count=25)
21
- @client._jobs.call([], ['scheduled', Time.now.to_f, @name, start, count])
24
+ def scheduled(start = 0, count = 25)
25
+ @client.call('jobs', 'scheduled', @name, start, count)
22
26
  end
23
27
 
24
- def depends(start=0, count=25)
25
- @client._jobs.call([], ['depends', Time.now.to_f, @name, start, count])
28
+ def depends(start = 0, count = 25)
29
+ @client.call('jobs', 'depends', @name, start, count)
26
30
  end
27
31
 
28
- def recurring(start=0, count=25)
29
- @client._jobs.call([], ['recurring', Time.now.to_f, @name, start, count])
32
+ def recurring(start = 0, count = 25)
33
+ @client.call('jobs', 'recurring', @name, start, count)
30
34
  end
31
35
  end
32
36
 
37
+ # A class for interacting with a specific queue. Not meant to be instantiated
38
+ # directly, it's accessed with Client#queues[...]
33
39
  class Queue
34
40
  attr_reader :name, :client
35
- attr_accessor :worker_name
36
41
 
37
42
  def initialize(name, client)
38
43
  @client = client
39
44
  @name = name
40
- self.worker_name = Qless.worker_name
45
+ end
46
+
47
+ # Our worker name is the same as our client's
48
+ def worker_name
49
+ @client.worker_name
41
50
  end
42
51
 
43
52
  def jobs
@@ -45,7 +54,7 @@ module Qless
45
54
  end
46
55
 
47
56
  def counts
48
- JSON.parse(@client._queues.call([], [Time.now.to_i, @name]))
57
+ JSON.parse(@client.call('queues', @name))
49
58
  end
50
59
 
51
60
  def heartbeat
@@ -57,20 +66,36 @@ module Qless
57
66
  end
58
67
 
59
68
  def max_concurrency
60
- value = get_config(:"max-concurrency")
69
+ value = get_config('max-concurrency')
61
70
  value && Integer(value)
62
71
  end
63
72
 
64
73
  def max_concurrency=(value)
65
- set_config :"max-concurrency", value
74
+ set_config 'max-concurrency', value
75
+ end
76
+
77
+ def paused?
78
+ counts['paused']
66
79
  end
67
80
 
68
- def pause
69
- @client._pause.call([], [name])
81
+ def pause(opts = {})
82
+ @client.call('pause', name)
83
+ @client.call('timeout', jobs.running(0, -1)) unless opts[:stopjobs].nil?
70
84
  end
71
85
 
72
86
  def unpause
73
- @client._unpause.call([], [name])
87
+ @client.call('unpause', name)
88
+ end
89
+
90
+ QueueNotEmptyError = Class.new(StandardError)
91
+
92
+ def forget
93
+ job_count = length
94
+ if job_count.zero?
95
+ @client.call('queue.forget', name)
96
+ else
97
+ raise QueueNotEmptyError, "The queue is not empty. It has #{job_count} jobs."
98
+ end
74
99
  end
75
100
 
76
101
  # Put the described job in this queue
@@ -78,20 +103,18 @@ module Qless
78
103
  # => priority (int)
79
104
  # => tags (array of strings)
80
105
  # => delay (int)
81
- def put(klass, data, opts={})
106
+ def put(klass, data, opts = {})
82
107
  opts = job_options(klass, data, opts)
83
-
84
- @client._put.call([@name], [
85
- (opts[:jid] or Qless.generate_jid),
86
- klass.name,
87
- JSON.generate(data),
88
- Time.now.to_f,
89
- opts.fetch(:delay, 0),
90
- 'priority', opts.fetch(:priority, 0),
91
- 'tags', JSON.generate(opts.fetch(:tags, [])),
92
- 'retries', opts.fetch(:retries, 5),
93
- 'depends', JSON.generate(opts.fetch(:depends, []))
94
- ])
108
+ @client.call('put', worker_name, @name,
109
+ (opts[:jid] || Qless.generate_jid),
110
+ klass.is_a?(String) ? klass : klass.name,
111
+ JSON.generate(data),
112
+ opts.fetch(:delay, 0),
113
+ 'priority', opts.fetch(:priority, 0),
114
+ 'tags', JSON.generate(opts.fetch(:tags, [])),
115
+ 'retries', opts.fetch(:retries, 5),
116
+ 'depends', JSON.generate(opts.fetch(:depends, []))
117
+ )
95
118
  end
96
119
 
97
120
  # Make a recurring job in this queue
@@ -100,52 +123,64 @@ module Qless
100
123
  # => tags (array of strings)
101
124
  # => retries (int)
102
125
  # => offset (int)
103
- def recur(klass, data, interval, opts={})
126
+ def recur(klass, data, interval, opts = {})
104
127
  opts = job_options(klass, data, opts)
105
-
106
- @client._recur.call([], [
107
- 'on',
128
+ @client.call(
129
+ 'recur',
108
130
  @name,
109
- (opts[:jid] or Qless.generate_jid),
110
- klass.to_s,
131
+ (opts[:jid] || Qless.generate_jid),
132
+ klass.is_a?(String) ? klass : klass.name,
111
133
  JSON.generate(data),
112
- Time.now.to_f,
113
134
  'interval', interval, opts.fetch(:offset, 0),
114
135
  'priority', opts.fetch(:priority, 0),
115
136
  'tags', JSON.generate(opts.fetch(:tags, [])),
116
- 'retries', opts.fetch(:retries, 5)
117
- ])
137
+ 'retries', opts.fetch(:retries, 5),
138
+ 'backlog', opts.fetch(:backlog, 0)
139
+ )
118
140
  end
119
141
 
120
142
  # Pop a work item off the queue
121
- def pop(count=nil)
122
- results = @client._pop.call([@name], [worker_name, (count || 1), Time.now.to_f]).map { |j| Job.new(@client, JSON.parse(j)) }
123
- count.nil? ? results[0] : results
143
+ def pop(count = nil)
144
+ jids = JSON.parse(@client.call('pop', @name, worker_name, (count || 1)))
145
+ jobs = jids.map { |j| Job.new(@client, j) }
146
+ count.nil? ? jobs[0] : jobs
124
147
  end
125
148
 
126
149
  # Peek at a work item
127
- def peek(count=nil)
128
- results = @client._peek.call([@name], [(count || 1), Time.now.to_f]).map { |j| Job.new(@client, JSON.parse(j)) }
129
- count.nil? ? results[0] : results
150
+ def peek(count = nil)
151
+ jids = JSON.parse(@client.call('peek', @name, (count || 1)))
152
+ jobs = jids.map { |j| Job.new(@client, j) }
153
+ count.nil? ? jobs[0] : jobs
130
154
  end
131
155
 
132
- def stats(date=nil)
133
- JSON.parse(@client._stats.call([], [@name, (date || Time.now.to_f)]))
156
+ def stats(date = nil)
157
+ JSON.parse(@client.call('stats', @name, (date || Time.now.to_f)))
134
158
  end
135
159
 
136
160
  # How many items in the queue?
137
161
  def length
138
162
  (@client.redis.multi do
139
- @client.redis.zcard("ql:q:#{@name}-locks")
140
- @client.redis.zcard("ql:q:#{@name}-work")
141
- @client.redis.zcard("ql:q:#{@name}-scheduled")
163
+ %w[ locks work scheduled depends ].each do |suffix|
164
+ @client.redis.zcard("ql:q:#{@name}-#{suffix}")
165
+ end
142
166
  end).inject(0, :+)
143
167
  end
144
168
 
145
169
  def to_s
146
170
  "#<Qless::Queue #{@name}>"
147
171
  end
148
- alias inspect to_s
172
+ alias_method :inspect, :to_s
173
+
174
+ def ==(other)
175
+ self.class == other.class &&
176
+ client == other.client &&
177
+ name.to_s == other.name.to_s
178
+ end
179
+ alias eql? ==
180
+
181
+ def hash
182
+ self.class.hash ^ client.hash ^ name.to_s.hash
183
+ end
149
184
 
150
185
  private
151
186
 
@@ -1,9 +1,10 @@
1
+ # Encoding: utf-8
2
+
1
3
  require 'sinatra/base'
2
4
  require 'qless'
3
5
 
4
- # Much of this is shamelessly poached from the resque web client
5
-
6
6
  module Qless
7
+ # The Qless web interface
7
8
  class Server < Sinatra::Base
8
9
  # Path-y-ness
9
10
  dir = File.dirname(File.expand_path(__FILE__))
@@ -27,7 +28,7 @@ module Qless
27
28
  include Rack::Utils
28
29
 
29
30
  def url_path(*path_parts)
30
- [ path_prefix, path_parts ].join("/").squeeze('/')
31
+ [path_prefix, path_parts].join('/').squeeze('/')
31
32
  end
32
33
  alias_method :u, :url_path
33
34
 
@@ -49,11 +50,11 @@ module Qless
49
50
  end
50
51
 
51
52
  def next_page_url
52
- page_url 1
53
+ page_url(1)
53
54
  end
54
55
 
55
56
  def prev_page_url
56
- page_url -1
57
+ page_url(-1)
57
58
  end
58
59
 
59
60
  def current_page
@@ -75,34 +76,35 @@ module Qless
75
76
  end
76
77
 
77
78
  def tabs
78
- return [
79
- {:name => 'Queues' , :path => '/queues' },
80
- {:name => 'Workers' , :path => '/workers' },
81
- {:name => 'Track' , :path => '/track' },
82
- {:name => 'Failed' , :path => '/failed' },
83
- {:name => 'Config' , :path => '/config' },
84
- {:name => 'About' , :path => '/about' }
79
+ [
80
+ { name: 'Queues' , path: '/queues' },
81
+ { name: 'Workers' , path: '/workers' },
82
+ { name: 'Track' , path: '/track' },
83
+ { name: 'Failed' , path: '/failed' },
84
+ { name: 'Completed', path: '/completed'},
85
+ { name: 'Config' , path: '/config' },
86
+ { name: 'About' , path: '/about' }
85
87
  ]
86
88
  end
87
89
 
88
90
  def application_name
89
- return client.config['application']
91
+ client.config['application']
90
92
  end
91
93
 
92
94
  def queues
93
- return client.queues.counts
95
+ client.queues.counts
94
96
  end
95
97
 
96
98
  def tracked
97
- return client.jobs.tracked
99
+ client.jobs.tracked
98
100
  end
99
101
 
100
102
  def workers
101
- return client.workers.counts
103
+ client.workers.counts
102
104
  end
103
105
 
104
106
  def failed
105
- return client.jobs.failed
107
+ client.jobs.failed
106
108
  end
107
109
 
108
110
  # Return the supplied object back as JSON
@@ -113,45 +115,46 @@ module Qless
113
115
 
114
116
  # Make the id acceptable as an id / att in HTML
115
117
  def sanitize_attr(attr)
116
- return attr.gsub(/[^a-zA-Z\:\_]/, '-')
118
+ attr.gsub(/[^a-zA-Z\:\_]/, '-')
117
119
  end
118
120
 
119
121
  # What are the top tags? Since it might go on, say, every
120
122
  # page, then we should probably be caching it
121
123
  def top_tags
122
124
  @top_tags ||= {
123
- :top => client.tags,
124
- :fetched => Time.now
125
+ top: client.tags,
126
+ fetched: Time.now
125
127
  }
126
- if (Time.now - @top_tags[:fetched]) > 60 then
128
+ if (Time.now - @top_tags[:fetched]) > 60
127
129
  @top_tags = {
128
- :top => client.tags,
129
- :fetched => Time.now
130
+ top: client.tags,
131
+ fetched: Time.now
130
132
  }
131
133
  end
132
134
  @top_tags[:top]
133
135
  end
134
136
 
135
137
  def strftime(t)
136
- # From http://stackoverflow.com/questions/195740/how-do-you-do-relative-time-in-rails
138
+ # From http://stackoverflow.com/questions/195740
137
139
  diff_seconds = Time.now - t
140
+ formatted = t.strftime('%b %e, %Y %H:%M:%S')
138
141
  case diff_seconds
139
- when 0 .. 59
140
- "#{diff_seconds.to_i} seconds ago"
141
- when 60 ... 3600
142
- "#{(diff_seconds/60).to_i} minutes ago"
143
- when 3600 ... 3600*24
144
- "#{(diff_seconds/3600).to_i} hours ago"
145
- when (3600*24) ... (3600*24*30)
146
- "#{(diff_seconds/(3600*24)).to_i} days ago"
147
- else
148
- t.strftime('%b %e, %Y %H:%M:%S %Z (%z)')
142
+ when 0 .. 59
143
+ "#{formatted} (#{diff_seconds.to_i} seconds ago)"
144
+ when 60 ... 3600
145
+ "#{formatted} (#{(diff_seconds / 60).to_i} minutes ago)"
146
+ when 3600 ... 3600 * 24
147
+ "#{formatted} (#{(diff_seconds / 3600).to_i} hours ago)"
148
+ when (3600 * 24) ... (3600 * 24 * 30)
149
+ "#{formatted} (#{(diff_seconds / (3600 * 24)).to_i} days ago)"
150
+ else
151
+ formatted
149
152
  end
150
153
  end
151
154
  end
152
155
 
153
156
  get '/?' do
154
- erb :overview, :layout => true, :locals => { :title => "Overview" }
157
+ erb :overview, layout: true, locals: { title: 'Overview' }
155
158
  end
156
159
 
157
160
  # Returns a JSON blob with the job counts for various queues
@@ -160,8 +163,8 @@ module Qless
160
163
  end
161
164
 
162
165
  get '/queues/?' do
163
- erb :queues, :layout => true, :locals => {
164
- :title => 'Queues'
166
+ erb :queues, layout: true, locals: {
167
+ title: 'Queues'
165
168
  }
166
169
  end
167
170
 
@@ -175,20 +178,19 @@ module Qless
175
178
  queue = client.queues[params[:name]]
176
179
  tab = params.fetch('tab', 'stats')
177
180
 
178
- jobs = if tab == 'waiting'
179
- queue.peek(20)
181
+ jobs = []
182
+ if tab == 'waiting'
183
+ jobs = queue.peek(20)
180
184
  elsif filtered_tabs.include?(tab)
181
- paginated(queue.jobs, tab).map { |jid| client.jobs[jid] }
182
- else
183
- []
185
+ jobs = paginated(queue.jobs, tab).map { |jid| client.jobs[jid] }
184
186
  end
185
187
 
186
- erb :queue, :layout => true, :locals => {
187
- :title => "Queue #{params[:name]}",
188
- :tab => tab,
189
- :jobs => jobs,
190
- :queue => client.queues[params[:name]].counts,
191
- :stats => queue.stats
188
+ erb :queue, layout: true, locals: {
189
+ title: "Queue #{params[:name]}",
190
+ tab: tab,
191
+ jobs: jobs,
192
+ queue: client.queues[params[:name]].counts,
193
+ stats: queue.stats
192
194
  }
193
195
  end
194
196
 
@@ -200,106 +202,117 @@ module Qless
200
202
  # qless-core doesn't provide functionality this way, so we'll
201
203
  # do it ourselves. I'm not sure if this is how the core library
202
204
  # should behave or not.
203
- erb :failed, :layout => true, :locals => {
204
- :title => 'Failed',
205
- :failed => client.jobs.failed.keys.map { |t| client.jobs.failed(t).tap { |f| f['type'] = t } }
205
+ erb :failed, layout: true, locals: {
206
+ title: 'Failed',
207
+ failed: client.jobs.failed.keys.map do |t|
208
+ client.jobs.failed(t).tap { |f| f['type'] = t }
209
+ end
206
210
  }
207
211
  end
208
212
 
209
213
  get '/failed/:type/?' do
210
- erb :failed_type, :layout => true, :locals => {
211
- :title => 'Failed | ' + params[:type],
212
- :type => params[:type],
213
- :failed => paginated(client.jobs, :failed, params[:type])
214
+ erb :failed_type, layout: true, locals: {
215
+ title: 'Failed | ' + params[:type],
216
+ type: params[:type],
217
+ failed: paginated(client.jobs, :failed, params[:type])
218
+ }
219
+ end
220
+
221
+ get '/completed/?' do
222
+ completed = paginated(client.jobs, :complete)
223
+ erb :completed, layout: true, locals: {
224
+ title: 'Completed',
225
+ jobs: completed.map { |jid| client.jobs[jid] }
214
226
  }
215
227
  end
216
228
 
217
229
  get '/track/?' do
218
- erb :track, :layout => true, :locals => {
219
- :title => 'Track'
230
+ erb :track, layout: true, locals: {
231
+ title: 'Track'
220
232
  }
221
233
  end
222
234
 
223
235
  get '/jobs/:jid' do
224
- erb :job, :layout => true, :locals => {
225
- :title => "Job | #{params[:jid]}",
226
- :jid => params[:jid],
227
- :job => client.jobs[params[:jid]]
236
+ erb :job, layout: true, locals: {
237
+ title: "Job | #{params[:jid]}",
238
+ jid: params[:jid],
239
+ job: client.jobs[params[:jid]]
228
240
  }
229
241
  end
230
242
 
231
243
  get '/workers/?' do
232
- erb :workers, :layout => true, :locals => {
233
- :title => 'Workers'
244
+ erb :workers, layout: true, locals: {
245
+ title: 'Workers'
234
246
  }
235
247
  end
236
248
 
237
249
  get '/workers/:worker' do
238
- erb :worker, :layout => true, :locals => {
239
- :title => 'Worker | ' + params[:worker],
240
- :worker => client.workers[params[:worker]].tap { |w|
250
+ erb :worker, layout: true, locals: {
251
+ title: 'Worker | ' + params[:worker],
252
+ worker: client.workers[params[:worker]].tap do |w|
241
253
  w['jobs'] = w['jobs'].map { |j| client.jobs[j] }
242
254
  w['stalled'] = w['stalled'].map { |j| client.jobs[j] }
243
255
  w['name'] = params[:worker]
244
- }
256
+ end
245
257
  }
246
258
  end
247
259
 
248
260
  get '/tag/?' do
249
261
  jobs = paginated(client.jobs, :tagged, params[:tag])
250
- erb :tag, :layout => true, :locals => {
251
- :title => "Tag | #{params[:tag]}",
252
- :tag => params[:tag],
253
- :jobs => jobs['jobs'].map { |jid| client.jobs[jid] },
254
- :total => jobs['total']
262
+ erb :tag, layout: true, locals: {
263
+ title: "Tag | #{params[:tag]}",
264
+ tag: params[:tag],
265
+ jobs: jobs['jobs'].map { |jid| client.jobs[jid] },
266
+ total: jobs['total']
255
267
  }
256
268
  end
257
269
 
258
270
  get '/config/?' do
259
- erb :config, :layout => true, :locals => {
260
- :title => 'Config',
261
- :options => client.config.all
271
+ erb :config, layout: true, locals: {
272
+ title: 'Config',
273
+ options: client.config.all
262
274
  }
263
275
  end
264
276
 
265
277
  get '/about/?' do
266
- erb :about, :layout => true, :locals => {
267
- :title => 'About'
278
+ erb :about, layout: true, locals: {
279
+ title: 'About'
268
280
  }
269
281
  end
270
282
 
271
283
  # These are the bits where we accept AJAX requests
272
- post "/track/?" do
284
+ post '/track/?' do
273
285
  # Expects a JSON-encoded hash with a job id, and optionally some tags
274
286
  data = JSON.parse(request.body.read)
275
- job = client.jobs[data["id"]]
276
- if not job.nil?
277
- data.fetch("tags", false) ? job.track(*data["tags"]) : job.track()
287
+ job = client.jobs[data['id']]
288
+ if !job.nil?
289
+ data.fetch('tags', false) ? job.track(*data['tags']) : job.track
278
290
  if request.xhr?
279
- json({ :tracked => [job.jid] })
291
+ json({ tracked: [job.jid] })
280
292
  else
281
293
  redirect to('/track')
282
294
  end
283
295
  else
284
296
  if request.xhr?
285
- json({ :tracked => [] })
297
+ json({ tracked: [] })
286
298
  else
287
299
  redirect to(request.referrer)
288
300
  end
289
301
  end
290
302
  end
291
303
 
292
- post "/untrack/?" do
304
+ post '/untrack/?' do
293
305
  # Expects a JSON-encoded array of job ids to stop tracking
294
- jobs = JSON.parse(request.body.read).map { |jid| client.jobs[jid] }.select { |j| not j.nil? }
306
+ jobs = JSON.parse(request.body.read).map { |jid| client.jobs[jid] }
307
+ jobs.compact!
295
308
  # Go ahead and cancel all the jobs!
296
309
  jobs.each do |job|
297
- job.untrack()
310
+ job.untrack
298
311
  end
299
- return json({ :untracked => jobs.map { |job| job.jid } })
312
+ return json({ untracked: jobs.map { |job| job.jid } })
300
313
  end
301
314
 
302
- post "/priority/?" do
315
+ post '/priority/?' do
303
316
  # Expects a JSON-encoded dictionary of jid => priority
304
317
  response = Hash.new
305
318
  r = JSON.parse(request.body.read)
@@ -314,7 +327,40 @@ module Qless
314
327
  return json(response)
315
328
  end
316
329
 
317
- post "/tag/?" do
330
+ post '/pause/?' do
331
+ # Expects JSON blob: {'queue': <queue>}
332
+ r = JSON.parse(request.body.read)
333
+ if r['queue']
334
+ @client.queues[r['queue']].pause
335
+ return json({ queue: 'paused' })
336
+ else
337
+ raise 'No queue provided'
338
+ end
339
+ end
340
+
341
+ post '/unpause/?' do
342
+ # Expects JSON blob: {'queue': <queue>}
343
+ r = JSON.parse(request.body.read)
344
+ if r['queue']
345
+ @client.queues[r['queue']].unpause
346
+ return json({ queue: 'unpaused' })
347
+ else
348
+ raise 'No queue provided'
349
+ end
350
+ end
351
+
352
+ post '/timeout/?' do
353
+ # Expects JSON blob: {'jid': <jid>}
354
+ r = JSON.parse(request.body.read)
355
+ if r['jid']
356
+ @client.jobs[r['jid']].timeout
357
+ return json({ jid: r['jid'] })
358
+ else
359
+ raise 'No jid provided'
360
+ end
361
+ end
362
+
363
+ post '/tag/?' do
318
364
  # Expects a JSON-encoded dictionary of jid => [tag, tag, tag]
319
365
  response = Hash.new
320
366
  JSON.parse(request.body.read).each_pair do |jid, tags|
@@ -328,7 +374,7 @@ module Qless
328
374
  return json(response)
329
375
  end
330
376
 
331
- post "/untag/?" do
377
+ post '/untag/?' do
332
378
  # Expects a JSON-encoded dictionary of jid => [tag, tag, tag]
333
379
  response = Hash.new
334
380
  JSON.parse(request.body.read).each_pair do |jid, tags|
@@ -342,99 +388,100 @@ module Qless
342
388
  return json(response)
343
389
  end
344
390
 
345
- post "/move/?" do
391
+ post '/move/?' do
346
392
  # Expects a JSON-encoded hash of id: jid, and queue: queue_name
347
393
  data = JSON.parse(request.body.read)
348
- if data["id"].nil? or data["queue"].nil?
349
- halt 400, "Need id and queue arguments"
394
+ if data['id'].nil? || data['queue'].nil?
395
+ halt 400, 'Need id and queue arguments'
350
396
  else
351
- job = client.jobs[data["id"]]
397
+ job = client.jobs[data['id']]
352
398
  if job.nil?
353
- halt 404, "Could not find job"
399
+ halt 404, 'Could not find job'
354
400
  else
355
- job.move(data["queue"])
356
- return json({ :id => data["id"], :queue => data["queue"]})
401
+ job.requeue(data['queue'])
402
+ return json({ id: data['id'], queue: data['queue'] })
357
403
  end
358
404
  end
359
405
  end
360
406
 
361
- post "/undepend/?" do
407
+ post '/undepend/?' do
362
408
  # Expects a JSON-encoded hash of id: jid, and queue: queue_name
363
409
  data = JSON.parse(request.body.read)
364
- if data["id"].nil?
365
- halt 400, "Need id"
410
+ if data['id'].nil?
411
+ halt 400, 'Need id'
366
412
  else
367
- job = client.jobs[data["id"]]
413
+ job = client.jobs[data['id']]
368
414
  if job.nil?
369
- halt 404, "Could not find job"
415
+ halt 404, 'Could not find job'
370
416
  else
371
417
  job.undepend(data['dependency'])
372
- return json({:id => data["id"]})
418
+ return json({ id: data['id'] })
373
419
  end
374
420
  end
375
421
  end
376
422
 
377
- post "/retry/?" do
423
+ post '/retry/?' do
378
424
  # Expects a JSON-encoded hash of id: jid, and queue: queue_name
379
425
  data = JSON.parse(request.body.read)
380
- if data["id"].nil?
381
- halt 400, "Need id"
426
+ if data['id'].nil?
427
+ halt 400, 'Need id'
382
428
  else
383
- job = client.jobs[data["id"]]
429
+ job = client.jobs[data['id']]
384
430
  if job.nil?
385
- halt 404, "Could not find job"
431
+ halt 404, 'Could not find job'
386
432
  else
387
- queue = job.raw_queue_history[-1]["q"]
388
- job.move(queue)
389
- return json({ :id => data["id"], :queue => queue})
433
+ job.requeue(job.queue_name)
434
+ return json({ id: data['id'], queue: job.queue_name })
390
435
  end
391
436
  end
392
437
  end
393
438
 
394
439
  # Retry all the failures of a particular type
395
- post "/retryall/?" do
440
+ post '/retryall/?' do
396
441
  # Expects a JSON-encoded hash of type: failure-type
397
442
  data = JSON.parse(request.body.read)
398
- if data["type"].nil?
399
- halt 400, "Neet type"
443
+ if data['type'].nil?
444
+ halt 400, 'Neet type'
400
445
  else
401
- return json(client.jobs.failed(data["type"], 0, 500)['jobs'].map do |job|
402
- queue = job.raw_queue_history[-1]["q"]
403
- job.move(queue)
404
- { :id => job.jid, :queue => queue}
405
- end)
446
+ jobs = client.jobs.failed(data['type'], 0, 500)['jobs']
447
+ results = jobs.map do |job|
448
+ job.requeue(job.queue_name)
449
+ { id: job.jid, queue: job.queue_name }
450
+ end
451
+ return json(results)
406
452
  end
407
453
  end
408
454
 
409
- post "/cancel/?" do
455
+ post '/cancel/?' do
410
456
  # Expects a JSON-encoded array of job ids to cancel
411
- jobs = JSON.parse(request.body.read).map { |jid| client.jobs[jid] }.select { |j| not j.nil? }
457
+ jobs = JSON.parse(request.body.read).map { |jid| client.jobs[jid] }
458
+ jobs.compact!
412
459
  # Go ahead and cancel all the jobs!
413
460
  jobs.each do |job|
414
- job.cancel()
461
+ job.cancel
415
462
  end
416
463
 
417
464
  if request.xhr?
418
- return json({ :canceled => jobs.map { |job| job.jid } })
465
+ return json({ canceled: jobs.map { |job| job.jid } })
419
466
  else
420
467
  redirect to(request.referrer)
421
468
  end
422
469
  end
423
470
 
424
- post "/cancelall/?" do
471
+ post '/cancelall/?' do
425
472
  # Expects a JSON-encoded hash of type: failure-type
426
473
  data = JSON.parse(request.body.read)
427
- if data["type"].nil?
428
- halt 400, "Neet type"
474
+ if data['type'].nil?
475
+ halt 400, 'Neet type'
429
476
  else
430
- return json(client.jobs.failed(data["type"])['jobs'].map do |job|
431
- job.cancel()
432
- { :id => job.jid }
477
+ return json(client.jobs.failed(data['type'])['jobs'].map do |job|
478
+ job.cancel
479
+ { id: job.jid }
433
480
  end)
434
481
  end
435
482
  end
436
483
 
437
484
  # start the server if ruby file executed directly
438
- run! if app_file == $0
485
+ run! if app_file == $PROGRAM_NAME
439
486
  end
440
487
  end