resque 1.23.0 → 1.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

@@ -1,3 +1,3 @@
1
1
  module Resque
2
- Version = VERSION = '1.23.0'
2
+ Version = VERSION = '1.23.1'
3
3
  end
@@ -7,14 +7,9 @@ module Resque
7
7
  # It also ensures workers are always listening to signals from you,
8
8
  # their master, and can react accordingly.
9
9
  class Worker
10
+ extend Resque::Helpers
10
11
  include Resque::Helpers
11
- extend Resque::Helpers
12
-
13
- # Whether the worker should log basic info to STDOUT
14
- attr_accessor :verbose
15
-
16
- # Whether the worker should log lots of info to STDOUT
17
- attr_accessor :very_verbose
12
+ include Resque::Logging
18
13
 
19
14
  # Boolean indicating whether this worker can or can not fork.
20
15
  # Automatically set if a fork(2) fails.
@@ -146,12 +141,13 @@ module Resque
146
141
  rescue SystemCallError
147
142
  nil
148
143
  end
144
+ job.fail(DirtyExit.new($?.to_s)) if $?.signaled?
149
145
  else
150
146
  unregister_signal_handlers if will_fork? && term_child
151
- procline "Processing #{job.queue} since #{Time.now.to_i}"
147
+ procline "Processing #{job.queue} since #{Time.now.to_i} [#{job.payload_class}]"
152
148
  reconnect
153
149
  perform(job, &block)
154
- exit! if will_fork?
150
+ exit!(true) if will_fork?
155
151
  end
156
152
 
157
153
  done_working
@@ -166,6 +162,8 @@ module Resque
166
162
 
167
163
  unregister_worker
168
164
  rescue Exception => exception
165
+ log "Failed to start worker : #{exception.inspect}"
166
+
169
167
  unregister_worker(exception)
170
168
  end
171
169
 
@@ -268,7 +266,7 @@ module Resque
268
266
 
269
267
  # Runs all the methods needed when a worker begins its lifecycle.
270
268
  def startup
271
- warn "WARNING: This way of doing signal handling is now deprecated. Please see http://hone.heroku.com/resque/2012/08/21/resque-signals.html for more info." unless term_child
269
+ Kernel.warn "WARNING: This way of doing signal handling is now deprecated. Please see http://hone.heroku.com/resque/2012/08/21/resque-signals.html for more info." unless term_child
272
270
  enable_gc_optimizations
273
271
  register_signal_handlers
274
272
  prune_dead_workers
@@ -435,12 +433,14 @@ module Resque
435
433
 
436
434
  # Runs a named hook, passing along any arguments.
437
435
  def run_hook(name, *args)
438
- return unless hook = Resque.send(name)
439
- msg = "Running #{name} hook"
436
+ return unless hooks = Resque.send(name)
437
+ msg = "Running #{name} hooks"
440
438
  msg << " with #{args.inspect}" if args.any?
441
439
  log msg
442
440
 
443
- args.any? ? hook.call(*args) : hook.call
441
+ hooks.each do |hook|
442
+ args.any? ? hook.call(*args) : hook.call
443
+ end
444
444
  end
445
445
 
446
446
  # Unregisters ourself as a worker. Useful when shutting down.
@@ -468,7 +468,7 @@ module Resque
468
468
  def working_on(job)
469
469
  data = encode \
470
470
  :queue => job.queue,
471
- :run_at => Time.now.strftime("%Y/%m/%d %H:%M:%S %Z"),
471
+ :run_at => Time.now.utc.iso8601,
472
472
  :payload => job.payload
473
473
  redis.set("worker:#{self}", data)
474
474
  end
@@ -529,7 +529,7 @@ module Resque
529
529
  end
530
530
 
531
531
  def will_fork?
532
- !(@cant_fork || $TESTING)
532
+ !@cant_fork && !$TESTING && (ENV["FORK_PER_JOB"] != 'false')
533
533
  end
534
534
 
535
535
  # Returns a symbol representing the current worker state,
@@ -606,19 +606,62 @@ module Resque
606
606
  log! $0
607
607
  end
608
608
 
