resque 1.22.0 → 1.23.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,10 @@
1
+ ## 1.23.0 (2012-10-01)
2
+
3
+ * don't run `before_fork` hook if Resque can't fork (@kjwierenga, @tarcieri, #672, #697)
4
+ * don't run `after_fork` hook if Resque can't fork (@kjwierenga, @tarcieri, #672, #697)
5
+ * retry connecting to redis up to 3 times (@trevorturk, #693)
6
+ * pass exceptions raised by the worker into the Failure backend (@trevorturk, #693)
7
+
1
8
  ## 1.22.0 (2012-08-21)
2
9
 
3
10
  * unregister signal handlers in child process when ENV["TERM_CHILD"] is set (@dylanasmith, #621)
@@ -1,3 +1,3 @@
1
1
  module Resque
2
- Version = VERSION = '1.22.0'
2
+ Version = VERSION = '1.23.0'
3
3
  end
@@ -136,10 +136,9 @@ module Resque
136
136
  if not paused? and job = reserve
137
137
  log "got: #{job.inspect}"
138
138
  job.worker = self
139
- run_hook :before_fork, job
140
139
  working_on job
141
140
 
142
- if @child = fork
141
+ if @child = fork(job)
143
142
  srand # Reseeding
144
143
  procline "Forked #{@child} at #{Time.now.to_i}"
145
144
  begin
@@ -148,11 +147,11 @@ module Resque
148
147
  nil
149
148
  end
150
149
  else
151
- unregister_signal_handlers if !@cant_fork && term_child
150
+ unregister_signal_handlers if will_fork? && term_child
152
151
  procline "Processing #{job.queue} since #{Time.now.to_i}"
153
- redis.client.reconnect # Don't share connection with parent
152
+ reconnect
154
153
  perform(job, &block)
155
- exit! unless @cant_fork
154
+ exit! if will_fork?
156
155
  end
157
156
 
158
157
  done_working
@@ -165,8 +164,9 @@ module Resque
165
164
  end
166
165
  end
167
166
 
168
- ensure
169
167
  unregister_worker
168
+ rescue Exception => exception
169
+ unregister_worker(exception)
170
170
  end
171
171
 
172
172
  # DEPRECATED. Processes a single job. If none is given, it will
@@ -184,7 +184,7 @@ module Resque
184
184
  # Processes a given job in the child.
185
185
  def perform(job)
186
186
  begin
187
- run_hook :after_fork, job
187
+ run_hook :after_fork, job if will_fork?
188
188
  job.perform
189
189
  rescue Object => e
190
190
  log "#{job.inspect} failed: #{e.inspect}"
@@ -219,6 +219,24 @@ module Resque
219
219
  raise e
220
220
  end
221
221
 
222
+ # Reconnect to Redis to avoid sharing a connection with the parent,
223
+ # retry up to 3 times with increasing delay before giving up.
224
+ def reconnect
225
+ tries = 0
226
+ begin
227
+ redis.client.reconnect
228
+ rescue Redis::BaseConnectionError
229
+ if (tries += 1) <= 3
230
+ log "Error reconnecting to Redis; retrying"
231
+ sleep(tries)
232
+ retry
233
+ else
234
+ log "Error reconnecting to Redis; quitting"
235
+ raise
236
+ end
237
+ end
238
+ end
239
+
222
240
  # Returns a list of queues to use when searching for a job.
223
241
  # A splat ("*") means you want every queue (in alpha order) - this
224
242
  # can be useful for dynamically adding new queues.
@@ -228,15 +246,17 @@ module Resque
228
246
 
229
247
  # Not every platform supports fork. Here we do our magic to
230
248
  # determine if yours does.
231
- def fork
232
- @cant_fork = true if $TESTING
233
-
249
+ def fork(job)
234
250
  return if @cant_fork
251
+
252
+ # Only run before_fork hooks if we're actually going to fork
253
+ # (after checking @cant_fork)
254
+ run_hook :before_fork, job
235
255
 
236
256
  begin
237
257
  # IronRuby doesn't support `Kernel.fork` yet
238
258
  if Kernel.respond_to?(:fork)
239
- Kernel.fork
259
+ Kernel.fork if will_fork?
240
260
  else
241
261
  raise NotImplementedError
242
262
  end
@@ -424,7 +444,7 @@ module Resque
424
444
  end
425
445
 
426
446
  # Unregisters ourself as a worker. Useful when shutting down.
427
- def unregister_worker
447
+ def unregister_worker(exception = nil)
428
448
  # If we're still processing a job, make sure it gets logged as a
429
449
  # failure.
430
450
  if (hash = processing) && !hash.empty?
@@ -432,7 +452,7 @@ module Resque
432
452
  # Ensure the proper worker is attached to this job, even if
433
453
  # it's not the precise instance that died.
434
454
  job.worker = self
435
- job.fail(DirtyExit.new)
455
+ job.fail(exception || DirtyExit.new)
436
456
  end
437
457
 
438
458
  redis.srem(:workers, self)
@@ -507,6 +527,10 @@ module Resque
507
527
  def idle?
508
528
  state == :idle
509
529
  end
530
+
531
+ def will_fork?
532
+ !(@cant_fork || $TESTING)
533
+ end
510
534
 
511
535
  # Returns a symbol representing the current worker state,
512
536
  # which can be either :working or :idle
@@ -13,10 +13,6 @@ context "Resque::Failure::Redis" do
13
13
 
14
14
  test 'cleans up bad strings before saving the failure, in order to prevent errors on the resque UI' do
15
15
  # test assumption: the bad string should not be able to round trip though JSON
16
- assert_raises(MultiJson::DecodeError) {
17
- MultiJson.decode(MultiJson.encode(@bad_string))
18
- }
19
-
20
16
  @redis_backend.save
21
17
  Resque::Failure::Redis.all # should not raise an error
22
18
  end
@@ -161,14 +161,18 @@ class Time
161
161
  self.fake_time = nil
162
162
  end
163
163
 
164
- def capture_stderr
165
- # The output stream must be an IO-like object. In this case we capture it in
166
- # an in-memory IO object so we can return the string value. You can assign any
167
- # IO object here.
168
- previous_stderr, $stderr = $stderr, StringIO.new
164
+ # From minitest/unit
165
+ def capture_io
166
+ require 'stringio'
167
+
168
+ orig_stdout, orig_stderr = $stdout, $stderr
169
+ captured_stdout, captured_stderr = StringIO.new, StringIO.new
170
+ $stdout, $stderr = captured_stdout, captured_stderr
171
+
169
172
  yield
170
- $stderr.string
173
+
174
+ return captured_stdout.string, captured_stderr.string
171
175
  ensure
172
- # Restore the previous value of stderr (typically equal to STDERR).
173
- $stderr = previous_stderr
176
+ $stdout = orig_stdout
177
+ $stderr = orig_stderr
174
178
  end
@@ -32,11 +32,20 @@ context "Resque::Worker" do
32
32
  end
33
33
  end
34
34
 
35
- test "fails uncompleted jobs on exit" do
35
+ test "fails uncompleted jobs with DirtyExit by default on exit" do
36
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
+ assert_equal('Resque::DirtyExit', Resque::Failure.all['exception'])
41
+ end
42
+
43
+ test "fails uncompleted jobs with worker exception on exit" do
44
+ job = Resque::Job.new(:jobs, {'class' => 'GoodJob', 'args' => "blah"})
45
+ @worker.working_on(job)
46
+ @worker.unregister_worker(StandardError.new)
47
+ assert_equal 1, Resque::Failure.count
48
+ assert_equal('StandardError', Resque::Failure.all['exception'])
40
49
  end
41
50
 
42
51
  class ::SimpleJobWithFailureHandling
@@ -376,6 +385,19 @@ context "Resque::Worker" do
376
385
  workerA.work(0)
377
386
  assert $BEFORE_FORK_CALLED
378
387
  end
388
+
389
+ test "Will not call a before_fork hook when the worker can't fork" do
390
+ Resque.redis.flushall
391
+ $BEFORE_FORK_CALLED = false
392
+ Resque.before_fork = Proc.new { $BEFORE_FORK_CALLED = true }
393
+ workerA = Resque::Worker.new(:jobs)
394
+ workerA.cant_fork = true
395
+
396
+ assert !$BEFORE_FORK_CALLED, "before_fork should not have been called before job runs"
397
+ Resque::Job.create(:jobs, SomeJob, 20, '/tmp')
398
+ workerA.work(0)
399
+ assert !$BEFORE_FORK_CALLED, "before_fork should not have been called after job runs"
400
+ end
379
401
 
380
402
  test "very verbose works in the afternoon" do
381
403
  begin
@@ -396,16 +418,29 @@ context "Resque::Worker" do
396
418
  end
397
419
  end
398
420
 
399
- test "Will call an after_fork hook after forking" do
421
+ test "Will call an after_fork hook if we're forking" do
422
+ Resque.redis.flushall
423
+ $AFTER_FORK_CALLED = false
424
+ Resque.after_fork = Proc.new { $AFTER_FORK_CALLED = true }
425
+ workerA = Resque::Worker.new(:jobs)
426
+
427
+ assert !$AFTER_FORK_CALLED
428
+ Resque::Job.create(:jobs, SomeJob, 20, '/tmp')
429
+ workerA.work(0)
430
+ assert $AFTER_FORK_CALLED == workerA.will_fork?
431
+ end
432
+
433
+ test "Will not call an after_fork hook when the worker can't fork" do
400
434
  Resque.redis.flushall
401
435
  $AFTER_FORK_CALLED = false
402
436
  Resque.after_fork = Proc.new { $AFTER_FORK_CALLED = true }
403
437
  workerA = Resque::Worker.new(:jobs)
438
+ workerA.cant_fork = true
404
439
 
405
440
  assert !$AFTER_FORK_CALLED
406
441
  Resque::Job.create(:jobs, SomeJob, 20, '/tmp')
407
442
  workerA.work(0)
408
- assert $AFTER_FORK_CALLED
443
+ assert !$AFTER_FORK_CALLED
409
444
  end
410
445
 
411
446
  test "returns PID of running process" do
@@ -438,6 +473,40 @@ context "Resque::Worker" do
438
473
  assert_not_equal original_connection, Resque.redis.client.connection.instance_variable_get("@sock")
439
474
  end
440
475
 
476
+ test "tries to reconnect three times before giving up" do
477
+ begin
478
+ class Redis::Client
479
+ alias_method :original_reconnect, :reconnect
480
+
481
+ def reconnect
482
+ raise Redis::BaseConnectionError
483
+ end
484
+ end
485
+
486
+ class Resque::Worker
487
+ alias_method :original_sleep, :sleep
488
+
489
+ def sleep(duration = nil)
490
+ # noop
491
+ end
492
+ end
493
+
494
+ @worker.very_verbose = true
495
+ stdout, stderr = capture_io { @worker.work(0) }
496
+
497
+ assert_equal 3, stdout.scan(/retrying/).count
498
+ assert_equal 1, stdout.scan(/quitting/).count
499
+ ensure
500
+ class Redis::Client
501
+ alias_method :reconnect, :original_reconnect
502
+ end
503
+
504
+ class Resque::Worker
505
+ alias_method :sleep, :original_sleep
506
+ end
507
+ end
508
+ end
509
+
441
510
  if !defined?(RUBY_ENGINE) || defined?(RUBY_ENGINE) && RUBY_ENGINE != "jruby"
442
511
  test "old signal handling is the default" do
443
512
  rescue_time = nil
@@ -573,14 +642,14 @@ context "Resque::Worker" do
573
642
  end
574
643
 
575
644
  test "displays warning when not using term_child" do
576
- stderr = capture_stderr { @worker.work(0) }
645
+ stdout, stderr = capture_io { @worker.work(0) }
577
646
 
578
647
  assert stderr.match(/^WARNING:/)
579
648
  end
580
649
 
581
650
  test "it does not display warning when using term_child" do
582
651
  @worker.term_child = "1"
583
- stderr = capture_stderr { @worker.work(0) }
652
+ stdout, stderr = capture_io { @worker.work(0) }
584
653
 
585
654
  assert !stderr.match(/^WARNING:/)
586
655
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resque
3
3
  version: !ruby/object:Gem::Version
4
- hash: 79
4
+ hash: 75
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
- - 22
8
+ - 23
9
9
  - 0
10
- version: 1.22.0
10
+ version: 1.23.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Chris Wanstrath
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2012-08-21 00:00:00 Z
19
+ date: 2012-10-01 00:00:00 Z
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
22
  name: redis-namespace
@@ -151,7 +151,6 @@ files:
151
151
  - test/test_helper.rb
152
152
  - test/hoptoad_test.rb
153
153
  - test/resque_test.rb
154
- - test/dump.rdb
155
154
  - test/job_plugins_test.rb
156
155
  homepage: http://github.com/defunkt/resque
157
156
  licenses: []
Binary file