resque 1.19.0 → 1.20.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of resque might be problematic. Click here for more details.

data/HISTORY.md CHANGED
@@ -1,3 +1,25 @@
1
+ ## 1.20.0 (2012-02-17)
2
+
3
+ * Fixed demos for ruby 1.9 (@BMorearty, #445)
4
+ * Fixed `#requeue` tests (@hone, #500)
5
+ * Web UI: optional trailing slashes of URLs (@elisehuard, #449)
6
+ * Allow * to appear anywhere in queue list (@tapajos, #405, #407)
7
+ * Wait for child with specific PID (@jacobkg)
8
+ * #decode raise takes a string when re-raising as a different exception class (Trevor Hart)
9
+ * Use Sinatra's `pubilc_folder` if it exists (@defunkt, #420, #421)
10
+ * Assign the job's worker before calling `before_fork` (@quirkey)
11
+ * Fix Resque::Helpers#constantize to work correctly on 1.9.2 (@rtlong)
12
+ * Added before & after hooks for dequeue (@humancopy, #398)
13
+ * daemonize support using `ENV["BACKGROUND"]` (@chrisleishman)
14
+ * requeue and remove failed jobs by queue name (@evanwhalen)
15
+ * `-r` flag for resque-web for redis connection (@gjastrab)
16
+ * Added `Resque.enqueue_to`: allows you to specif the queue and still run hooks (@dan-g)
17
+ * Web UI: Set the default encoding to UTF-8 (@elubow)
18
+ * fix finding worker pids on JRuby (John Andrews + Andrew Grieser)
19
+ * Added distributed redis support (@stipple)
20
+ * Added better failure hooks (@raykrueger)
21
+ * Added before & after dequeue hooks (@humancopy)
22
+
1
23
  ## 1.19.0 (2011-09-01)
2
24
 
3
25
  * Added Airbrake (formerly Hoptoad) support.
@@ -210,7 +210,7 @@ loop do
210
210
  if job = reserve
211
211
  job.process
212
212
  else
213
- sleep 5
213
+ sleep 5 # Polling frequency = 5
214
214
  end
215
215
  end
216
216
  shutdown
@@ -265,6 +265,22 @@ worker process. Use the PIDFILE option for easy access to the PID:
265
265
 
266
266
  $ PIDFILE=./resque.pid QUEUE=file_serve rake environment resque:work
267
267
 
268
+ ### Running in the background
269
+
270
+ (Only supported with ruby >= 1.9). There are scenarios where it's helpful for
271
+ the resque worker to run itself in the background (usually in combination with
272
+ PIDFILE). Use the BACKGROUND option so that rake will return as soon as the
273
+ worker is started.
274
+
275
+ $ PIDFILE=./resque.pid BACKGROUND=yes QUEUE=file_serve \
276
+ rake environment resque:work
277
+
278
+ ### Polling frequency
279
+
280
+ You can pass an INTERVAL option which is a float representing the polling frequency.
281
+ The default is 5 seconds, but for a semi-active app you may want to use a smaller value.
282
+
283
+ $ INTERVAL=0.1 QUEUE=file_serve rake environment resque:work
268
284
 
269
285
  ### Priorities and Queue Lists
270
286
 
@@ -445,6 +461,10 @@ You can also set the namespace directly using `resque-web`:
445
461
 
446
462
  $ resque-web -p 8282 -N myapp
447
463
 
464
+ or set the Redis connection string if you need to do something like select a different database:
465
+
466
+ $ resque-web -p 8282 -r localhost:6379:2
467
+
448
468
  ### Passenger
449
469
 
450
470
  Using Passenger? Resque ships with a `config.ru` you can use. See
@@ -471,7 +491,7 @@ HTTP basic auth).
471
491
 
472
492
  ### Rails 3
473
493
 
474
- You can also easily mount Resque on a subpath in your existing Rails 3 app by adding this to your `routes.rb`:
494
+ You can also mount Resque on a subpath in your existing Rails 3 app by adding `require 'resque/server'` to the top of your routes file or in an initializer then adding this to `routes.rb`:
475
495
 
476
496
  ``` ruby
477
497
  mount Resque::Server.new, :at => "/resque"
@@ -840,6 +860,8 @@ sort it out.
840
860
  Contributing
841
861
  ------------
842
862
 
863
+ Read the [Contributing][cb] wiki page first.
864
+
843
865
  Once you've made your great commits:
844
866
 
845
867
  1. [Fork][1] Resque
@@ -848,9 +870,6 @@ Once you've made your great commits:
848
870
  4. Create a [Pull Request](http://help.github.com/pull-requests/) from your branch
849
871
  5. That's it!
850
872
 
851
- You might want to checkout our [Contributing][cb] wiki page for information
852
- on coding standards, new features, etc.
853
-
854
873
 
855
874
  Mailing List
856
875
  ------------
@@ -20,4 +20,8 @@ Vegas::Runner.new(Resque::Server, 'resque-web', {
20
20
  runner.logger.info "Using Redis namespace '#{namespace}'"
21
21
  Resque.redis.namespace = namespace
22
22
  }
23
+ opts.on('-r redis-connection', "--redis redis-connection", "set the Redis connection string") {|redis_conf|
24
+ runner.logger.info "Using Redis connection '#{redis_conf}'"
25
+ Resque.redis = redis_conf
26
+ }
23
27
  end
@@ -222,19 +222,37 @@ module Resque
222
222
  #
223
223
  # If no queue can be inferred this method will raise a `Resque::NoQueueError`
224
224
  #
225
+ # Returns true if the job was queued, nil if the job was rejected by a
226
+ # before_enqueue hook.
227
+ #
225
228
  # This method is considered part of the `stable` API.
226
229
  def enqueue(klass, *args)
230
+ enqueue_to(queue_from_class(klass), klass, *args)
231
+ end
232
+
233
+ # Just like `enqueue` but allows you to specify the queue you want to
234
+ # use. Runs hooks.
235
+ #
236
+ # `queue` should be the String name of the queue you're targeting.
237
+ #
238
+ # Returns true if the job was queued, nil if the job was rejected by a
239
+ # before_enqueue hook.
240
+ #
241
+ # This method is considered part of the `stable` API.
242
+ def enqueue_to(queue, klass, *args)
227
243
  # Perform before_enqueue hooks. Don't perform enqueue if any hook returns false
228
244
  before_hooks = Plugin.before_enqueue_hooks(klass).collect do |hook|
229
245
  klass.send(hook, *args)
230
246
  end
231
- return if before_hooks.any? { |result| result == false }
247
+ return nil if before_hooks.any? { |result| result == false }
232
248
 
233
- Job.create(queue_from_class(klass), klass, *args)
249
+ Job.create(queue, klass, *args)
234
250
 
235
251
  Plugin.after_enqueue_hooks(klass).each do |hook|
236
252
  klass.send(hook, *args)
237
253
  end
254
+
255
+ return true
238
256
  end
239
257
 
240
258
  # This method can be used to conveniently remove a job from a queue.
@@ -265,7 +283,17 @@ module Resque
265
283
  #
266
284
  # This method is considered part of the `stable` API.
267
285
  def dequeue(klass, *args)
286
+ # Perform before_dequeue hooks. Don't perform dequeue if any hook returns false
287
+ before_hooks = Plugin.before_dequeue_hooks(klass).collect do |hook|
288
+ klass.send(hook, *args)
289
+ end
290
+ return if before_hooks.any? { |result| result == false }
291
+
268
292
  Job.destroy(queue_from_class(klass), klass, *args)
293
+
294
+ Plugin.after_dequeue_hooks(klass).each do |hook|
295
+ klass.send(hook, *args)
296
+ end
269
297
  end
270
298
 
271
299
  # Given a class, try to extrapolate an appropriate queue based on a
@@ -66,5 +66,31 @@ module Resque
66
66
  def self.remove(index)
67
67
  backend.remove(index)
68
68
  end
69
+
70
+ # Requeues all failed jobs in a specific queue.
71
+ # Queue name should be a string.
72
+ def self.requeue_queue(queue)
73
+ i=0
74
+ while job = Resque::Failure.all(i)
75
+ if job['queue'] == queue
76
+ Resque::Failure.requeue(i)
77
+ end
78
+ i+=1
79
+ end
80
+ end
81
+
82
+ # Removes all failed jobs in a specific queue.
83
+ # Queue name should be a string.
84
+ def self.remove_queue(queue)
85
+ i=0
86
+ while job = Resque::Failure.all(i)
87
+ if job['queue'] == queue
88
+ # This will remove the failure from the array so do not increment the index.
89
+ Resque::Failure.remove(i)
90
+ else
91
+ i+=1
92
+ end
93
+ end
94
+ end
69
95
  end
70
96
  end
@@ -29,7 +29,7 @@ module Resque
29
29
  begin
30
30
  ::MultiJson.decode(object)
31
31
  rescue ::MultiJson::DecodeError => e
32
- raise DecodeException, e
32
+ raise DecodeException, e.message, e.backtrace
33
33
  end
34
34
  end
35
35
 
@@ -40,9 +40,23 @@ module Resque
40
40
  dashed_word.split('-').each { |part| part[0] = part[0].chr.upcase }.join
41
41
  end
42
42
 
43
- # Given a camel cased word, returns the constant it represents
43
+ # Tries to find a constant with the name specified in the argument string:
44
44
  #
45
- # constantize('JobName') # => JobName
45
+ # constantize("Module") # => Module
46
+ # constantize("Test::Unit") # => Test::Unit
47
+ #
48
+ # The name is assumed to be the one of a top-level constant, no matter
49
+ # whether it starts with "::" or not. No lexical context is taken into
50
+ # account:
51
+ #
52
+ # C = 'outside'
53
+ # module M
54
+ # C = 'inside'
55
+ # C # => 'inside'
56
+ # constantize("C") # => 'outside', same as ::C
57
+ # end
58
+ #
59
+ # NameError is raised when the constant is unknown.
46
60
  def constantize(camel_cased_word)
47
61
  camel_cased_word = camel_cased_word.to_s
48
62
 
@@ -55,7 +69,13 @@ module Resque
55
69
 
56
70
  constant = Object
57
71
  names.each do |name|
58
- constant = constant.const_get(name) || constant.const_missing(name)
72
+ args = Module.method(:const_get).arity != 1 ? [false] : []
73
+
74
+ if constant.const_defined?(name, *args)
75
+ constant = constant.const_get(name)
76
+ else
77
+ constant = constant.const_missing(name)
78
+ end
59
79
  end
60
80
  constant
61
81
  end
@@ -106,11 +106,6 @@ module Resque
106
106
  job_args = args || []
107
107
  job_was_performed = false
108
108
 
109
- before_hooks = Plugin.before_hooks(job)
110
- around_hooks = Plugin.around_hooks(job)
111
- after_hooks = Plugin.after_hooks(job)
112
- failure_hooks = Plugin.failure_hooks(job)
113
-
114
109
  begin
115
110
  # Execute before_perform hook. Abort the job gracefully if
116
111
  # Resque::DontPerform is raised.
@@ -158,7 +153,7 @@ module Resque
158
153
  # If an exception occurs during the job execution, look for an
159
154
  # on_failure hook then re-raise.
160
155
  rescue Object => e
161
- failure_hooks.each { |hook| job.send(hook, e, *job_args) }
156
+ run_failure_hooks(e)
162
157
  raise e
163
158
  end
164
159
  end
@@ -176,6 +171,7 @@ module Resque
176
171
  # Given an exception object, hands off the needed parameters to
177
172
  # the Failure module.
178
173
  def fail(exception)
174
+ run_failure_hooks(exception)
179
175
  Failure.create \
180
176
  :payload => payload,
181
177
  :exception => exception,
@@ -201,5 +197,27 @@ module Resque
201
197
  payload_class == other.payload_class &&
202
198
  args == other.args
203
199
  end
200
+
201
+ def before_hooks
202
+ @before_hooks ||= Plugin.before_hooks(payload_class)
203
+ end
204
+
205
+ def around_hooks
206
+ @around_hooks ||= Plugin.around_hooks(payload_class)
207
+ end
208
+
209
+ def after_hooks
210
+ @after_hooks ||= Plugin.after_hooks(payload_class)
211
+ end
212
+
213
+ def failure_hooks
214
+ @failure_hooks ||= Plugin.failure_hooks(payload_class)
215
+ end
216
+
217
+ def run_failure_hooks(exception)
218
+ job_args = args || []
219
+ failure_hooks.each { |hook| payload_class.send(hook, exception, *job_args) }
220
+ end
221
+
204
222
  end
205
223
  end
@@ -52,5 +52,15 @@ module Resque
52
52
  def before_enqueue_hooks(job)
53
53
  job.methods.grep(/^before_enqueue/).sort
54
54
  end
55
+
56
+ # Given an object, returns a list `after_dequeue` hook names.
57
+ def after_dequeue_hooks(job)
58
+ job.methods.grep(/^after_dequeue/).sort
59
+ end
60
+
61
+ # Given an object, returns a list `before_dequeue` hook names.
62
+ def before_dequeue_hooks(job)
63
+ job.methods.grep(/^before_dequeue/).sort
64
+ end
55
65
  end
56
66
  end
@@ -4,12 +4,22 @@ require 'resque'
4
4
  require 'resque/version'
5
5
  require 'time'
6
6
 
7
+ if defined? Encoding
8
+ Encoding.default_external = Encoding::UTF_8
9
+ end
10
+
7
11
  module Resque
8
12
  class Server < Sinatra::Base
9
13
  dir = File.dirname(File.expand_path(__FILE__))
10
14
 
11
15
  set :views, "#{dir}/server/views"
12
- set :public, "#{dir}/server/public"
16
+
17
+ if respond_to? :public_folder
18
+ set :public_folder, "#{dir}/server/public"
19
+ else
20
+ set :public, "#{dir}/server/public"
21
+ end
22
+
13
23
  set :static, true
14
24
 
15
25
  helpers do
@@ -139,21 +149,21 @@ module Resque
139
149
  end
140
150
 
141
151
  %w( overview workers ).each do |page|
142
- get "/#{page}.poll" do
152
+ get "/#{page}.poll/?" do
143
153
  show_for_polling(page)
144
154
  end
145
155
 
146
- get "/#{page}/:id.poll" do
156
+ get "/#{page}/:id.poll/?" do
147
157
  show_for_polling(page)
148
158
  end
149
159
  end
150
160
 
151
161
  %w( overview queues working workers key ).each do |page|
152
- get "/#{page}" do
162
+ get "/#{page}/?" do
153
163
  show page
154
164
  end
155
165
 
156
- get "/#{page}/:id" do
166
+ get "/#{page}/:id/?" do
157
167
  show page
158
168
  end
159
169
  end
@@ -163,7 +173,7 @@ module Resque
163
173
  redirect u('queues')
164
174
  end
165
175
 
166
- get "/failed" do
176
+ get "/failed/?" do
167
177
  if Resque::Failure.url
168
178
  redirect Resque::Failure.url
169
179
  else
@@ -183,7 +193,7 @@ module Resque
183
193
  redirect u('failed')
184
194
  end
185
195
 
186
- get "/failed/requeue/:index" do
196
+ get "/failed/requeue/:index/?" do
187
197
  Resque::Failure.requeue(params[:index])
188
198
  if request.xhr?
189
199
  return Resque::Failure.all(params[:index])['retried_at']
@@ -192,24 +202,24 @@ module Resque
192
202
  end
193
203
  end
194
204
 
195
- get "/failed/remove/:index" do
205
+ get "/failed/remove/:index/?" do
196
206
  Resque::Failure.remove(params[:index])
197
207
  redirect u('failed')
198
208
  end
199
209
 
200
- get "/stats" do
210
+ get "/stats/?" do
201
211
  redirect url_path("/stats/resque")
202
212
  end
203
213
 
204
- get "/stats/:id" do
214
+ get "/stats/:id/?" do
205
215
  show :stats
206
216
  end
207
217
 
208
- get "/stats/keys/:key" do
218
+ get "/stats/keys/:key/?" do
209
219
  show :stats
210
220
  end
211
221
 
212
- get "/stats.txt" do
222
+ get "/stats.txt/?" do
213
223
  info = Resque.info
214
224
 
215
225
  stats = []
@@ -41,6 +41,7 @@ body { padding:0; margin:0; }
41
41
  #main table.jobs td.args{ width:50%;}
42
42
 
43
43
  #main table.workers td.icon {width:1%; background:#efefef;text-align:center;}
44
+ #main table.workers td.icon img { height: 16px; width: 16px; }
44
45
  #main table.workers td.where { width:25%;}
45
46
  #main table.workers td.queues { width:35%;}
46
47
  #main .queue-tag { background:#b1d2e9; padding:2px; margin:0 3px; font-size:80%; text-decoration:none; text-transform:uppercase; font-weight:bold; color:#3274a2; -webkit-border-radius:4px; -moz-border-radius:4px;}
@@ -18,6 +18,13 @@ namespace :resque do
18
18
  abort "set QUEUE env var, e.g. $ QUEUE=critical,high rake resque:work"
19
19
  end
20
20
 
21
+ if ENV['BACKGROUND']
22
+ unless Process.respond_to?('daemon')
23
+ abort "env var BACKGROUND is set, which requires ruby >= 1.9"
24
+ end
25
+ Process.daemon(true)
26
+ end
27
+
21
28
  if ENV['PIDFILE']
22
29
  File.open(ENV['PIDFILE'], 'w') { |f| f << worker.pid }
23
30
  end
@@ -1,3 +1,3 @@
1
1
  module Resque
2
- Version = VERSION = '1.19.0'
2
+ Version = VERSION = '1.20.0'
3
3
  end
@@ -35,9 +35,19 @@ module Resque
35
35
 
36
36
  names.map! { |name| "worker:#{name}" }
37
37
 
38
- reportedly_working = redis.mapped_mget(*names).reject do |key, value|
39
- value.nil? || value.empty?
38
+ reportedly_working = {}
39
+
40
+ begin
41
+ reportedly_working = redis.mapped_mget(*names).reject do |key, value|
42
+ value.nil? || value.empty?
43
+ end
44
+ rescue Redis::Distributed::CannotDistribute
45
+ names.each do |name|
46
+ value = redis.get name
47
+ reportedly_working[name] = value unless value.nil? || value.empty?
48
+ end
40
49
  end
50
+
41
51
  reportedly_working.keys.map do |key|
42
52
  find key.sub("worker:", '')
43
53
  end.compact
@@ -118,13 +128,14 @@ module Resque
118
128
 
119
129
  if not paused? and job = reserve
120
130
  log "got: #{job.inspect}"
131
+ job.worker = self
121
132
  run_hook :before_fork, job
122
133
  working_on job
123
134
 
124
135
  if @child = fork
125
136
  srand # Reseeding
126
137
  procline "Forked #{@child} at #{Time.now.to_i}"
127
- Process.wait
138
+ Process.wait(@child)
128
139
  else
129
140
  procline "Processing #{job.queue} since #{Time.now.to_i}"
130
141
  perform(job, &block)
@@ -150,6 +161,7 @@ module Resque
150
161
  def process(job = nil, &block)
151
162
  return unless job ||= reserve
152
163
 
164
+ job.worker = self
153
165
  working_on job
154
166
  perform(job, &block)
155
167
  ensure
@@ -181,7 +193,7 @@ module Resque
181
193
  def reserve
182
194
  queues.each do |queue|
183
195
  log! "Checking #{queue}"
184
- if job = Resque::Job.reserve(queue)
196
+ if job = Resque.reserve(queue)
185
197
  log! "Found job on #{queue}"
186
198
  return job
187
199
  end
@@ -198,7 +210,7 @@ module Resque
198
210
  # A splat ("*") means you want every queue (in alpha order) - this
199
211
  # can be useful for dynamically adding new queues.
200
212
  def queues
201
- @queues[0] == "*" ? Resque.queues.sort : @queues
213
+ @queues.map {|queue| queue == "*" ? Resque.queues.sort : queue }.flatten.uniq
202
214
  end
203
215
 
204
216
  # Not every platform supports fork. Here we do our magic to
@@ -378,7 +390,6 @@ module Resque
378
390
  # Given a job, tells Redis we're working on it. Useful for seeing
379
391
  # what workers are doing and when.
380
392
  def working_on(job)
381
- job.worker = self
382
393
  data = encode \
383
394
  :queue => job.queue,
384
395
  :run_at => Time.now.strftime("%Y/%m/%d %H:%M:%S %Z"),
@@ -470,7 +481,7 @@ module Resque
470
481
 
471
482
  # Returns Integer PID of running worker
472
483
  def pid
473
- @pid ||= to_s.split(":")[1].to_i
484
+ Process.pid
474
485
  end
475
486
 
476
487
  # Returns an Array of string pids of all the other workers on this
@@ -488,7 +499,7 @@ module Resque
488
499
  # Returns an Array of string pids of all the other workers on this
489
500
  # machine. Useful when pruning dead workers on startup.
490
501
  def linux_worker_pids
491
- `ps -A -o pid,command | grep [r]esque | grep -v "resque-web"`.split("\n").map do |line|
502
+ `ps -A -o pid,command | grep "[r]esque" | grep -v "resque-web"`.split("\n").map do |line|
492
503
  line.split(' ')[0]
493
504
  end
494
505
  end
@@ -498,7 +509,7 @@ module Resque
498
509
  # Returns an Array of string pids of all the other workers on this
499
510
  # machine. Useful when pruning dead workers on startup.
500
511
  def solaris_worker_pids
501
- `ps -A -o pid,comm | grep [r]uby | grep -v "resque-web"`.split("\n").map do |line|
512
+ `ps -A -o pid,comm | grep "[r]uby" | grep -v "resque-web"`.split("\n").map do |line|
502
513
  real_pid = line.split(' ')[0]
503
514
  pargs_command = `pargs -a #{real_pid} 2>/dev/null | grep [r]esque | grep -v "resque-web"`
504
515
  if pargs_command.split(':')[1] == " resque-#{Resque::Version}"
@@ -277,7 +277,7 @@ context "Resque::Job before_enqueue" do
277
277
  test "the before enqueue hook should run" do
278
278
  history = []
279
279
  @worker = Resque::Worker.new(:jobs)
280
- Resque.enqueue(BeforeEnqueueJob, history)
280
+ assert Resque.enqueue(BeforeEnqueueJob, history)
281
281
  @worker.work(0)
282
282
  assert_equal history, [:before_enqueue], "before_enqueue was not run"
283
283
  end
@@ -285,11 +285,71 @@ context "Resque::Job before_enqueue" do
285
285
  test "a before enqueue hook that returns false should prevent the job from getting queued" do
286
286
  history = []
287
287
  @worker = Resque::Worker.new(:jobs)
288
- Resque.enqueue(BeforeEnqueueJobAbort, history)
288
+ assert_nil Resque.enqueue(BeforeEnqueueJobAbort, history)
289
289
  assert_equal 0, Resque.size(:jobs)
290
290
  end
291
291
  end
292
292
 
293
+ context "Resque::Job after_dequeue" do
294
+ include PerformJob
295
+
296
+ class ::AfterDequeueJob
297
+ @queue = :jobs
298
+ def self.after_dequeue_record_history(history)
299
+ history << :after_dequeue
300
+ end
301
+
302
+ def self.perform(history)
303
+ end
304
+ end
305
+
306
+ test "the after dequeue hook should run" do
307
+ history = []
308
+ @worker = Resque::Worker.new(:jobs)
309
+ Resque.dequeue(AfterDequeueJob, history)
310
+ @worker.work(0)
311
+ assert_equal history, [:after_dequeue], "after_dequeue was not run"
312
+ end
313
+ end
314
+
315
+
316
+ context "Resque::Job before_dequeue" do
317
+ include PerformJob
318
+
319
+ class ::BeforeDequeueJob
320
+ @queue = :jobs
321
+ def self.before_dequeue_record_history(history)
322
+ history << :before_dequeue
323
+ end
324
+
325
+ def self.perform(history)
326
+ end
327
+ end
328
+
329
+ class ::BeforeDequeueJobAbort
330
+ @queue = :jobs
331
+ def self.before_dequeue_abort(history)
332
+ false
333
+ end
334
+
335
+ def self.perform(history)
336
+ end
337
+ end
338
+
339
+ test "the before dequeue hook should run" do
340
+ history = []
341
+ @worker = Resque::Worker.new(:jobs)
342
+ Resque.dequeue(BeforeDequeueJob, history)
343
+ @worker.work(0)
344
+ assert_equal history, [:before_dequeue], "before_dequeue was not run"
345
+ end
346
+
347
+ test "a before dequeue hook that returns false should prevent the job from getting dequeued" do
348
+ history = []
349
+ assert_equal nil, Resque.dequeue(BeforeDequeueJobAbort, history)
350
+ end
351
+ end
352
+
293
353
  context "Resque::Job all hooks" do
294
354
  include PerformJob
295
355
 
@@ -0,0 +1,115 @@
1
+ # Redis configuration file example
2
+
3
+ # By default Redis does not run as a daemon. Use 'yes' if you need it.
4
+ # Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
5
+ daemonize yes
6
+
7
+ # When run as a daemon, Redis write a pid file in /var/run/redis.pid by default.
8
+ # You can specify a custom pid file location here.
9
+ pidfile ./test/redis-test-cluster.pid
10
+
11
+ # Accept connections on the specified port, default is 6379
12
+ port 9737
13
+
14
+ # If you want you can bind a single interface, if the bind option is not
15
+ # specified all the interfaces will listen for connections.
16
+ #
17
+ # bind 127.0.0.1
18
+
19
+ # Close the connection after a client is idle for N seconds (0 to disable)
20
+ timeout 300
21
+
22
+ # Save the DB on disk:
23
+ #
24
+ # save <seconds> <changes>
25
+ #
26
+ # Will save the DB if both the given number of seconds and the given
27
+ # number of write operations against the DB occurred.
28
+ #
29
+ # In the example below the behaviour will be to save:
30
+ # after 900 sec (15 min) if at least 1 key changed
31
+ # after 300 sec (5 min) if at least 10 keys changed
32
+ # after 60 sec if at least 10000 keys changed
33
+ save 900 1
34
+ save 300 10
35
+ save 60 10000
36
+
37
+ # The filename where to dump the DB
38
+ dbfilename dump-cluster.rdb
39
+
40
+ # For default save/load DB in/from the working directory
41
+ # Note that you must specify a directory not a file name.
42
+ dir ./test/
43
+
44
+ # Set server verbosity to 'debug'
45
+ # it can be one of:
46
+ # debug (a lot of information, useful for development/testing)
47
+ # notice (moderately verbose, what you want in production probably)
48
+ # warning (only very important / critical messages are logged)
49
+ loglevel debug
50
+
51
+ # Specify the log file name. Also 'stdout' can be used to force
52
+ # the demon to log on the standard output. Note that if you use standard
53
+ # output for logging but daemonize, logs will be sent to /dev/null
54
+ logfile stdout
55
+
56
+ # Set the number of databases. The default database is DB 0, you can select
57
+ # a different one on a per-connection basis using SELECT <dbid> where
58
+ # dbid is a number between 0 and 'databases'-1
59
+ databases 16
60
+
61
+ ################################# REPLICATION #################################
62
+
63
+ # Master-Slave replication. Use slaveof to make a Redis instance a copy of
64
+ # another Redis server. Note that the configuration is local to the slave
65
+ # so for example it is possible to configure the slave to save the DB with a
66
+ # different interval, or to listen to another port, and so on.
67
+
68
+ # slaveof <masterip> <masterport>
69
+
70
+ ################################## SECURITY ###################################
71
+
72
+ # Require clients to issue AUTH <PASSWORD> before processing any other
73
+ # commands. This might be useful in environments in which you do not trust
74
+ # others with access to the host running redis-server.
75
+ #
76
+ # This should stay commented out for backward compatibility and because most
77
+ # people do not need auth (e.g. they run their own servers).
78
+
79
+ # requirepass foobared
80
+
81
+ ################################### LIMITS ####################################
82
+
83
+ # Set the max number of connected clients at the same time. By default there
84
+ # is no limit, and it's up to the number of file descriptors the Redis process
85
+ # is able to open. The special value '0' means no limts.
86
+ # Once the limit is reached Redis will close all the new connections sending
87
+ # an error 'max number of clients reached'.
88
+
89
+ # maxclients 128
90
+
91
+ # Don't use more memory than the specified amount of bytes.
92
+ # When the memory limit is reached Redis will try to remove keys with an
93
+ # EXPIRE set. It will try to start freeing keys that are going to expire
94
+ # in little time and preserve keys with a longer time to live.
95
+ # Redis will also try to remove objects from free lists if possible.
96
+ #
97
+ # If all this fails, Redis will start to reply with errors to commands
98
+ # that will use more memory, like SET, LPUSH, and so on, and will continue
99
+ # to reply to most read-only commands like GET.
100
+ #
101
+ # WARNING: maxmemory can be a good idea mainly if you want to use Redis as a
102
+ # 'state' server or cache, not as a real DB. When Redis is used as a real
103
+ # database the memory usage will grow over the weeks, it will be obvious if
104
+ # it is going to use too much memory in the long run, and you'll have the time
105
+ # to upgrade. With maxmemory after the limit is reached you'll start to get
106
+ # errors for write operations, and this may even lead to DB inconsistency.
107
+
108
+ # maxmemory <bytes>
109
+
110
+ ############################### ADVANCED CONFIG ###############################
111
+
112
+ # Glue small output buffers together in order to send small replies in a
113
+ # single TCP packet. Uses a bit more CPU but most of the times it is a win
114
+ # in terms of number of queries per second. Use 'yes' if unsure.
115
+ glueoutputbuf yes
@@ -51,3 +51,9 @@ context "on GET to /stats/resque" do
51
51
 
52
52
  should_respond_with_success
53
53
  end
54
+
55
+ context "also works with slash at the end" do
56
+ setup { get "/working/" }
57
+
58
+ should_respond_with_success
59
+ end
@@ -7,6 +7,11 @@ context "Resque" do
7
7
  Resque.push(:people, { 'name' => 'chris' })
8
8
  Resque.push(:people, { 'name' => 'bob' })
9
9
  Resque.push(:people, { 'name' => 'mark' })
10
+ @original_redis = Resque.redis
11
+ end
12
+
13
+ teardown do
14
+ Resque.redis = @original_redis
10
15
  end
11
16
 
12
17
  test "can set a namespace through a url-like string" do
@@ -131,6 +136,16 @@ context "Resque" do
131
136
  assert_equal nil, Resque.reserve(:method)
132
137
  end
133
138
 
139
+ test "can define a queue for jobs by way of a method" do
140
+ assert_equal 0, Resque.size(:method)
141
+ assert Resque.enqueue_to(:new_queue, SomeMethodJob, 20, '/tmp')
142
+
143
+ job = Resque.reserve(:new_queue)
144
+ assert_equal SomeMethodJob, job.payload_class
145
+ assert_equal 20, job.args[0]
146
+ assert_equal '/tmp', job.args[1]
147
+ end
148
+
134
149
  test "needs to infer a queue with enqueue" do
135
150
  assert_raises Resque::NoQueueError do
136
151
  Resque.enqueue(SomeJob, 20, '/tmp')
@@ -201,7 +216,7 @@ context "Resque" do
201
216
  end
202
217
 
203
218
  test "keeps track of resque keys" do
204
- assert_equal ["queue:people", "queues"], Resque.keys
219
+ assert_equal ["queue:people", "queues"].sort, Resque.keys.sort
205
220
  end
206
221
 
207
222
  test "badly wants a class name, too" do
@@ -238,7 +253,11 @@ context "Resque" do
238
253
  assert_equal 3, stats[:queues]
239
254
  assert_equal 3, stats[:processed]
240
255
  assert_equal 1, stats[:failed]
241
- assert_equal [Resque.redis.respond_to?(:server) ? 'localhost:9736' : 'redis://localhost:9736/0'], stats[:servers]
256
+ if ENV.key? 'RESQUE_DISTRIBUTED'
257
+ assert_equal [Resque.redis.respond_to?(:server) ? 'localhost:9736, localhost:9737' : 'redis://localhost:9736/0, redis://localhost:9737/0'], stats[:servers]
258
+ else
259
+ assert_equal [Resque.redis.respond_to?(:server) ? 'localhost:9736' : 'redis://localhost:9736/0'], stats[:servers]
260
+ end
242
261
  end
243
262
 
244
263
  test "decode bad json" do
@@ -39,16 +39,26 @@ at_exit do
39
39
  exit_code = Test::Unit::AutoRunner.run
40
40
  end
41
41
 
42
- pid = `ps -A -o pid,command | grep [r]edis-test`.split(" ")[0]
42
+ processes = `ps -A -o pid,command | grep [r]edis-test`.split("\n")
43
+ pids = processes.map { |process| process.split(" ")[0] }
43
44
  puts "Killing test redis server..."
44
- `rm -f #{dir}/dump.rdb`
45
- Process.kill("KILL", pid.to_i)
45
+ `rm -f #{dir}/dump.rdb #{dir}/dump-cluster.rdb`
46
+ pids.each { |pid| Process.kill("KILL", pid.to_i) }
46
47
  exit exit_code
47
48
  end
48
49
 
49
- puts "Starting redis for testing at localhost:9736..."
50
- `redis-server #{dir}/redis-test.conf`
51
- Resque.redis = 'localhost:9736'
50
+ if ENV.key? 'RESQUE_DISTRIBUTED'
51
+ require 'redis/distributed'
52
+ puts "Starting redis for testing at localhost:9736 and localhost:9737..."
53
+ `redis-server #{dir}/redis-test.conf`
54
+ `redis-server #{dir}/redis-test-cluster.conf`
55
+ r = Redis::Distributed.new(['redis://localhost:9736', 'redis://localhost:9737'])
56
+ Resque.redis = Redis::Namespace.new :resque, :redis => r
57
+ else
58
+ puts "Starting redis for testing at localhost:9736..."
59
+ `redis-server #{dir}/redis-test.conf`
60
+ Resque.redis = 'localhost:9736'
61
+ end
52
62
 
53
63
 
54
64
  ##
@@ -33,12 +33,30 @@ context "Resque::Worker" do
33
33
  end
34
34
 
35
35
  test "fails uncompleted jobs on exit" do
36
- job = Resque::Job.new(:jobs, [GoodJob, "blah"])
36
+ job = Resque::Job.new(:jobs, {'class' => 'GoodJob', 'args' => "blah"})
37
37
  @worker.working_on(job)
38
38
  @worker.unregister_worker
39
39
  assert_equal 1, Resque::Failure.count
40
40
  end
41
41
 
42
+ class ::SimpleJobWithFailureHandling
43
+ def self.on_failure_record_failure(exception, *job_args)
44
+ @@exception = exception
45
+ end
46
+
47
+ def self.exception
48
+ @@exception
49
+ end
50
+ end
51
+
52
+ test "fails uncompleted jobs on exit, and calls failure hook" do
53
+ job = Resque::Job.new(:jobs, {'class' => 'SimpleJobWithFailureHandling', 'args' => ""})
54
+ @worker.working_on(job)
55
+ @worker.unregister_worker
56
+ assert_equal 1, Resque::Failure.count
57
+ assert(SimpleJobWithFailureHandling.exception.kind_of?(Resque::DirtyExit))
58
+ end
59
+
42
60
  test "can peek at failed jobs" do
43
61
  10.times { Resque::Job.create(:jobs, BadJob) }
44
62
  @worker.work(0)
@@ -97,6 +115,36 @@ context "Resque::Worker" do
97
115
  assert_equal 0, Resque.size(:blahblah)
98
116
  end
99
117
 
118
+ test "can work with wildcard at the end of the list" do
119
+ Resque::Job.create(:high, GoodJob)
120
+ Resque::Job.create(:critical, GoodJob)
121
+ Resque::Job.create(:blahblah, GoodJob)
122
+ Resque::Job.create(:beer, GoodJob)
123
+
124
+ worker = Resque::Worker.new(:critical, :high, "*")
125
+
126
+ worker.work(0)
127
+ assert_equal 0, Resque.size(:high)
128
+ assert_equal 0, Resque.size(:critical)
129
+ assert_equal 0, Resque.size(:blahblah)
130
+ assert_equal 0, Resque.size(:beer)
131
+ end
132
+
133
+ test "can work with wildcard at the middle of the list" do
134
+ Resque::Job.create(:high, GoodJob)
135
+ Resque::Job.create(:critical, GoodJob)
136
+ Resque::Job.create(:blahblah, GoodJob)
137
+ Resque::Job.create(:beer, GoodJob)
138
+
139
+ worker = Resque::Worker.new(:critical, "*", :high)
140
+
141
+ worker.work(0)
142
+ assert_equal 0, Resque.size(:high)
143
+ assert_equal 0, Resque.size(:critical)
144
+ assert_equal 0, Resque.size(:blahblah)
145
+ assert_equal 0, Resque.size(:beer)
146
+ end
147
+
100
148
  test "processes * queues in alphabetical order" do
101
149
  Resque::Job.create(:high, GoodJob)
102
150
  Resque::Job.create(:critical, GoodJob)
@@ -267,6 +315,11 @@ context "Resque::Worker" do
267
315
  end
268
316
  end
269
317
 
318
+ test "worker_pids returns pids" do
319
+ known_workers = @worker.worker_pids
320
+ assert !known_workers.empty?
321
+ end
322
+
270
323
  test "Processed jobs count" do
271
324
  @worker.work(0)
272
325
  assert_equal 1, Resque.info[:processed]
@@ -329,4 +382,24 @@ context "Resque::Worker" do
329
382
  test "returns PID of running process" do
330
383
  assert_equal @worker.to_s.split(":")[1].to_i, @worker.pid
331
384
  end
385
+
386
+ test "requeue failed queue" do
387
+ queue = 'good_job'
388
+ Resque::Failure.create(:exception => Exception.new, :worker => Resque::Worker.new(queue), :queue => queue, :payload => {'class' => 'GoodJob'})
389
+ Resque::Failure.create(:exception => Exception.new, :worker => Resque::Worker.new(queue), :queue => 'some_job', :payload => {'class' => 'SomeJob'})
390
+ Resque::Failure.requeue_queue(queue)
391
+ assert Resque::Failure.all(0).has_key?('retried_at')
392
+ assert !Resque::Failure.all(1).has_key?('retried_at')
393
+ end
394
+
395
+ test "remove failed queue" do
396
+ queue = 'good_job'
397
+ queue2 = 'some_job'
398
+ Resque::Failure.create(:exception => Exception.new, :worker => Resque::Worker.new(queue), :queue => queue, :payload => {'class' => 'GoodJob'})
399
+ Resque::Failure.create(:exception => Exception.new, :worker => Resque::Worker.new(queue2), :queue => queue2, :payload => {'class' => 'SomeJob'})
400
+ Resque::Failure.create(:exception => Exception.new, :worker => Resque::Worker.new(queue), :queue => queue, :payload => {'class' => 'GoodJob'})
401
+ Resque::Failure.remove_queue(queue)
402
+ assert_equal queue2, Resque::Failure.all(0)['queue']
403
+ assert_equal 1, Resque::Failure.count
404
+ end
332
405
  end
metadata CHANGED
@@ -1,21 +1,22 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resque
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
4
+ hash: 71
5
+ prerelease:
5
6
  segments:
6
7
  - 1
7
- - 19
8
+ - 20
8
9
  - 0
9
- version: 1.19.0
10
+ version: 1.20.0
10
11
  platform: ruby
11
12
  authors:
12
13
  - Chris Wanstrath
14
+ - Terence Lee
13
15
  autorequire:
14
16
  bindir: bin
15
17
  cert_chain: []
16
18
 
17
- date: 2011-09-02 00:00:00 -07:00
18
- default_executable:
19
+ date: 2012-02-17 00:00:00 Z
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
22
  name: redis-namespace
@@ -25,6 +26,7 @@ dependencies:
25
26
  requirements:
26
27
  - - ~>
27
28
  - !ruby/object:Gem::Version
29
+ hash: 19
28
30
  segments:
29
31
  - 1
30
32
  - 0
@@ -40,6 +42,7 @@ dependencies:
40
42
  requirements:
41
43
  - - ~>
42
44
  - !ruby/object:Gem::Version
45
+ hash: 31
43
46
  segments:
44
47
  - 0
45
48
  - 1
@@ -55,6 +58,7 @@ dependencies:
55
58
  requirements:
56
59
  - - ">="
57
60
  - !ruby/object:Gem::Version
61
+ hash: 63
58
62
  segments:
59
63
  - 0
60
64
  - 9
@@ -70,6 +74,7 @@ dependencies:
70
74
  requirements:
71
75
  - - ~>
72
76
  - !ruby/object:Gem::Version
77
+ hash: 15
73
78
  segments:
74
79
  - 1
75
80
  - 0
@@ -91,59 +96,59 @@ files:
91
96
  - Rakefile
92
97
  - LICENSE
93
98
  - HISTORY.md
94
- - lib/resque/errors.rb
95
- - lib/resque/failure/airbrake.rb
96
- - lib/resque/failure/base.rb
97
- - lib/resque/failure/hoptoad.rb
98
- - lib/resque/failure/multiple.rb
99
- - lib/resque/failure/redis.rb
100
- - lib/resque/failure/thoughtbot.rb
101
- - lib/resque/failure.rb
102
- - lib/resque/helpers.rb
99
+ - lib/tasks/redis.rake
100
+ - lib/tasks/resque.rake
103
101
  - lib/resque/job.rb
104
- - lib/resque/plugin.rb
102
+ - lib/resque/server/views/workers.erb
103
+ - lib/resque/server/views/next_more.erb
104
+ - lib/resque/server/views/key_string.erb
105
+ - lib/resque/server/views/error.erb
106
+ - lib/resque/server/views/layout.erb
107
+ - lib/resque/server/views/overview.erb
108
+ - lib/resque/server/views/stats.erb
109
+ - lib/resque/server/views/failed.erb
110
+ - lib/resque/server/views/key_sets.erb
111
+ - lib/resque/server/views/working.erb
112
+ - lib/resque/server/views/queues.erb
113
+ - lib/resque/server/public/jquery-1.3.2.min.js
114
+ - lib/resque/server/public/working.png
105
115
  - lib/resque/server/public/favicon.ico
106
116
  - lib/resque/server/public/idle.png
107
- - lib/resque/server/public/jquery-1.3.2.min.js
108
- - lib/resque/server/public/jquery.relatize_date.js
109
- - lib/resque/server/public/poll.png
110
117
  - lib/resque/server/public/ranger.js
111
118
  - lib/resque/server/public/reset.css
112
119
  - lib/resque/server/public/style.css
113
- - lib/resque/server/public/working.png
120
+ - lib/resque/server/public/jquery.relatize_date.js
121
+ - lib/resque/server/public/poll.png
114
122
  - lib/resque/server/test_helper.rb
115
- - lib/resque/server/views/error.erb
116
- - lib/resque/server/views/failed.erb
117
- - lib/resque/server/views/key_sets.erb
118
- - lib/resque/server/views/key_string.erb
119
- - lib/resque/server/views/layout.erb
120
- - lib/resque/server/views/next_more.erb
121
- - lib/resque/server/views/overview.erb
122
- - lib/resque/server/views/queues.erb
123
- - lib/resque/server/views/stats.erb
124
- - lib/resque/server/views/workers.erb
125
- - lib/resque/server/views/working.erb
126
- - lib/resque/server.rb
127
- - lib/resque/stat.rb
128
- - lib/resque/tasks.rb
129
123
  - lib/resque/version.rb
124
+ - lib/resque/failure.rb
130
125
  - lib/resque/worker.rb
126
+ - lib/resque/errors.rb
127
+ - lib/resque/plugin.rb
128
+ - lib/resque/server.rb
129
+ - lib/resque/helpers.rb
130
+ - lib/resque/tasks.rb
131
+ - lib/resque/stat.rb
132
+ - lib/resque/failure/hoptoad.rb
133
+ - lib/resque/failure/thoughtbot.rb
134
+ - lib/resque/failure/multiple.rb
135
+ - lib/resque/failure/redis.rb
136
+ - lib/resque/failure/base.rb
137
+ - lib/resque/failure/airbrake.rb
131
138
  - lib/resque.rb
132
- - lib/tasks/redis.rake
133
- - lib/tasks/resque.rake
134
- - bin/resque
135
139
  - bin/resque-web
140
+ - bin/resque
136
141
  - test/airbrake_test.rb
137
- - test/hoptoad_test.rb
138
142
  - test/job_hooks_test.rb
139
- - test/job_plugins_test.rb
140
143
  - test/plugin_test.rb
141
- - test/redis-test.conf
144
+ - test/redis-test-cluster.conf
142
145
  - test/resque-web_test.rb
143
- - test/resque_test.rb
144
- - test/test_helper.rb
146
+ - test/redis-test.conf
145
147
  - test/worker_test.rb
146
- has_rdoc: true
148
+ - test/test_helper.rb
149
+ - test/hoptoad_test.rb
150
+ - test/resque_test.rb
151
+ - test/job_plugins_test.rb
147
152
  homepage: http://github.com/defunkt/resque
148
153
  licenses: []
149
154
 
@@ -157,6 +162,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
157
162
  requirements:
158
163
  - - ">="
159
164
  - !ruby/object:Gem::Version
165
+ hash: 3
160
166
  segments:
161
167
  - 0
162
168
  version: "0"
@@ -165,13 +171,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
165
171
  requirements:
166
172
  - - ">="
167
173
  - !ruby/object:Gem::Version
174
+ hash: 3
168
175
  segments:
169
176
  - 0
170
177
  version: "0"
171
178
  requirements: []
172
179
 
173
180
  rubyforge_project:
174
- rubygems_version: 1.3.7
181
+ rubygems_version: 1.8.11
175
182
  signing_key:
176
183
  specification_version: 3
177
184
  summary: Resque is a Redis-backed queueing system.