609
- # Log a message to STDOUT if we are verbose or very_verbose.
609
+ # Log a message to Resque.logger
610
+ # can't use alias_method since info/debug are private methods
610
611
  def log(message)
611
- if verbose
612
- puts "*** #{message}"
613
- elsif very_verbose
614
- time = Time.now.strftime('%H:%M:%S %Y-%m-%d')
615
- puts "** [#{time}] #$$: #{message}"
616
- end
612
+ info(message)
617
613
  end
618
614
 
619
- # Logs a very verbose message to STDOUT.
620
615
  def log!(message)
621
- log message if very_verbose
616
+ debug(message)
617
+ end
618
+
619
+ # Deprecated legacy methods for controlling the logging threshhold
620
+ # Use Resque.logger.level now, e.g.:
621
+ #
622
+ # Resque.logger.level = Logger::DEBUG
623
+ #
624
+ def verbose
625
+ logger_severity_deprecation_warning
626
+ @verbose
627
+ end
628
+
629
+ def very_verbose
630
+ logger_severity_deprecation_warning
631
+ @very_verbose
632
+ end
633
+
634
+ def verbose=(value);
635
+ logger_severity_deprecation_warning
636
+
637
+ if value && !very_verbose
638
+ Resque.logger.formatter = VerboseFormatter.new
639
+ elsif !value
640
+ Resque.logger.formatter = QuietFormatter.new
641
+ end
642
+
643
+ @verbose = value
644
+ end
645
+
646
+ def very_verbose=(value)
647
+ logger_severity_deprecation_warning
648
+ if value
649
+ Resque.logger.formatter = VeryVerboseFormatter.new
650
+ elsif !value && verbose
651
+ Resque.logger.formatter = VerboseFormatter.new
652
+ else
653
+ Resque.logger.formatter = QuietFormatter.new
654
+ end
655
+
656
+ @very_verbose = value
657
+ end
658
+
659
+ def logger_severity_deprecation_warning
660
+ return if $warned_logger_severity_deprecation
661
+ Kernel.warn "*** DEPRECATION WARNING: Resque::Worker#verbose and #very_verbose are deprecated. Please set Resque.logger.level instead"
662
+ Kernel.warn "Called from: #{caller[0..5].join("\n\t")}"
663
+ $warned_logger_severity_deprecation = true
664
+ nil
622
665
  end
623
666
  end
624
667
  end
Binary file
@@ -0,0 +1,24 @@
1
+ require 'test_helper'
2
+ require 'minitest/mock'
3
+
4
+ context "Resque::Logging" do
5
+ teardown { reset_logger }
6
+
7
+ test "sets and receives the active logger" do
8
+ my_logger = Object.new
9
+ Resque.logger = my_logger
10
+ assert_equal my_logger, Resque.logger
11
+ end
12
+
13
+ %w(debug info error fatal).each do |severity|
14
+ test "logs #{severity} messages" do
15
+ message = "test message"
16
+ mock_logger = MiniTest::Mock.new
17
+ mock_logger.expect severity.to_sym, nil, [message]
18
+ Resque.logger = mock_logger
19
+
20
+ Resque::Logging.send severity, message
21
+ mock_logger.verify
22
+ end
23
+ end
24
+ end
@@ -112,4 +112,4 @@ databases 16
112
112
  # Glue small output buffers together in order to send small replies in a
113
113
  # single TCP packet. Uses a bit more CPU but most of the times it is a win
114
114
  # in terms of number of queries per second. Use 'yes' if unsure.
115
- glueoutputbuf yes
115
+ # glueoutputbuf yes
@@ -3,7 +3,7 @@ require 'resque/failure/redis'
3
3
 
4
4
  context "Resque::Failure::Redis" do
5
5
  setup do
6
- @bad_string = [39, 250, 141, 168, 138, 191, 52, 211, 159, 86, 93, 95, 39].map { |c| c.chr }.join
6
+ @bad_string = [39, 52, 127, 86, 93, 95, 39].map { |c| c.chr }.join
7
7
  exception = StandardError.exception(@bad_string)
8
8
  worker = Resque::Worker.new(:test)
