resque 1.26.pre.0 → 1.26.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.
- checksums.yaml +7 -0
- data/HISTORY.md +29 -16
- data/README.markdown +60 -6
- data/Rakefile +4 -17
- data/bin/resque-web +4 -0
- data/lib/resque.rb +116 -16
- data/lib/resque/errors.rb +1 -0
- data/lib/resque/failure.rb +11 -5
- data/lib/resque/failure/multiple.rb +6 -1
- data/lib/resque/failure/redis.rb +13 -4
- data/lib/resque/failure/redis_multi_queue.rb +14 -6
- data/lib/resque/helpers.rb +5 -64
- data/lib/resque/job.rb +25 -79
- data/lib/resque/logging.rb +1 -1
- data/lib/resque/plugin.rb +22 -10
- data/lib/resque/server.rb +35 -7
- data/lib/resque/server/helpers.rb +1 -1
- data/lib/resque/server/views/failed.erb +1 -1
- data/lib/resque/server/views/failed_job.erb +3 -3
- data/lib/resque/server/views/failed_queues_overview.erb +3 -3
- data/lib/resque/server/views/workers.erb +2 -0
- data/lib/resque/server/views/working.erb +4 -5
- data/lib/resque/tasks.rb +7 -25
- data/lib/resque/vendor/utf8_util/utf8_util_19.rb +1 -0
- data/lib/resque/version.rb +1 -1
- data/lib/resque/worker.rb +225 -116
- metadata +68 -90
- data/test/airbrake_test.rb +0 -27
- data/test/dump.rdb +0 -0
- data/test/failure_base_test.rb +0 -15
- data/test/job_hooks_test.rb +0 -464
- data/test/job_plugins_test.rb +0 -230
- data/test/logging_test.rb +0 -24
- data/test/plugin_test.rb +0 -116
- data/test/redis-test-cluster.conf +0 -115
- data/test/redis-test.conf +0 -115
- data/test/resque-web_test.rb +0 -59
- data/test/resque_failure_redis_test.rb +0 -19
- data/test/resque_hook_test.rb +0 -165
- data/test/resque_test.rb +0 -278
- data/test/test_helper.rb +0 -198
- data/test/worker_test.rb +0 -1015
data/test/test_helper.rb
DELETED
@@ -1,198 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'bundler/setup'
|
3
|
-
require 'minitest/autorun'
|
4
|
-
require 'redis/namespace'
|
5
|
-
|
6
|
-
require 'mocha/setup'
|
7
|
-
|
8
|
-
$dir = File.dirname(File.expand_path(__FILE__))
|
9
|
-
$LOAD_PATH.unshift $dir + '/../lib'
|
10
|
-
require 'resque'
|
11
|
-
$TESTING = true
|
12
|
-
$TEST_PID=Process.pid
|
13
|
-
|
14
|
-
begin
|
15
|
-
require 'leftright'
|
16
|
-
rescue LoadError
|
17
|
-
end
|
18
|
-
|
19
|
-
|
20
|
-
#
|
21
|
-
# make sure we can run redis
|
22
|
-
#
|
23
|
-
|
24
|
-
if !system("which redis-server")
|
25
|
-
puts '', "** can't find `redis-server` in your path"
|
26
|
-
puts "** try running `sudo rake install`"
|
27
|
-
abort ''
|
28
|
-
end
|
29
|
-
|
30
|
-
|
31
|
-
#
|
32
|
-
# start our own redis when the tests start,
|
33
|
-
# kill it when they end
|
34
|
-
#
|
35
|
-
|
36
|
-
MiniTest::Unit.after_tests do
|
37
|
-
if Process.pid == $TEST_PID
|
38
|
-
processes = `ps -A -o pid,command | grep [r]edis-test`.split("\n")
|
39
|
-
pids = processes.map { |process| process.split(" ")[0] }
|
40
|
-
puts "Killing test redis server..."
|
41
|
-
pids.each { |pid| Process.kill("TERM", pid.to_i) }
|
42
|
-
system("rm -f #{$dir}/dump.rdb #{$dir}/dump-cluster.rdb")
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
if ENV.key? 'RESQUE_DISTRIBUTED'
|
47
|
-
require 'redis/distributed'
|
48
|
-
puts "Starting redis for testing at localhost:9736 and localhost:9737..."
|
49
|
-
`redis-server #{$dir}/redis-test.conf`
|
50
|
-
`redis-server #{$dir}/redis-test-cluster.conf`
|
51
|
-
r = Redis::Distributed.new(['redis://localhost:9736', 'redis://localhost:9737'])
|
52
|
-
Resque.redis = Redis::Namespace.new :resque, :redis => r
|
53
|
-
else
|
54
|
-
puts "Starting redis for testing at localhost:9736..."
|
55
|
-
`redis-server #{$dir}/redis-test.conf`
|
56
|
-
Resque.redis = 'localhost:9736'
|
57
|
-
end
|
58
|
-
|
59
|
-
|
60
|
-
##
|
61
|
-
# test/spec/mini 3
|
62
|
-
# http://gist.github.com/25455
|
63
|
-
# chris@ozmm.org
|
64
|
-
#
|
65
|
-
def context(*args, &block)
|
66
|
-
return super unless (name = args.first) && block
|
67
|
-
require 'test/unit'
|
68
|
-
klass = Class.new(defined?(ActiveSupport::TestCase) ? ActiveSupport::TestCase : Test::Unit::TestCase) do
|
69
|
-
def self.test(name, &block)
|
70
|
-
define_method("test_#{name.gsub(/\W/,'_')}", &block) if block
|
71
|
-
end
|
72
|
-
def self.xtest(*args) end
|
73
|
-
def self.setup(&block) define_method(:setup, &block) end
|
74
|
-
def self.teardown(&block) define_method(:teardown, &block) end
|
75
|
-
end
|
76
|
-
(class << klass; self end).send(:define_method, :name) { name.gsub(/\W/,'_') }
|
77
|
-
klass.class_eval &block
|
78
|
-
# XXX: In 1.8.x, not all tests will run unless anonymous classes are kept in scope.
|
79
|
-
($test_classes ||= []) << klass
|
80
|
-
end
|
81
|
-
|
82
|
-
##
|
83
|
-
# Helper to perform job classes
|
84
|
-
#
|
85
|
-
module PerformJob
|
86
|
-
def perform_job(klass, *args)
|
87
|
-
resque_job = Resque::Job.new(:testqueue, 'class' => klass, 'args' => args)
|
88
|
-
resque_job.perform
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
#
|
93
|
-
# fixture classes
|
94
|
-
#
|
95
|
-
|
96
|
-
class SomeJob
|
97
|
-
def self.perform(repo_id, path)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
class SomeIvarJob < SomeJob
|
102
|
-
@queue = :ivar
|
103
|
-
end
|
104
|
-
|
105
|
-
class SomeMethodJob < SomeJob
|
106
|
-
def self.queue
|
107
|
-
:method
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
class BadJob
|
112
|
-
def self.perform
|
113
|
-
raise "Bad job!"
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
class GoodJob
|
118
|
-
def self.perform(name)
|
119
|
-
"Good job, #{name}"
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
class AtExitJob
|
124
|
-
def self.perform(filename)
|
125
|
-
at_exit do
|
126
|
-
File.open(filename, "w") {|file| file.puts "at_exit"}
|
127
|
-
end
|
128
|
-
"at_exit job"
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
class BadJobWithSyntaxError
|
133
|
-
def self.perform
|
134
|
-
raise SyntaxError, "Extra Bad job!"
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
class BadFailureBackend < Resque::Failure::Base
|
139
|
-
def save
|
140
|
-
raise Exception.new("Failure backend error")
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def with_failure_backend(failure_backend, &block)
|
145
|
-
previous_backend = Resque::Failure.backend
|
146
|
-
Resque::Failure.backend = failure_backend
|
147
|
-
yield block
|
148
|
-
ensure
|
149
|
-
Resque::Failure.backend = previous_backend
|
150
|
-
end
|
151
|
-
|
152
|
-
require 'time'
|
153
|
-
|
154
|
-
class Time
|
155
|
-
# Thanks, Timecop
|
156
|
-
class << self
|
157
|
-
attr_accessor :fake_time
|
158
|
-
|
159
|
-
alias_method :now_without_mock_time, :now
|
160
|
-
|
161
|
-
def now
|
162
|
-
fake_time || now_without_mock_time
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
self.fake_time = nil
|
167
|
-
end
|
168
|
-
|
169
|
-
# From minitest/unit
|
170
|
-
def capture_io
|
171
|
-
require 'stringio'
|
172
|
-
|
173
|
-
orig_stdout, orig_stderr = $stdout, $stderr
|
174
|
-
captured_stdout, captured_stderr = StringIO.new, StringIO.new
|
175
|
-
$stdout, $stderr = captured_stdout, captured_stderr
|
176
|
-
|
177
|
-
yield
|
178
|
-
|
179
|
-
return captured_stdout.string, captured_stderr.string
|
180
|
-
ensure
|
181
|
-
$stdout = orig_stdout
|
182
|
-
$stderr = orig_stderr
|
183
|
-
end
|
184
|
-
|
185
|
-
# Log to log/test.log
|
186
|
-
def reset_logger
|
187
|
-
$test_logger ||= MonoLogger.new(File.open(File.expand_path("../../log/test.log", __FILE__), "w"))
|
188
|
-
Resque.logger = $test_logger
|
189
|
-
end
|
190
|
-
|
191
|
-
reset_logger
|
192
|
-
|
193
|
-
def suppress_warnings
|
194
|
-
old_verbose, $VERBOSE = $VERBOSE, nil
|
195
|
-
yield
|
196
|
-
ensure
|
197
|
-
$VERBOSE = old_verbose
|
198
|
-
end
|
data/test/worker_test.rb
DELETED
@@ -1,1015 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
require 'tmpdir'
|
3
|
-
|
4
|
-
context "Resque::Worker" do
|
5
|
-
setup 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
|
-
Resque::Job.create(:jobs, SomeJob, 20, '/tmp')
|
14
|
-
end
|
15
|
-
|
16
|
-
test "can fail jobs" do
|
17
|
-
Resque::Job.create(:jobs, BadJob)
|
18
|
-
@worker.work(0)
|
19
|
-
assert_equal 1, Resque::Failure.count
|
20
|
-
end
|
21
|
-
|
22
|
-
test "failed jobs report exception and message" do
|
23
|
-
Resque::Job.create(:jobs, BadJobWithSyntaxError)
|
24
|
-
@worker.work(0)
|
25
|
-
assert_equal('SyntaxError', Resque::Failure.all['exception'])
|
26
|
-
assert_equal('Extra Bad job!', Resque::Failure.all['error'])
|
27
|
-
end
|
28
|
-
|
29
|
-
test "does not allow exceptions from failure backend to escape" do
|
30
|
-
job = Resque::Job.new(:jobs, {})
|
31
|
-
with_failure_backend BadFailureBackend do
|
32
|
-
@worker.perform job
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
test "does not raise exception for completed jobs" do
|
37
|
-
if worker_pid = Kernel.fork
|
38
|
-
Process.waitpid(worker_pid)
|
39
|
-
assert_equal 0, Resque::Failure.count
|
40
|
-
else
|
41
|
-
# ensure we actually fork
|
42
|
-
$TESTING = false
|
43
|
-
Resque.redis.client.reconnect
|
44
|
-
worker = Resque::Worker.new(:jobs)
|
45
|
-
suppress_warnings do
|
46
|
-
worker.work(0)
|
47
|
-
end
|
48
|
-
exit
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
test "executes at_exit hooks when configured with run_at_exit_hooks" do
|
53
|
-
tmpfile = File.join(Dir.tmpdir, "resque_at_exit_test_file")
|
54
|
-
FileUtils.rm_f tmpfile
|
55
|
-
|
56
|
-
if worker_pid = Kernel.fork
|
57
|
-
Process.waitpid(worker_pid)
|
58
|
-
assert File.exist?(tmpfile), "The file '#{tmpfile}' does not exist"
|
59
|
-
assert_equal "at_exit", File.open(tmpfile).read.strip
|
60
|
-
else
|
61
|
-
# ensure we actually fork
|
62
|
-
$TESTING = false
|
63
|
-
Resque.redis.client.reconnect
|
64
|
-
Resque::Job.create(:at_exit_jobs, AtExitJob, tmpfile)
|
65
|
-
worker = Resque::Worker.new(:at_exit_jobs)
|
66
|
-
worker.run_at_exit_hooks = true
|
67
|
-
suppress_warnings do
|
68
|
-
worker.work(0)
|
69
|
-
end
|
70
|
-
exit
|
71
|
-
end
|
72
|
-
|
73
|
-
end
|
74
|
-
|
75
|
-
class ::RaiseExceptionOnFailure
|
76
|
-
|
77
|
-
def self.on_failure_trhow_exception(exception,*args)
|
78
|
-
$TESTING = true
|
79
|
-
raise "The worker threw an exception"
|
80
|
-
end
|
81
|
-
|
82
|
-
def self.perform
|
83
|
-
""
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
test "should not treat SystemExit as an exception in the child with run_at_exit_hooks == true" do
|
88
|
-
|
89
|
-
if worker_pid = Kernel.fork
|
90
|
-
Process.waitpid(worker_pid)
|
91
|
-
else
|
92
|
-
# ensure we actually fork
|
93
|
-
$TESTING = false
|
94
|
-
Resque.redis.client.reconnect
|
95
|
-
Resque::Job.create(:not_failing_job, RaiseExceptionOnFailure)
|
96
|
-
worker = Resque::Worker.new(:not_failing_job)
|
97
|
-
worker.run_at_exit_hooks = true
|
98
|
-
suppress_warnings do
|
99
|
-
worker.work(0)
|
100
|
-
end
|
101
|
-
exit
|
102
|
-
end
|
103
|
-
|
104
|
-
end
|
105
|
-
|
106
|
-
|
107
|
-
test "does not execute at_exit hooks by default" do
|
108
|
-
tmpfile = File.join(Dir.tmpdir, "resque_at_exit_test_file")
|
109
|
-
FileUtils.rm_f tmpfile
|
110
|
-
|
111
|
-
if worker_pid = Kernel.fork
|
112
|
-
Process.waitpid(worker_pid)
|
113
|
-
assert !File.exist?(tmpfile), "The file '#{tmpfile}' exists, at_exit hooks were run"
|
114
|
-
else
|
115
|
-
# ensure we actually fork
|
116
|
-
$TESTING = false
|
117
|
-
Resque.redis.client.reconnect
|
118
|
-
Resque::Job.create(:at_exit_jobs, AtExitJob, tmpfile)
|
119
|
-
worker = Resque::Worker.new(:at_exit_jobs)
|
120
|
-
suppress_warnings do
|
121
|
-
worker.work(0)
|
122
|
-
end
|
123
|
-
exit
|
124
|
-
end
|
125
|
-
|
126
|
-
end
|
127
|
-
|
128
|
-
test "does report failure for jobs with invalid payload" do
|
129
|
-
job = Resque::Job.new(:jobs, { 'class' => 'NotAValidJobClass', 'args' => '' })
|
130
|
-
@worker.perform job
|
131
|
-
assert_equal 1, Resque::Failure.count, 'failure not reported'
|
132
|
-
end
|
133
|
-
|
134
|
-
test "register 'run_at' time on UTC timezone in ISO8601 format" do
|
135
|
-
job = Resque::Job.new(:jobs, {'class' => 'GoodJob', 'args' => "blah"})
|
136
|
-
now = Time.now.utc.iso8601
|
137
|
-
@worker.working_on(job)
|
138
|
-
assert_equal now, @worker.processing['run_at']
|
139
|
-
end
|
140
|
-
|
141
|
-
test "fails uncompleted jobs with DirtyExit by default on exit" do
|
142
|
-
job = Resque::Job.new(:jobs, {'class' => 'GoodJob', 'args' => "blah"})
|
143
|
-
@worker.working_on(job)
|
144
|
-
@worker.unregister_worker
|
145
|
-
assert_equal 1, Resque::Failure.count
|
146
|
-
assert_equal('Resque::DirtyExit', Resque::Failure.all['exception'])
|
147
|
-
end
|
148
|
-
|
149
|
-
test "fails uncompleted jobs with worker exception on exit" do
|
150
|
-
job = Resque::Job.new(:jobs, {'class' => 'GoodJob', 'args' => "blah"})
|
151
|
-
@worker.working_on(job)
|
152
|
-
@worker.unregister_worker(StandardError.new)
|
153
|
-
assert_equal 1, Resque::Failure.count
|
154
|
-
assert_equal('StandardError', Resque::Failure.all['exception'])
|
155
|
-
end
|
156
|
-
|
157
|
-
class ::SimpleJobWithFailureHandling
|
158
|
-
def self.on_failure_record_failure(exception, *job_args)
|
159
|
-
@@exception = exception
|
160
|
-
end
|
161
|
-
|
162
|
-
def self.exception
|
163
|
-
@@exception
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
test "fails uncompleted jobs on exit, and calls failure hook" do
|
168
|
-
job = Resque::Job.new(:jobs, {'class' => 'SimpleJobWithFailureHandling', 'args' => ""})
|
169
|
-
@worker.working_on(job)
|
170
|
-
@worker.unregister_worker
|
171
|
-
assert_equal 1, Resque::Failure.count
|
172
|
-
assert(SimpleJobWithFailureHandling.exception.kind_of?(Resque::DirtyExit))
|
173
|
-
end
|
174
|
-
|
175
|
-
class ::SimpleFailingJob
|
176
|
-
@@exception_count = 0
|
177
|
-
|
178
|
-
def self.on_failure_record_failure(exception, *job_args)
|
179
|
-
@@exception_count += 1
|
180
|
-
end
|
181
|
-
|
182
|
-
def self.exception_count
|
183
|
-
@@exception_count
|
184
|
-
end
|
185
|
-
|
186
|
-
def self.perform
|
187
|
-
raise Exception.new
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
test "only calls failure hook once on exception" do
|
192
|
-
job = Resque::Job.new(:jobs, {'class' => 'SimpleFailingJob', 'args' => ""})
|
193
|
-
@worker.perform(job)
|
194
|
-
assert_equal 1, Resque::Failure.count
|
195
|
-
assert_equal 1, SimpleFailingJob.exception_count
|
196
|
-
end
|
197
|
-
|
198
|
-
test "can peek at failed jobs" do
|
199
|
-
10.times { Resque::Job.create(:jobs, BadJob) }
|
200
|
-
@worker.work(0)
|
201
|
-
assert_equal 10, Resque::Failure.count
|
202
|
-
|
203
|
-
assert_equal 10, Resque::Failure.all(0, 20).size
|
204
|
-
end
|
205
|
-
|
206
|
-
test "can clear failed jobs" do
|
207
|
-
Resque::Job.create(:jobs, BadJob)
|
208
|
-
@worker.work(0)
|
209
|
-
assert_equal 1, Resque::Failure.count
|
210
|
-
Resque::Failure.clear
|
211
|
-
assert_equal 0, Resque::Failure.count
|
212
|
-
end
|
213
|
-
|
214
|
-
test "catches exceptional jobs" do
|
215
|
-
Resque::Job.create(:jobs, BadJob)
|
216
|
-
Resque::Job.create(:jobs, BadJob)
|
217
|
-
@worker.process
|
218
|
-
@worker.process
|
219
|
-
@worker.process
|
220
|
-
assert_equal 2, Resque::Failure.count
|
221
|
-
end
|
222
|
-
|
223
|
-
test "strips whitespace from queue names" do
|
224
|
-
queues = "critical, high, low".split(',')
|
225
|
-
worker = Resque::Worker.new(*queues)
|
226
|
-
assert_equal %w( critical high low ), worker.queues
|
227
|
-
end
|
228
|
-
|
229
|
-
test "can work on multiple queues" do
|
230
|
-
Resque::Job.create(:high, GoodJob)
|
231
|
-
Resque::Job.create(:critical, GoodJob)
|
232
|
-
|
233
|
-
worker = Resque::Worker.new(:critical, :high)
|
234
|
-
|
235
|
-
worker.process
|
236
|
-
assert_equal 1, Resque.size(:high)
|
237
|
-
assert_equal 0, Resque.size(:critical)
|
238
|
-
|
239
|
-
worker.process
|
240
|
-
assert_equal 0, Resque.size(:high)
|
241
|
-
end
|
242
|
-
|
243
|
-
test "can work on all queues" do
|
244
|
-
Resque::Job.create(:high, GoodJob)
|
245
|
-
Resque::Job.create(:critical, GoodJob)
|
246
|
-
Resque::Job.create(:blahblah, GoodJob)
|
247
|
-
|
248
|
-
worker = Resque::Worker.new("*")
|
249
|
-
|
250
|
-
worker.work(0)
|
251
|
-
assert_equal 0, Resque.size(:high)
|
252
|
-
assert_equal 0, Resque.size(:critical)
|
253
|
-
assert_equal 0, Resque.size(:blahblah)
|
254
|
-
end
|
255
|
-
|
256
|
-
test "can work with wildcard at the end of the list" do
|
257
|
-
Resque::Job.create(:high, GoodJob)
|
258
|
-
Resque::Job.create(:critical, GoodJob)
|
259
|
-
Resque::Job.create(:blahblah, GoodJob)
|
260
|
-
Resque::Job.create(:beer, GoodJob)
|
261
|
-
|
262
|
-
worker = Resque::Worker.new(:critical, :high, "*")
|
263
|
-
|
264
|
-
worker.work(0)
|
265
|
-
assert_equal 0, Resque.size(:high)
|
266
|
-
assert_equal 0, Resque.size(:critical)
|
267
|
-
assert_equal 0, Resque.size(:blahblah)
|
268
|
-
assert_equal 0, Resque.size(:beer)
|
269
|
-
end
|
270
|
-
|
271
|
-
test "can work with wildcard at the middle of the list" do
|
272
|
-
Resque::Job.create(:high, GoodJob)
|
273
|
-
Resque::Job.create(:critical, GoodJob)
|
274
|
-
Resque::Job.create(:blahblah, GoodJob)
|
275
|
-
Resque::Job.create(:beer, GoodJob)
|
276
|
-
|
277
|
-
worker = Resque::Worker.new(:critical, "*", :high)
|
278
|
-
|
279
|
-
worker.work(0)
|
280
|
-
assert_equal 0, Resque.size(:high)
|
281
|
-
assert_equal 0, Resque.size(:critical)
|
282
|
-
assert_equal 0, Resque.size(:blahblah)
|
283
|
-
assert_equal 0, Resque.size(:beer)
|
284
|
-
end
|
285
|
-
|
286
|
-
test "processes * queues in alphabetical order" do
|
287
|
-
Resque::Job.create(:high, GoodJob)
|
288
|
-
Resque::Job.create(:critical, GoodJob)
|
289
|
-
Resque::Job.create(:blahblah, GoodJob)
|
290
|
-
|
291
|
-
worker = Resque::Worker.new("*")
|
292
|
-
processed_queues = []
|
293
|
-
|
294
|
-
worker.work(0) do |job|
|
295
|
-
processed_queues << job.queue
|
296
|
-
end
|
297
|
-
|
298
|
-
assert_equal %w( jobs high critical blahblah ).sort, processed_queues
|
299
|
-
end
|
300
|
-
|
301
|
-
test "works with globs" do
|
302
|
-
Resque::Job.create(:critical, GoodJob)
|
303
|
-
Resque::Job.create(:test_one, GoodJob)
|
304
|
-
Resque::Job.create(:test_two, GoodJob)
|
305
|
-
|
306
|
-
worker = Resque::Worker.new("test_*")
|
307
|
-
|
308
|
-
worker.work(0)
|
309
|
-
assert_equal 1, Resque.size(:critical)
|
310
|
-
assert_equal 0, Resque.size(:test_one)
|
311
|
-
assert_equal 0, Resque.size(:test_two)
|
312
|
-
end
|
313
|
-
|
314
|
-
test "has a unique id" do
|
315
|
-
assert_equal "#{`hostname`.chomp}:#{$$}:jobs", @worker.to_s
|
316
|
-
end
|
317
|
-
|
318
|
-
test "complains if no queues are given" do
|
319
|
-
assert_raise Resque::NoQueueError do
|
320
|
-
Resque::Worker.new
|
321
|
-
end
|
322
|
-
end
|
323
|
-
|
324
|
-
test "fails if a job class has no `perform` method" do
|
325
|
-
worker = Resque::Worker.new(:perform_less)
|
326
|
-
Resque::Job.create(:perform_less, Object)
|
327
|
-
|
328
|
-
assert_equal 0, Resque::Failure.count
|
329
|
-
worker.work(0)
|
330
|
-
assert_equal 1, Resque::Failure.count
|
331
|
-
end
|
332
|
-
|
333
|
-
test "inserts itself into the 'workers' list on startup" do
|
334
|
-
@worker.work(0) do
|
335
|
-
assert_equal @worker, Resque.workers[0]
|
336
|
-
end
|
337
|
-
end
|
338
|
-
|
339
|
-
test "removes itself from the 'workers' list on shutdown" do
|
340
|
-
@worker.work(0) do
|
341
|
-
assert_equal @worker, Resque.workers[0]
|
342
|
-
end
|
343
|
-
|
344
|
-
assert_equal [], Resque.workers
|
345
|
-
end
|
346
|
-
|
347
|
-
test "removes worker with stringified id" do
|
348
|
-
@worker.work(0) do
|
349
|
-
worker_id = Resque.workers[0].to_s
|
350
|
-
Resque.remove_worker(worker_id)
|
351
|
-
assert_equal [], Resque.workers
|
352
|
-
end
|
353
|
-
end
|
354
|
-
|
355
|
-
test "records what it is working on" do
|
356
|
-
@worker.work(0) do
|
357
|
-
task = @worker.job
|
358
|
-
assert_equal({"args"=>[20, "/tmp"], "class"=>"SomeJob"}, task['payload'])
|
359
|
-
assert task['run_at']
|
360
|
-
assert_equal 'jobs', task['queue']
|
361
|
-
end
|
362
|
-
end
|
363
|
-
|
364
|
-
test "clears its status when not working on anything" do
|
365
|
-
@worker.work(0)
|
366
|
-
assert_equal Hash.new, @worker.job
|
367
|
-
end
|
368
|
-
|
369
|
-
test "knows when it is working" do
|
370
|
-
@worker.work(0) do
|
371
|
-
assert @worker.working?
|
372
|
-
end
|
373
|
-
end
|
374
|
-
|
375
|
-
test "knows when it is idle" do
|
376
|
-
@worker.work(0)
|
377
|
-
assert @worker.idle?
|
378
|
-
end
|
379
|
-
|
380
|
-
test "knows who is working" do
|
381
|
-
@worker.work(0) do
|
382
|
-
assert_equal [@worker], Resque.working
|
383
|
-
end
|
384
|
-
end
|
385
|
-
|
386
|
-
test "keeps track of how many jobs it has processed" do
|
387
|
-
Resque::Job.create(:jobs, BadJob)
|
388
|
-
Resque::Job.create(:jobs, BadJob)
|
389
|
-
|
390
|
-
3.times do
|
391
|
-
job = @worker.reserve
|
392
|
-
@worker.process job
|
393
|
-
end
|
394
|
-
assert_equal 3, @worker.processed
|
395
|
-
end
|
396
|
-
|
397
|
-
test "keeps track of how many failures it has seen" do
|
398
|
-
Resque::Job.create(:jobs, BadJob)
|
399
|
-
Resque::Job.create(:jobs, BadJob)
|
400
|
-
|
401
|
-
3.times do
|
402
|
-
job = @worker.reserve
|
403
|
-
@worker.process job
|
404
|
-
end
|
405
|
-
assert_equal 2, @worker.failed
|
406
|
-
end
|
407
|
-
|
408
|
-
test "stats are erased when the worker goes away" do
|
409
|
-
@worker.work(0)
|
410
|
-
assert_equal 0, @worker.processed
|
411
|
-
assert_equal 0, @worker.failed
|
412
|
-
end
|
413
|
-
|
414
|
-
test "knows when it started" do
|
415
|
-
time = Time.now
|
416
|
-
@worker.work(0) do
|
417
|
-
assert Time.parse(@worker.started) - time < 0.1
|
418
|
-
end
|
419
|
-
end
|
420
|
-
|
421
|
-
test "knows whether it exists or not" do
|
422
|
-
@worker.work(0) do
|
423
|
-
assert Resque::Worker.exists?(@worker)
|
424
|
-
assert !Resque::Worker.exists?('blah-blah')
|
425
|
-
end
|
426
|
-
end
|
427
|
-
|
428
|
-
test "sets $0 while working" do
|
429
|
-
@worker.work(0) do
|
430
|
-
ver = Resque::Version
|
431
|
-
assert_equal "resque-#{ver}: Processing jobs since #{Time.now.to_i}", $0
|
432
|
-
end
|
433
|
-
end
|
434
|
-
|
435
|
-
test "can be found" do
|
436
|
-
@worker.work(0) do
|
437
|
-
found = Resque::Worker.find(@worker.to_s)
|
438
|
-
assert_equal @worker.to_s, found.to_s
|
439
|
-
assert found.working?
|
440
|
-
assert_equal @worker.job, found.job
|
441
|
-
end
|
442
|
-
end
|
443
|
-
|
444
|
-
test "doesn't find fakes" do
|
445
|
-
@worker.work(0) do
|
446
|
-
found = Resque::Worker.find('blah-blah')
|
447
|
-
assert_equal nil, found
|
448
|
-
end
|
449
|
-
end
|
450
|
-
|
451
|
-
test "cleans up dead worker info on start (crash recovery)" do
|
452
|
-
# first we fake out several dead workers
|
453
|
-
# 1: matches queue and hostname; gets pruned.
|
454
|
-
workerA = Resque::Worker.new(:jobs)
|
455
|
-
workerA.instance_variable_set(:@to_s, "#{`hostname`.chomp}:1:jobs")
|
456
|
-
workerA.register_worker
|
457
|
-
|
458
|
-
# 2. matches queue but not hostname; no prune.
|
459
|
-
workerB = Resque::Worker.new(:jobs)
|
460
|
-
workerB.instance_variable_set(:@to_s, "#{`hostname`.chomp}-foo:2:jobs")
|
461
|
-
workerB.register_worker
|
462
|
-
|
463
|
-
# 3. matches hostname but not queue; no prune.
|
464
|
-
workerB = Resque::Worker.new(:high)
|
465
|
-
workerB.instance_variable_set(:@to_s, "#{`hostname`.chomp}:3:high")
|
466
|
-
workerB.register_worker
|
467
|
-
|
468
|
-
# 4. matches neither hostname nor queue; no prune.
|
469
|
-
workerB = Resque::Worker.new(:high)
|
470
|
-
workerB.instance_variable_set(:@to_s, "#{`hostname`.chomp}-foo:4:high")
|
471
|
-
workerB.register_worker
|
472
|
-
|
473
|
-
assert_equal 4, Resque.workers.size
|
474
|
-
|
475
|
-
# then we prune them
|
476
|
-
@worker.work(0)
|
477
|
-
|
478
|
-
worker_strings = Resque::Worker.all.map(&:to_s)
|
479
|
-
|
480
|
-
assert_equal 3, Resque.workers.size
|
481
|
-
|
482
|
-
# pruned
|
483
|
-
assert !worker_strings.include?("#{`hostname`.chomp}:1:jobs")
|
484
|
-
|
485
|
-
# not pruned
|
486
|
-
assert worker_strings.include?("#{`hostname`.chomp}-foo:2:jobs")
|
487
|
-
assert worker_strings.include?("#{`hostname`.chomp}:3:high")
|
488
|
-
assert worker_strings.include?("#{`hostname`.chomp}-foo:4:high")
|
489
|
-
end
|
490
|
-
|
491
|
-
test "worker_pids returns pids" do
|
492
|
-
known_workers = @worker.worker_pids
|
493
|
-
assert !known_workers.empty?
|
494
|
-
end
|
495
|
-
|
496
|
-
test "Processed jobs count" do
|
497
|
-
@worker.work(0)
|
498
|
-
assert_equal 1, Resque.info[:processed]
|
499
|
-
end
|
500
|
-
|
501
|
-
test "Will call a before_first_fork hook only once" do
|
502
|
-
Resque.redis.flushall
|
503
|
-
$BEFORE_FORK_CALLED = 0
|
504
|
-
Resque.before_first_fork = Proc.new { $BEFORE_FORK_CALLED += 1 }
|
505
|
-
workerA = Resque::Worker.new(:jobs)
|
506
|
-
Resque::Job.create(:jobs, SomeJob, 20, '/tmp')
|
507
|
-
|
508
|
-
assert_equal 0, $BEFORE_FORK_CALLED
|
509
|
-
|
510
|
-
workerA.work(0)
|
511
|
-
assert_equal 1, $BEFORE_FORK_CALLED
|
512
|
-
|
513
|
-
# TODO: Verify it's only run once. Not easy.
|
514
|
-
# workerA.work(0)
|
515
|
-
# assert_equal 1, $BEFORE_FORK_CALLED
|
516
|
-
end
|
517
|
-
|
518
|
-
test "Will call a before_fork hook before forking" do
|
519
|
-
Resque.redis.flushall
|
520
|
-
$BEFORE_FORK_CALLED = false
|
521
|
-
Resque.before_fork = Proc.new { $BEFORE_FORK_CALLED = true }
|
522
|
-
workerA = Resque::Worker.new(:jobs)
|
523
|
-
|
524
|
-
assert !$BEFORE_FORK_CALLED
|
525
|
-
Resque::Job.create(:jobs, SomeJob, 20, '/tmp')
|
526
|
-
workerA.work(0)
|
527
|
-
assert $BEFORE_FORK_CALLED
|
528
|
-
end
|
529
|
-
|
530
|
-
test "Will not call a before_fork hook when the worker can't fork" do
|
531
|
-
Resque.redis.flushall
|
532
|
-
$BEFORE_FORK_CALLED = false
|
533
|
-
Resque.before_fork = Proc.new { $BEFORE_FORK_CALLED = true }
|
534
|
-
workerA = Resque::Worker.new(:jobs)
|
535
|
-
workerA.cant_fork = true
|
536
|
-
|
537
|
-
assert !$BEFORE_FORK_CALLED, "before_fork should not have been called before job runs"
|
538
|
-
Resque::Job.create(:jobs, SomeJob, 20, '/tmp')
|
539
|
-
workerA.work(0)
|
540
|
-
assert !$BEFORE_FORK_CALLED, "before_fork should not have been called after job runs"
|
541
|
-
end
|
542
|
-
|
543
|
-
test "setting verbose to true" do
|
544
|
-
@worker.verbose = true
|
545
|
-
|
546
|
-
assert @worker.verbose
|
547
|
-
assert !@worker.very_verbose
|
548
|
-
end
|
549
|
-
|
550
|
-
test "setting verbose to false" do
|
551
|
-
@worker.verbose = false
|
552
|
-
|
553
|
-
assert !@worker.verbose
|
554
|
-
assert !@worker.very_verbose
|
555
|
-
end
|
556
|
-
|
557
|
-
test "setting very_verbose to true" do
|
558
|
-
@worker.very_verbose = true
|
559
|
-
|
560
|
-
assert !@worker.verbose
|
561
|
-
assert @worker.very_verbose
|
562
|
-
end
|
563
|
-
|
564
|
-
test "setting setting verbose to true and then very_verbose to false" do
|
565
|
-
@worker.very_verbose = true
|
566
|
-
@worker.verbose = true
|
567
|
-
@worker.very_verbose = false
|
568
|
-
|
569
|
-
assert @worker.verbose
|
570
|
-
assert !@worker.very_verbose
|
571
|
-
end
|
572
|
-
|
573
|
-
test "verbose prints out logs" do
|
574
|
-
messages = StringIO.new
|
575
|
-
Resque.logger = Logger.new(messages)
|
576
|
-
@worker.verbose = true
|
577
|
-
|
578
|
-
begin
|
579
|
-
@worker.log("omghi mom")
|
580
|
-
ensure
|
581
|
-
reset_logger
|
582
|
-
end
|
583
|
-
|
584
|
-
assert_equal "*** omghi mom\n", messages.string
|
585
|
-
end
|
586
|
-
|
587
|
-
test "unsetting verbose works" do
|
588
|
-
messages = StringIO.new
|
589
|
-
Resque.logger = Logger.new(messages)
|
590
|
-
@worker.verbose = true
|
591
|
-
@worker.verbose = false
|
592
|
-
|
593
|
-
begin
|
594
|
-
@worker.log("omghi mom")
|
595
|
-
ensure
|
596
|
-
reset_logger
|
597
|
-
end
|
598
|
-
|
599
|
-
assert_equal "", messages.string
|
600
|
-
end
|
601
|
-
|
602
|
-
test "very verbose works in the afternoon" do
|
603
|
-
messages = StringIO.new
|
604
|
-
Resque.logger = Logger.new(messages)
|
605
|
-
|
606
|
-
begin
|
607
|
-
require 'time'
|
608
|
-
last_puts = ""
|
609
|
-
Time.fake_time = Time.parse("15:44:33 2011-03-02")
|
610
|
-
|
611
|
-
@worker.very_verbose = true
|
612
|
-
@worker.log("some log text")
|
613
|
-
|
614
|
-
assert_match /\*\* \[15:44:33 2011-03-02\] \d+: some log text/, messages.string
|
615
|
-
ensure
|
616
|
-
Time.fake_time = nil
|
617
|
-
reset_logger
|
618
|
-
end
|
619
|
-
end
|
620
|
-
|
621
|
-
test "won't fork if ENV['FORK_PER_JOB'] is false" do
|
622
|
-
begin
|
623
|
-
$TESTING = false
|
624
|
-
workerA = Resque::Worker.new(:jobs)
|
625
|
-
|
626
|
-
if workerA.will_fork?
|
627
|
-
begin
|
628
|
-
ENV["FORK_PER_JOB"] = 'false'
|
629
|
-
assert !workerA.will_fork?
|
630
|
-
ensure
|
631
|
-
ENV["FORK_PER_JOB"] = 'true'
|
632
|
-
end
|
633
|
-
end
|
634
|
-
ensure
|
635
|
-
$TESTING = true
|
636
|
-
end
|
637
|
-
end
|
638
|
-
|
639
|
-
test "Will call an after_fork hook if we're forking" do
|
640
|
-
Resque.redis.flushall
|
641
|
-
$AFTER_FORK_CALLED = false
|
642
|
-
Resque.after_fork = Proc.new { $AFTER_FORK_CALLED = true }
|
643
|
-
workerA = Resque::Worker.new(:jobs)
|
644
|
-
|
645
|
-
assert !$AFTER_FORK_CALLED
|
646
|
-
Resque::Job.create(:jobs, SomeJob, 20, '/tmp')
|
647
|
-
workerA.work(0)
|
648
|
-
assert $AFTER_FORK_CALLED == workerA.will_fork?
|
649
|
-
end
|
650
|
-
|
651
|
-
test "Will not call an after_fork hook when the worker can't fork" do
|
652
|
-
Resque.redis.flushall
|
653
|
-
$AFTER_FORK_CALLED = false
|
654
|
-
Resque.after_fork = Proc.new { $AFTER_FORK_CALLED = true }
|
655
|
-
workerA = Resque::Worker.new(:jobs)
|
656
|
-
workerA.cant_fork = true
|
657
|
-
|
658
|
-
assert !$AFTER_FORK_CALLED
|
659
|
-
Resque::Job.create(:jobs, SomeJob, 20, '/tmp')
|
660
|
-
workerA.work(0)
|
661
|
-
assert !$AFTER_FORK_CALLED
|
662
|
-
end
|
663
|
-
|
664
|
-
test "returns PID of running process" do
|
665
|
-
assert_equal @worker.to_s.split(":")[1].to_i, @worker.pid
|
666
|
-
end
|
667
|
-
|
668
|
-
test "requeue failed queue" do
|
669
|
-
queue = 'good_job'
|
670
|
-
Resque::Failure.create(:exception => Exception.new, :worker => Resque::Worker.new(queue), :queue => queue, :payload => {'class' => 'GoodJob'})
|
671
|
-
Resque::Failure.create(:exception => Exception.new, :worker => Resque::Worker.new(queue), :queue => 'some_job', :payload => {'class' => 'SomeJob'})
|
672
|
-
Resque::Failure.requeue_queue(queue)
|
673
|
-
assert Resque::Failure.all(0).has_key?('retried_at')
|
674
|
-
assert !Resque::Failure.all(1).has_key?('retried_at')
|
675
|
-
end
|
676
|
-
|
677
|
-
test "remove failed queue" do
|
678
|
-
queue = 'good_job'
|
679
|
-
queue2 = 'some_job'
|
680
|
-
Resque::Failure.create(:exception => Exception.new, :worker => Resque::Worker.new(queue), :queue => queue, :payload => {'class' => 'GoodJob'})
|
681
|
-
Resque::Failure.create(:exception => Exception.new, :worker => Resque::Worker.new(queue2), :queue => queue2, :payload => {'class' => 'SomeJob'})
|
682
|
-
Resque::Failure.create(:exception => Exception.new, :worker => Resque::Worker.new(queue), :queue => queue, :payload => {'class' => 'GoodJob'})
|
683
|
-
Resque::Failure.remove_queue(queue)
|
684
|
-
assert_equal queue2, Resque::Failure.all(0)['queue']
|
685
|
-
assert_equal 1, Resque::Failure.count
|
686
|
-
end
|
687
|
-
|
688
|
-
test "reconnects to redis after fork" do
|
689
|
-
original_connection = Resque.redis.client.connection.instance_variable_get("@sock")
|
690
|
-
@worker.work(0)
|
691
|
-
assert_not_equal original_connection, Resque.redis.client.connection.instance_variable_get("@sock")
|
692
|
-
end
|
693
|
-
|
694
|
-
test "tries to reconnect three times before giving up and the failure does not unregister the parent" do
|
695
|
-
begin
|
696
|
-
class Redis::Client
|
697
|
-
alias_method :original_reconnect, :reconnect
|
698
|
-
|
699
|
-
def reconnect
|
700
|
-
raise Redis::BaseConnectionError
|
701
|
-
end
|
702
|
-
end
|
703
|
-
|
704
|
-
class Resque::Worker
|
705
|
-
alias_method :original_sleep, :sleep
|
706
|
-
|
707
|
-
def sleep(duration = nil)
|
708
|
-
# noop
|
709
|
-
end
|
710
|
-
end
|
711
|
-
|
712
|
-
stdout, stderr = capture_io do
|
713
|
-
Resque.logger = Logger.new($stdout)
|
714
|
-
@worker.work(0)
|
715
|
-
end
|
716
|
-
|
717
|
-
assert_equal 3, stdout.scan(/retrying/).count
|
718
|
-
assert_equal 1, stdout.scan(/quitting/).count
|
719
|
-
assert_equal 0, stdout.scan(/Failed to start worker/).count
|
720
|
-
assert_equal 1, stdout.scan(/Redis::BaseConnectionError: Redis::BaseConnectionError/).count
|
721
|
-
|
722
|
-
ensure
|
723
|
-
class Redis::Client
|
724
|
-
alias_method :reconnect, :original_reconnect
|
725
|
-
end
|
726
|
-
|
727
|
-
class Resque::Worker
|
728
|
-
alias_method :sleep, :original_sleep
|
729
|
-
end
|
730
|
-
end
|
731
|
-
end
|
732
|
-
|
733
|
-
test "will call before_pause before it is paused" do
|
734
|
-
before_pause_called = false
|
735
|
-
captured_worker = nil
|
736
|
-
begin
|
737
|
-
class Redis::Client
|
738
|
-
alias_method :original_reconnect, :reconnect
|
739
|
-
|
740
|
-
def reconnect
|
741
|
-
raise Redis::BaseConnectionError
|
742
|
-
end
|
743
|
-
end
|
744
|
-
|
745
|
-
class Resque::Worker
|
746
|
-
alias_method :original_sleep, :sleep
|
747
|
-
|
748
|
-
def sleep(duration = nil)
|
749
|
-
# noop
|
750
|
-
end
|
751
|
-
end
|
752
|
-
|
753
|
-
class DummyLogger
|
754
|
-
attr_reader :messages
|
755
|
-
|
756
|
-
def initialize
|
757
|
-
@messages = []
|
758
|
-
end
|
759
|
-
|
760
|
-
def info(message); @messages << message; end
|
761
|
-
alias_method :debug, :info
|
762
|
-
alias_method :warn, :info
|
763
|
-
alias_method :error, :info
|
764
|
-
alias_method :fatal, :info
|
765
|
-
end
|
766
|
-
|
767
|
-
Resque.logger = DummyLogger.new
|
768
|
-
begin
|
769
|
-
@worker.work(0)
|
770
|
-
messages = Resque.logger.messages
|
771
|
-
ensure
|
772
|
-
reset_logger
|
773
|
-
end
|
774
|
-
|
775
|
-
assert_equal 3, messages.grep(/retrying/).count
|
776
|
-
assert_equal 1, messages.grep(/quitting/).count
|
777
|
-
ensure
|
778
|
-
class Redis::Client
|
779
|
-
alias_method :reconnect, :original_reconnect
|
780
|
-
end
|
781
|
-
|
782
|
-
class Resque::Worker
|
783
|
-
alias_method :sleep, :original_sleep
|
784
|
-
end
|
785
|
-
end
|
786
|
-
end
|
787
|
-
|
788
|
-
if !defined?(RUBY_ENGINE) || defined?(RUBY_ENGINE) && RUBY_ENGINE != "jruby"
|
789
|
-
test "old signal handling is the default" do
|
790
|
-
rescue_time = nil
|
791
|
-
|
792
|
-
begin
|
793
|
-
class LongRunningJob
|
794
|
-
@queue = :long_running_job
|
795
|
-
|
796
|
-
def self.perform( run_time, rescue_time=nil )
|
797
|
-
Resque.redis.client.reconnect # get its own connection
|
798
|
-
Resque.redis.rpush( 'sigterm-test:start', Process.pid )
|
799
|
-
sleep run_time
|
800
|
-
Resque.redis.rpush( 'sigterm-test:result', 'Finished Normally' )
|
801
|
-
rescue Resque::TermException => e
|
802
|
-
Resque.redis.rpush( 'sigterm-test:result', %Q(Caught SignalException: #{e.inspect}))
|
803
|
-
sleep rescue_time unless rescue_time.nil?
|
804
|
-
ensure
|
805
|
-
puts 'fuuuu'
|
806
|
-
Resque.redis.rpush( 'sigterm-test:final', 'exiting.' )
|
807
|
-
end
|
808
|
-
end
|
809
|
-
|
810
|
-
Resque.enqueue( LongRunningJob, 5, rescue_time )
|
811
|
-
|
812
|
-
worker_pid = Kernel.fork do
|
813
|
-
# ensure we actually fork
|
814
|
-
$TESTING = false
|
815
|
-
# reconnect since we just forked
|
816
|
-
Resque.redis.client.reconnect
|
817
|
-
|
818
|
-
worker = Resque::Worker.new(:long_running_job)
|
819
|
-
|
820
|
-
suppress_warnings do
|
821
|
-
worker.work(0)
|
822
|
-
end
|
823
|
-
exit!
|
824
|
-
end
|
825
|
-
|
826
|
-
# ensure the worker is started
|
827
|
-
start_status = Resque.redis.blpop( 'sigterm-test:start', 5 )
|
828
|
-
assert_not_nil start_status
|
829
|
-
child_pid = start_status[1].to_i
|
830
|
-
assert_operator child_pid, :>, 0
|
831
|
-
|
832
|
-
# send signal to abort the worker
|
833
|
-
Process.kill('TERM', worker_pid)
|
834
|
-
Process.waitpid(worker_pid)
|
835
|
-
|
836
|
-
# wait to see how it all came down
|
837
|
-
result = Resque.redis.blpop( 'sigterm-test:result', 5 )
|
838
|
-
assert_nil result
|
839
|
-
|
840
|
-
# ensure that the child pid is no longer running
|
841
|
-
child_not_running = `ps -p #{child_pid.to_s} -o pid=`.empty?
|
842
|
-
assert child_not_running
|
843
|
-
ensure
|
844
|
-
remaining_keys = Resque.redis.keys('sigterm-test:*') || []
|
845
|
-
Resque.redis.del(*remaining_keys) unless remaining_keys.empty?
|
846
|
-
end
|
847
|
-
end
|
848
|
-
end
|
849
|
-
|
850
|
-
if !defined?(RUBY_ENGINE) || defined?(RUBY_ENGINE) && RUBY_ENGINE != "jruby"
|
851
|
-
[SignalException, Resque::TermException].each do |exception|
|
852
|
-
{
|
853
|
-
'cleanup occurs in allotted time' => nil,
|
854
|
-
'cleanup takes too long' => 2
|
855
|
-
}.each do |scenario,rescue_time|
|
856
|
-
test "SIGTERM when #{scenario} while catching #{exception}" do
|
857
|
-
begin
|
858
|
-
eval("class LongRunningJob; @@exception = #{exception}; end")
|
859
|
-
class LongRunningJob
|
860
|
-
@queue = :long_running_job
|
861
|
-
|
862
|
-
def self.perform( run_time, rescue_time=nil )
|
863
|
-
Resque.redis.client.reconnect # get its own connection
|
864
|
-
Resque.redis.rpush( 'sigterm-test:start', Process.pid )
|
865
|
-
sleep run_time
|
866
|
-
Resque.redis.rpush( 'sigterm-test:result', 'Finished Normally' )
|
867
|
-
rescue @@exception => e
|
868
|
-
Resque.redis.rpush( 'sigterm-test:result', %Q(Caught SignalException: #{e.inspect}))
|
869
|
-
sleep rescue_time unless rescue_time.nil?
|
870
|
-
ensure
|
871
|
-
Resque.redis.rpush( 'sigterm-test:final', 'exiting.' )
|
872
|
-
end
|
873
|
-
end
|
874
|
-
|
875
|
-
Resque.enqueue( LongRunningJob, 5, rescue_time )
|
876
|
-
|
877
|
-
worker_pid = Kernel.fork do
|
878
|
-
# ensure we actually fork
|
879
|
-
$TESTING = false
|
880
|
-
# reconnect since we just forked
|
881
|
-
Resque.redis.client.reconnect
|
882
|
-
|
883
|
-
worker = Resque::Worker.new(:long_running_job)
|
884
|
-
worker.term_timeout = 1
|
885
|
-
worker.term_child = 1
|
886
|
-
|
887
|
-
worker.work(0)
|
888
|
-
exit!
|
889
|
-
end
|
890
|
-
|
891
|
-
# ensure the worker is started
|
892
|
-
start_status = Resque.redis.blpop( 'sigterm-test:start', 5 )
|
893
|
-
assert_not_nil start_status
|
894
|
-
child_pid = start_status[1].to_i
|
895
|
-
assert_operator child_pid, :>, 0
|
896
|
-
|
897
|
-
# send signal to abort the worker
|
898
|
-
Process.kill('TERM', worker_pid)
|
899
|
-
Process.waitpid(worker_pid)
|
900
|
-
|
901
|
-
# wait to see how it all came down
|
902
|
-
result = Resque.redis.blpop( 'sigterm-test:result', 5 )
|
903
|
-
assert_not_nil result
|
904
|
-
assert !result[1].start_with?('Finished Normally'), 'Job Finished normally. Sleep not long enough?'
|
905
|
-
assert result[1].start_with? 'Caught SignalException', 'Signal exception not raised in child.'
|
906
|
-
|
907
|
-
# ensure that the child pid is no longer running
|
908
|
-
child_still_running = !(`ps -p #{child_pid.to_s} -o pid=`).empty?
|
909
|
-
assert !child_still_running
|
910
|
-
|
911
|
-
# see if post-cleanup occurred. This should happen IFF the rescue_time is less than the term_timeout
|
912
|
-
post_cleanup_occurred = Resque.redis.lpop( 'sigterm-test:final' )
|
913
|
-
assert post_cleanup_occurred, 'post cleanup did not occur. SIGKILL sent too early?' if rescue_time.nil?
|
914
|
-
assert !post_cleanup_occurred, 'post cleanup occurred. SIGKILL sent too late?' unless rescue_time.nil?
|
915
|
-
|
916
|
-
ensure
|
917
|
-
remaining_keys = Resque.redis.keys('sigterm-test:*') || []
|
918
|
-
Resque.redis.del(*remaining_keys) unless remaining_keys.empty?
|
919
|
-
end
|
920
|
-
end
|
921
|
-
end
|
922
|
-
end
|
923
|
-
|
924
|
-
test "displays warning when not using term_child" do
|
925
|
-
begin
|
926
|
-
$TESTING = false
|
927
|
-
stdout, stderr = capture_io { @worker.work(0) }
|
928
|
-
|
929
|
-
assert stderr.match(/^WARNING:/)
|
930
|
-
ensure
|
931
|
-
$TESTING = true
|
932
|
-
end
|
933
|
-
end
|
934
|
-
|
935
|
-
test "it does not display warning when using term_child" do
|
936
|
-
@worker.term_child = "1"
|
937
|
-
stdout, stderr = capture_io { @worker.work(0) }
|
938
|
-
|
939
|
-
assert !stderr.match(/^WARNING:/)
|
940
|
-
end
|
941
|
-
|
942
|
-
class SuicidalJob
|
943
|
-
@queue = :jobs
|
944
|
-
|
945
|
-
def self.perform
|
946
|
-
Process.kill('KILL', Process.pid)
|
947
|
-
end
|
948
|
-
|
949
|
-
def self.on_failure_store_exception(exc, *args)
|
950
|
-
@@failure_exception = exc
|
951
|
-
end
|
952
|
-
end
|
953
|
-
|
954
|
-
test "will notify failure hooks when a job is killed by a signal" do
|
955
|
-
begin
|
956
|
-
$TESTING = false
|
957
|
-
Resque.enqueue(SuicidalJob)
|
958
|
-
suppress_warnings do
|
959
|
-
@worker.work(0)
|
960
|
-
end
|
961
|
-
assert_equal Resque::DirtyExit, SuicidalJob.send(:class_variable_get, :@@failure_exception).class
|
962
|
-
ensure
|
963
|
-
$TESTING = true
|
964
|
-
end
|
965
|
-
end
|
966
|
-
end
|
967
|
-
|
968
|
-
test "displays warning when using verbose" do
|
969
|
-
begin
|
970
|
-
$TESTING = false
|
971
|
-
stdout, stderr = capture_io { @worker.verbose }
|
972
|
-
ensure
|
973
|
-
$TESTING = true
|
974
|
-
end
|
975
|
-
$warned_logger_severity_deprecation = false
|
976
|
-
|
977
|
-
assert stderr.match(/WARNING:/)
|
978
|
-
end
|
979
|
-
|
980
|
-
test "displays warning when using verbose=" do
|
981
|
-
begin
|
982
|
-
$TESTING = false
|
983
|
-
stdout, stderr = capture_io { @worker.verbose = true }
|
984
|
-
ensure
|
985
|
-
$TESTING = true
|
986
|
-
end
|
987
|
-
$warned_logger_severity_deprecation = false
|
988
|
-
|
989
|
-
assert stderr.match(/WARNING:/)
|
990
|
-
end
|
991
|
-
|
992
|
-
test "displays warning when using very_verbose" do
|
993
|
-
begin
|
994
|
-
$TESTING = false
|
995
|
-
stdout, stderr = capture_io { @worker.very_verbose }
|
996
|
-
ensure
|
997
|
-
$TESTING = true
|
998
|
-
end
|
999
|
-
$warned_logger_severity_deprecation = false
|
1000
|
-
|
1001
|
-
assert stderr.match(/WARNING:/)
|
1002
|
-
end
|
1003
|
-
|
1004
|
-
test "displays warning when using very_verbose=" do
|
1005
|
-
begin
|
1006
|
-
$TESTING = false
|
1007
|
-
stdout, stderr = capture_io { @worker.very_verbose = true }
|
1008
|
-
ensure
|
1009
|
-
$TESTING = true
|
1010
|
-
end
|
1011
|
-
$warned_logger_severity_deprecation = false
|
1012
|
-
|
1013
|
-
assert stderr.match(/WARNING:/)
|
1014
|
-
end
|
1015
|
-
end
|