9
9
  queue = "queue"
@@ -0,0 +1,153 @@
1
+ require 'test_helper'
2
+ require 'tempfile'
3
+
4
+ describe "Resque Hooks" do
5
+ before do
6
+ Resque.redis.flushall
7
+
8
+ Resque.before_first_fork = nil
9
+ Resque.before_fork = nil
10
+ Resque.after_fork = nil
11
+
12
+ @worker = Resque::Worker.new(:jobs)
13
+
14
+ $called = false
15
+
16
+ class CallNotifyJob
17
+ def self.perform
18
+ $called = true
19
+ end
20
+ end
21
+ end
22
+
23
+ it 'retrieving hooks if none have been set' do
24
+ assert_equal [], Resque.before_first_fork
25
+ assert_equal [], Resque.before_fork
26
+ assert_equal [], Resque.after_fork
27
+ end
28
+
29
+ it 'it calls before_first_fork once' do
30
+ counter = 0
31
+
32
+ Resque.before_first_fork { counter += 1 }
33
+ 2.times { Resque::Job.create(:jobs, CallNotifyJob) }
34
+
35
+ assert_equal(0, counter)
36
+ @worker.work(0)
37
+ assert_equal(1, counter)
38
+ end
39
+
40
+ it 'it calls before_fork before each job' do
41
+ counter = 0
42
+
43
+ Resque.before_fork { counter += 1 }
44
+ 2.times { Resque::Job.create(:jobs, CallNotifyJob) }
45
+
46
+ assert_equal(0, counter)
47
+ @worker.work(0)
48
+ assert_equal(2, counter)
49
+ end
50
+
51
+ it 'it calls after_fork after each job' do
52
+ # We have to stub out will_fork? to return true, which is going to cause an actual fork(). As such, the
53
+ # exit!(true) will be called in Worker#work; to share state, use a tempfile
54
+ file = Tempfile.new("resque_after_fork")
55
+
56
+ begin
57
+ File.open(file.path, "w") {|f| f.write(0)}
58
+ Resque.after_fork do
59
+ val = File.read(file).strip.to_i
60
+ File.open(file.path, "w") {|f| f.write(val + 1)}
61
+ end
62
+ 2.times { Resque::Job.create(:jobs, CallNotifyJob) }
63
+
64
+ val = File.read(file.path).strip.to_i
65
+ assert_equal(0, val)
66
+ @worker.stubs(:will_fork?).returns(true)
67
+ @worker.work(0)
68
+ val = File.read(file.path).strip.to_i
69
+ assert_equal(2, val)
70
+ ensure
71
+ file.delete
72
+ end
73
+ end
74
+
75
+ it 'it calls before_first_fork before forking' do
76
+ Resque.before_first_fork { assert(!$called) }
77
+
78
+ Resque::Job.create(:jobs, CallNotifyJob)
79
+ @worker.work(0)
80
+ end
81
+
82
+ it 'it calls before_fork before forking' do
83
+ Resque.before_fork { assert(!$called) }
84
+
85
+ Resque::Job.create(:jobs, CallNotifyJob)
86
+ @worker.work(0)
87
+ end
88
+
89
+ it 'it calls after_fork after forking' do
90
+ Resque.after_fork { assert($called) }
91
+
92
+ Resque::Job.create(:jobs, CallNotifyJob)
93
+ @worker.work(0)
94
+ end
95
+
96
+ it 'it registers multiple before_first_forks' do
97
+ first = false
98
+ second = false
99
+
100
+ Resque.before_first_fork { first = true }
101
+ Resque.before_first_fork { second = true }
102
+ Resque::Job.create(:jobs, CallNotifyJob)
103
+
104
+ assert(!first && !second)
105
+ @worker.work(0)
106
+ assert(first && second)
107
+ end
108
+
109
+ it 'it registers multiple before_forks' do
110
+ first = false
111
+ second = false
112
+
113
+ Resque.before_fork { first = true }
114
+ Resque.before_fork { second = true }
115
+ Resque::Job.create(:jobs, CallNotifyJob)
116
+
117
+ assert(!first && !second)
118
+ @worker.work(0)
119
+ assert(first && second)
120
+ end
121
+
122
+ it 'it registers multiple after_forks' do
123
+ # We have to stub out will_fork? to return true, which is going to cause an actual fork(). As such, the
124
+ # exit!(true) will be called in Worker#work; to share state, use a tempfile
125
+ file = Tempfile.new("resque_after_fork_first")
126
+ file2 = Tempfile.new("resque_after_fork_second")
127
+ begin
128
+ File.open(file.path, "w") {|f| f.write(1)}
129
+ File.open(file2.path, "w") {|f| f.write(2)}
130
+
131
+ Resque.after_fork do
132
+ val = File.read(file.path).strip.to_i
133
+ File.open(file.path, "w") {|f| f.write(val + 1)}
134
+ end
135
+
136
+ Resque.after_fork do
137
+ val = File.read(file2.path).strip.to_i
138
+ File.open(file2.path, "w") {|f| f.write(val + 1)}
139
+ end
140
+ Resque::Job.create(:jobs, CallNotifyJob)
141
+
142
+ @worker.stubs(:will_fork?).returns(true)
143
+ @worker.work(0)
144
+ val = File.read(file.path).strip.to_i
145
+ val2 = File.read(file2.path).strip.to_i
146
+ assert_equal(val, 2)
147
+ assert_equal(val2, 3)
148
+ ensure
149
+ file.delete
150
+ file2.delete
151
+ end
152
+ end
153
+ end
@@ -80,9 +80,9 @@ context "Resque" do
80
80
  assert Resque.enqueue(SomeIvarJob, 20, '/tmp')
81
81
  assert_equal 5, Resque.size(:ivar)
82
82
 
83
- assert Resque.dequeue(SomeIvarJob, 30, '/tmp')
83
+ assert_equal 1, Resque.dequeue(SomeIvarJob, 30, '/tmp')
84
84
  assert_equal 4, Resque.size(:ivar)
85
- assert Resque.dequeue(SomeIvarJob)
85
+ assert_equal 3, Resque.dequeue(SomeIvarJob)
86
86
  assert_equal 1, Resque.size(:ivar)
87
87
  end
88
88
 
@@ -199,7 +199,7 @@ context "Resque" do
199
199
  test "knows what queues it is managing" do
200
200
  assert_equal %w( people ), Resque.queues
201
201
  Resque.push(:cars, { 'make' => 'bmw' })
202
- assert_equal %w( cars people ), Resque.queues
202
+ assert_equal %w( cars people ).sort, Resque.queues.sort
203
203
  end
204
204
 
205
205
  test "queues are always a list" do
@@ -209,7 +209,7 @@ context "Resque" do
209
209
 
210
210
  test "can delete a queue" do
211
211
  Resque.push(:cars, { 'make' => 'bmw' })
212
- assert_equal %w( cars people ), Resque.queues
212
+ assert_equal %w( cars people ).sort, Resque.queues.sort
213
213
  Resque.remove_queue(:people)
214
214
  assert_equal %w( cars ), Resque.queues
215
215
  assert_equal nil, Resque.pop(:people)
@@ -1,12 +1,13 @@
1
1
  require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'minitest/autorun'
4
+ require 'redis/namespace'
5
+ require 'mocha'
2
6
 
3
7
  $dir = File.dirname(File.expand_path(__FILE__))
4
8
  $LOAD_PATH.unshift $dir + '/../lib'
5
- $TESTING = true
6
- require 'test/unit'
7
-
8
- require 'redis/namespace'
9
9
  require 'resque'
10
+ $TESTING = true
10
11
 
11
12
  begin
12
13
  require 'leftright'
@@ -30,21 +31,12 @@ end
30
31
  # kill it when they end
31
32
  #
32
33
 
33
- at_exit do
34
- next if $!
35
-
36
- if defined?(MiniTest)
37
- exit_code = MiniTest::Unit.new.run(ARGV)
38
- else
39
- exit_code = Test::Unit::AutoRunner.run
40
- end
41
-
34
+ MiniTest::Unit.after_tests do
42
35
  processes = `ps -A -o pid,command | grep [r]edis-test`.split("\n")
43
36
  pids = processes.map { |process| process.split(" ")[0] }
44
37
  puts "Killing test redis server..."
45
38
  pids.each { |pid| Process.kill("TERM", pid.to_i) }
46
39
  system("rm -f #{$dir}/dump.rdb #{$dir}/dump-cluster.rdb")
47
- exit exit_code
48
40
  end
49
41
 
50
42
  if ENV.key? 'RESQUE_DISTRIBUTED'
@@ -176,3 +168,11 @@ ensure
176
168
  $stdout = orig_stdout
177
169
  $stderr = orig_stderr
178
170
  end
171
+
172
+ # Log to log/test.log
173
+ def reset_logger
174
+ $test_logger ||= Logger.new(File.open(File.expand_path("../../log/test.log", __FILE__), "w"))
175
+ Resque.logger = $test_logger
176
+ end
177
+
178
+ reset_logger
@@ -31,6 +31,13 @@ context "Resque::Worker" do
31
31
  @worker.perform job
32
32
  end
33
33
  end
34
+
35
+ test "register 'run_at' time on UTC timezone in ISO8601 format" do
36
+ job = Resque::Job.new(:jobs, {'class' => 'GoodJob', 'args' => "blah"})
37
+ now = Time.now.utc.iso8601
38
+ @worker.working_on(job)
39
+ assert_equal now, @worker.processing['run_at']
40
+ end
34
41
 
35
42
  test "fails uncompleted jobs with DirtyExit by default on exit" do
36
43
  job = Resque::Job.new(:jobs, {'class' => 'GoodJob', 'args' => "blah"})
@@ -399,22 +406,99 @@ context "Resque::Worker" do
399
406
  assert !$BEFORE_FORK_CALLED, "before_fork should not have been called after job runs"
400
407
  end
401
408
 
409
+ test "setting verbose to true" do
410
+ @worker.verbose = true
411
+
412
+ assert @worker.verbose
413
+ assert !@worker.very_verbose
414
+ end
415
+
416
+ test "setting verbose to false" do
417
+ @worker.verbose = false
418
+
419
+ assert !@worker.verbose
420
+ assert !@worker.very_verbose
421
+ end
422
+
423
+ test "setting very_verbose to true" do
424
+ @worker.very_verbose = true
425
+
426
+ assert !@worker.verbose
427
+ assert @worker.very_verbose
428
+ end
429
+
430
+ test "setting setting verbose to true and then very_verbose to false" do
431
+ @worker.very_verbose = true
432
+ @worker.verbose = true
433
+ @worker.very_verbose = false
434
+
435
+ assert @worker.verbose
436
+ assert !@worker.very_verbose
437
+ end
438
+
439
+ test "verbose prints out logs" do
440
+ messages = StringIO.new
441
+ Resque.logger = Logger.new(messages)
442
+ @worker.verbose = true
443
+
444
+ begin
445
+ @worker.log("omghi mom")
446
+ ensure
447
+ reset_logger
448
+ end
449
+
450
+ assert_equal "*** omghi mom\n", messages.string
451
+ end
452
+
453
+ test "unsetting verbose works" do
454
+ messages = StringIO.new
455
+ Resque.logger = Logger.new(messages)
456
+ @worker.verbose = true
457
+ @worker.verbose = false
458
+
459
+ begin
460
+ @worker.log("omghi mom")
461
+ ensure
462
+ reset_logger
463
+ end
464
+
465
+ assert_equal "", messages.string
466
+ end
467
+
402
468
  test "very verbose works in the afternoon" do
469
+ messages = StringIO.new
470
+ Resque.logger = Logger.new(messages)
471
+
403
472
  begin
404
473
  require 'time'
405
474
  last_puts = ""
406
475
  Time.fake_time = Time.parse("15:44:33 2011-03-02")
407
476
 
408
- @worker.extend(Module.new {
409
- define_method(:puts) { |thing| last_puts = thing }
410
- })
411
-
412
477
  @worker.very_verbose = true
413
478
  @worker.log("some log text")
414
479
 
415
- assert_match /\*\* \[15:44:33 2011-03-02\] \d+: some log text/, last_puts
480
+ assert_match /\*\* \[15:44:33 2011-03-02\] \d+: some log text/, messages.string
416
481
  ensure
417
482
  Time.fake_time = nil
483
+ reset_logger
484
+ end
485
+ end
486
+
487
+ test "won't fork if ENV['FORK_PER_JOB'] is false" do
488
+ begin
489
+ $TESTING = false
490
+ workerA = Resque::Worker.new(:jobs)
491
+
492
+ if workerA.will_fork?
493
+ begin
494
+ ENV["FORK_PER_JOB"] = 'false'
495
+ assert !workerA.will_fork?
496
+ ensure
497
+ ENV["FORK_PER_JOB"] = 'true'
498
+ end
499
+ end
500
+ ensure
501
+ $TESTING = true
418
502
  end
419
503
  end
420
504
 
@@ -491,11 +575,30 @@ context "Resque::Worker" do
491
575
  end
492
576
  end
493
577
 
494
- @worker.very_verbose = true
495
- stdout, stderr = capture_io { @worker.work(0) }
578
+ class DummyLogger
579
+ attr_reader :messages
580
+
581
+ def initialize
582
+ @messages = []
583
+ end
584
+
585
+ def info(message); @messages << message; end
586
+ alias_method :debug, :info
587
+ alias_method :warn, :info
588
+ alias_method :error, :info
589
+ alias_method :fatal, :info
590
+ end
496
591
 
497
- assert_equal 3, stdout.scan(/retrying/).count
498
- assert_equal 1, stdout.scan(/quitting/).count
592
+ Resque.logger = DummyLogger.new
593
+ begin
594
+ @worker.work(0)
595
+ messages = Resque.logger.messages
596
+ ensure
597
+ reset_logger
598
+ end
599
+
600
+ assert_equal 3, messages.grep(/retrying/).count
601
+ assert_equal 1, messages.grep(/quitting/).count
499
602
  ensure
500
603
  class Redis::Client
501
604
  alias_method :reconnect, :original_reconnect
@@ -653,5 +756,56 @@ context "Resque::Worker" do
653
756
 
654
757
  assert !stderr.match(/^WARNING:/)
655
758
  end
759
+
760
+ class SuicidalJob
761
+ @queue = :jobs
762
+
763
+ def self.perform
764
+ Process.kill('KILL', Process.pid)
765
+ end
766
+
767
+ def self.on_failure_store_exception(exc, *args)
768
+ @@failure_exception = exc
769
+ end
770
+ end
771
+
772
+ test "will notify failure hooks when a job is killed by a signal" do
773
+ begin
774
+ $TESTING = false
775
+ Resque.enqueue(SuicidalJob)
776
+ @worker.work(0)
777
+ assert_equal Resque::DirtyExit, SuicidalJob.send(:class_variable_get, :@@failure_exception).class
778
+ ensure
779
+ $TESTING = true
780
+ end
781
+ end
782
+ end
783
+
784
+ test "displays warning when using verbose" do
785
+ stdout, stderr = capture_io { @worker.verbose }
786
+ $warned_logger_severity_deprecation = false
787
+
788
+ assert stderr.match(/WARNING:/)
789
+ end
790
+
791
+ test "displays warning when using verbose=" do
792
+ stdout, stderr = capture_io { @worker.verbose = true }
793
+ $warned_logger_severity_deprecation = false
794
+
795
+ assert stderr.match(/WARNING:/)
796
+ end
797
+
798
+ test "displays warning when using very_verbose" do
799
+ stdout, stderr = capture_io { @worker.very_verbose }
800
+ $warned_logger_severity_deprecation = false
801
+
802
+ assert stderr.match(/WARNING:/)
803
+ end
804
+
805
+ test "displays warning when using very_verbose=" do
806
+ stdout, stderr = capture_io { @worker.very_verbose = true }
807
+ $warned_logger_severity_deprecation = false
808
+
809
+ assert stderr.match(/WARNING:/)
656
810
  end
657
811
  end