resque-loner 1.2.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +92 -0
- data/.simplecov +1 -0
- data/.travis.yml +12 -0
- data/CHANGELOG.markdown +11 -0
- data/Gemfile +4 -5
- data/README.markdown +4 -1
- data/Rakefile +13 -41
- data/lib/resque-ext/job.rb +9 -9
- data/lib/resque-ext/resque.rb +4 -9
- data/lib/resque-loner.rb +2 -1
- data/lib/resque-loner/helpers.rb +22 -9
- data/lib/resque-loner/legacy_helpers.rb +91 -0
- data/lib/resque-loner/unique_job.rb +23 -14
- data/lib/resque-loner/version.rb +1 -1
- data/resque-loner.gemspec +41 -32
- data/spec/loner_spec.rb +73 -51
- data/spec/spec_helper.rb +19 -3
- data/spec/support/redis_instance.rb +133 -0
- data/test/airbrake_test.rb +27 -0
- data/test/dump.rdb +0 -0
- data/test/job_hooks_test.rb +191 -52
- data/test/job_plugins_test.rb +33 -31
- data/test/plugin_test.rb +38 -26
- data/{spec/redis-test.conf → test/redis-test-cluster.conf} +4 -4
- data/test/resque-web_test.rb +23 -17
- data/test/resque_failure_redis_test.rb +23 -0
- data/test/resque_test.rb +56 -37
- data/test/test_helper.rb +58 -29
- data/test/worker_test.rb +311 -55
- metadata +157 -58
- data/init.rb +0 -1
- data/rails/init.rb +0 -1
- data/test/hoptoad_test.rb +0 -25
data/test/test_helper.rb
CHANGED
@@ -1,37 +1,37 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require 'bundler'
|
3
|
-
Bundler.setup(:default, :test)
|
4
|
-
Bundler.require(:default, :test)
|
2
|
+
require 'bundler/setup'
|
5
3
|
|
6
|
-
dir = File.dirname(File.expand_path(__FILE__))
|
7
|
-
$LOAD_PATH.unshift dir + '/../lib'
|
4
|
+
$dir = File.dirname(File.expand_path(__FILE__))
|
5
|
+
$LOAD_PATH.unshift $dir + '/../lib'
|
8
6
|
$TESTING = true
|
9
7
|
require 'test/unit'
|
10
8
|
|
9
|
+
require 'redis/namespace'
|
10
|
+
require 'resque'
|
11
|
+
require 'resque-loner'
|
12
|
+
|
11
13
|
begin
|
12
14
|
require 'leftright'
|
13
15
|
rescue LoadError
|
14
16
|
end
|
15
17
|
|
16
|
-
|
17
18
|
#
|
18
19
|
# make sure we can run redis
|
19
20
|
#
|
20
21
|
|
21
|
-
if !system(
|
22
|
+
if !system('which redis-server')
|
22
23
|
puts '', "** can't find `redis-server` in your path"
|
23
|
-
puts
|
24
|
+
puts '** try running `sudo rake install`'
|
24
25
|
abort ''
|
25
26
|
end
|
26
27
|
|
27
|
-
|
28
28
|
#
|
29
29
|
# start our own redis when the tests start,
|
30
30
|
# kill it when they end
|
31
31
|
#
|
32
32
|
|
33
33
|
at_exit do
|
34
|
-
next if
|
34
|
+
next if $ERROR_INFO
|
35
35
|
|
36
36
|
if defined?(MiniTest)
|
37
37
|
exit_code = MiniTest::Unit.new.run(ARGV)
|
@@ -39,17 +39,26 @@ at_exit do
|
|
39
39
|
exit_code = Test::Unit::AutoRunner.run
|
40
40
|
end
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
Process.kill(
|
42
|
+
processes = `ps -A -o pid,command | grep [r]edis-test`.split("\n")
|
43
|
+
pids = processes.map { |process| process.split(' ')[0] }
|
44
|
+
puts 'Killing test redis server...'
|
45
|
+
pids.each { |pid| Process.kill('TERM', pid.to_i) }
|
46
|
+
system("rm -f #{$dir}/dump.rdb #{$dir}/dump-cluster.rdb")
|
46
47
|
exit exit_code
|
47
48
|
end
|
48
49
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
53
62
|
|
54
63
|
##
|
55
64
|
# test/spec/mini 3
|
@@ -61,13 +70,17 @@ def context(*args, &block)
|
|
61
70
|
require 'test/unit'
|
62
71
|
klass = Class.new(defined?(ActiveSupport::TestCase) ? ActiveSupport::TestCase : Test::Unit::TestCase) do
|
63
72
|
def self.test(name, &block)
|
64
|
-
define_method("test_#{name.gsub(/\W/,'_')}", &block) if block
|
73
|
+
define_method("test_#{name.gsub(/\W/, '_')}", &block) if block
|
65
74
|
end
|
66
75
|
def self.xtest(*args) end
|
67
|
-
def self.setup(&block)
|
68
|
-
|
76
|
+
def self.setup(&block)
|
77
|
+
define_method(:setup, &block)
|
78
|
+
end
|
79
|
+
def self.teardown(&block)
|
80
|
+
define_method(:teardown, &block)
|
81
|
+
end
|
69
82
|
end
|
70
|
-
(class << klass; self end).send(:define_method, :name) { name.gsub(/\W/,'_') }
|
83
|
+
(class << klass; self end).send(:define_method, :name) { name.gsub(/\W/, '_') }
|
71
84
|
klass.class_eval &block
|
72
85
|
# XXX: In 1.8.x, not all tests will run unless anonymous classes are kept in scope.
|
73
86
|
($test_classes ||= []) << klass
|
@@ -104,7 +117,7 @@ end
|
|
104
117
|
|
105
118
|
class BadJob
|
106
119
|
def self.perform
|
107
|
-
|
120
|
+
fail 'Bad job!'
|
108
121
|
end
|
109
122
|
end
|
110
123
|
|
@@ -116,13 +129,13 @@ end
|
|
116
129
|
|
117
130
|
class BadJobWithSyntaxError
|
118
131
|
def self.perform
|
119
|
-
|
132
|
+
fail SyntaxError, 'Extra Bad job!'
|
120
133
|
end
|
121
134
|
end
|
122
135
|
|
123
136
|
class BadFailureBackend < Resque::Failure::Base
|
124
137
|
def save
|
125
|
-
|
138
|
+
fail Exception.new('Failure backend error')
|
126
139
|
end
|
127
140
|
end
|
128
141
|
|
@@ -134,15 +147,31 @@ ensure
|
|
134
147
|
Resque::Failure.backend = previous_backend
|
135
148
|
end
|
136
149
|
|
150
|
+
require 'time'
|
151
|
+
|
137
152
|
class Time
|
138
153
|
# Thanks, Timecop
|
139
154
|
class << self
|
155
|
+
attr_accessor :fake_time
|
156
|
+
|
140
157
|
alias_method :now_without_mock_time, :now
|
141
158
|
|
142
|
-
def
|
143
|
-
|
159
|
+
def now
|
160
|
+
fake_time || now_without_mock_time
|
144
161
|
end
|
145
|
-
|
146
|
-
alias_method :now, :now_with_mock_time
|
147
162
|
end
|
163
|
+
|
164
|
+
self.fake_time = nil
|
165
|
+
end
|
166
|
+
|
167
|
+
def capture_stderr
|
168
|
+
# The output stream must be an IO-like object. In this case we capture it in
|
169
|
+
# an in-memory IO object so we can return the string value. You can assign any
|
170
|
+
# IO object here.
|
171
|
+
previous_stderr, $stderr = $stderr, StringIO.new
|
172
|
+
yield
|
173
|
+
$stderr.string
|
174
|
+
ensure
|
175
|
+
# Restore the previous value of stderr (typically equal to STDERR).
|
176
|
+
$stderr = previous_stderr
|
148
177
|
end
|
data/test/worker_test.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
context
|
3
|
+
context 'Resque::Worker' do
|
4
4
|
setup do
|
5
5
|
Resque.redis.flushall
|
6
6
|
|
@@ -12,34 +12,75 @@ context "Resque::Worker" do
|
|
12
12
|
Resque::Job.create(:jobs, SomeJob, 20, '/tmp')
|
13
13
|
end
|
14
14
|
|
15
|
-
test
|
15
|
+
test 'can fail jobs' do
|
16
16
|
Resque::Job.create(:jobs, BadJob)
|
17
17
|
@worker.work(0)
|
18
18
|
assert_equal 1, Resque::Failure.count
|
19
19
|
end
|
20
20
|
|
21
|
-
test
|
21
|
+
test 'failed jobs report exception and message' do
|
22
22
|
Resque::Job.create(:jobs, BadJobWithSyntaxError)
|
23
23
|
@worker.work(0)
|
24
24
|
assert_equal('SyntaxError', Resque::Failure.all['exception'])
|
25
25
|
assert_equal('Extra Bad job!', Resque::Failure.all['error'])
|
26
26
|
end
|
27
27
|
|
28
|
-
test
|
28
|
+
test 'does not allow exceptions from failure backend to escape' do
|
29
29
|
job = Resque::Job.new(:jobs, {})
|
30
30
|
with_failure_backend BadFailureBackend do
|
31
31
|
@worker.perform job
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
test
|
36
|
-
job = Resque::Job.new(:jobs,
|
35
|
+
test 'fails uncompleted jobs on exit' do
|
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
|
-
|
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
|
+
|
60
|
+
class ::SimpleFailingJob
|
61
|
+
@@exception_count = 0
|
62
|
+
|
63
|
+
def self.on_failure_record_failure(exception, *job_args)
|
64
|
+
@@exception_count += 1
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.exception_count
|
68
|
+
@@exception_count
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.perform
|
72
|
+
fail Exception.new
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
test 'only calls failure hook once on exception' do
|
77
|
+
job = Resque::Job.new(:jobs, 'class' => 'SimpleFailingJob', 'args' => '')
|
78
|
+
@worker.perform(job)
|
79
|
+
assert_equal 1, Resque::Failure.count
|
80
|
+
assert_equal 1, SimpleFailingJob.exception_count
|
81
|
+
end
|
82
|
+
|
83
|
+
test 'can peek at failed jobs' do
|
43
84
|
10.times { Resque::Job.create(:jobs, BadJob) }
|
44
85
|
@worker.work(0)
|
45
86
|
assert_equal 10, Resque::Failure.count
|
@@ -47,7 +88,7 @@ context "Resque::Worker" do
|
|
47
88
|
assert_equal 10, Resque::Failure.all(0, 20).size
|
48
89
|
end
|
49
90
|
|
50
|
-
test
|
91
|
+
test 'can clear failed jobs' do
|
51
92
|
Resque::Job.create(:jobs, BadJob)
|
52
93
|
@worker.work(0)
|
53
94
|
assert_equal 1, Resque::Failure.count
|
@@ -55,7 +96,7 @@ context "Resque::Worker" do
|
|
55
96
|
assert_equal 0, Resque::Failure.count
|
56
97
|
end
|
57
98
|
|
58
|
-
test
|
99
|
+
test 'catches exceptional jobs' do
|
59
100
|
Resque::Job.create(:jobs, BadJob)
|
60
101
|
Resque::Job.create(:jobs, BadJob)
|
61
102
|
@worker.process
|
@@ -64,13 +105,13 @@ context "Resque::Worker" do
|
|
64
105
|
assert_equal 2, Resque::Failure.count
|
65
106
|
end
|
66
107
|
|
67
|
-
test
|
68
|
-
queues =
|
108
|
+
test 'strips whitespace from queue names' do
|
109
|
+
queues = 'critical, high, low'.split(',')
|
69
110
|
worker = Resque::Worker.new(*queues)
|
70
111
|
assert_equal %w( critical high low ), worker.queues
|
71
112
|
end
|
72
113
|
|
73
|
-
test
|
114
|
+
test 'can work on multiple queues' do
|
74
115
|
Resque::Job.create(:high, GoodJob)
|
75
116
|
Resque::Job.create(:critical, GoodJob)
|
76
117
|
|
@@ -84,25 +125,55 @@ context "Resque::Worker" do
|
|
84
125
|
assert_equal 0, Resque.size(:high)
|
85
126
|
end
|
86
127
|
|
87
|
-
test
|
128
|
+
test 'can work on all queues' do
|
129
|
+
Resque::Job.create(:high, GoodJob)
|
130
|
+
Resque::Job.create(:critical, GoodJob)
|
131
|
+
Resque::Job.create(:blahblah, GoodJob)
|
132
|
+
|
133
|
+
worker = Resque::Worker.new('*')
|
134
|
+
|
135
|
+
worker.work(0)
|
136
|
+
assert_equal 0, Resque.size(:high)
|
137
|
+
assert_equal 0, Resque.size(:critical)
|
138
|
+
assert_equal 0, Resque.size(:blahblah)
|
139
|
+
end
|
140
|
+
|
141
|
+
test 'can work with wildcard at the end of the list' do
|
88
142
|
Resque::Job.create(:high, GoodJob)
|
89
143
|
Resque::Job.create(:critical, GoodJob)
|
90
144
|
Resque::Job.create(:blahblah, GoodJob)
|
145
|
+
Resque::Job.create(:beer, GoodJob)
|
91
146
|
|
92
|
-
worker = Resque::Worker.new(
|
147
|
+
worker = Resque::Worker.new(:critical, :high, '*')
|
93
148
|
|
94
149
|
worker.work(0)
|
95
150
|
assert_equal 0, Resque.size(:high)
|
96
151
|
assert_equal 0, Resque.size(:critical)
|
97
152
|
assert_equal 0, Resque.size(:blahblah)
|
153
|
+
assert_equal 0, Resque.size(:beer)
|
98
154
|
end
|
99
155
|
|
100
|
-
test
|
156
|
+
test 'can work with wildcard at the middle of the list' do
|
101
157
|
Resque::Job.create(:high, GoodJob)
|
102
158
|
Resque::Job.create(:critical, GoodJob)
|
103
159
|
Resque::Job.create(:blahblah, GoodJob)
|
160
|
+
Resque::Job.create(:beer, GoodJob)
|
161
|
+
|
162
|
+
worker = Resque::Worker.new(:critical, '*', :high)
|
104
163
|
|
105
|
-
worker
|
164
|
+
worker.work(0)
|
165
|
+
assert_equal 0, Resque.size(:high)
|
166
|
+
assert_equal 0, Resque.size(:critical)
|
167
|
+
assert_equal 0, Resque.size(:blahblah)
|
168
|
+
assert_equal 0, Resque.size(:beer)
|
169
|
+
end
|
170
|
+
|
171
|
+
test 'processes * queues in alphabetical order' do
|
172
|
+
Resque::Job.create(:high, GoodJob)
|
173
|
+
Resque::Job.create(:critical, GoodJob)
|
174
|
+
Resque::Job.create(:blahblah, GoodJob)
|
175
|
+
|
176
|
+
worker = Resque::Worker.new('*')
|
106
177
|
processed_queues = []
|
107
178
|
|
108
179
|
worker.work(0) do |job|
|
@@ -112,17 +183,17 @@ context "Resque::Worker" do
|
|
112
183
|
assert_equal %w( jobs high critical blahblah ).sort, processed_queues
|
113
184
|
end
|
114
185
|
|
115
|
-
test
|
116
|
-
assert_equal "#{`hostname`.chomp}:#{
|
186
|
+
test 'has a unique id' do
|
187
|
+
assert_equal "#{`hostname`.chomp}:#{$PROCESS_ID}:jobs", @worker.to_s
|
117
188
|
end
|
118
189
|
|
119
|
-
test
|
190
|
+
test 'complains if no queues are given' do
|
120
191
|
assert_raise Resque::NoQueueError do
|
121
192
|
Resque::Worker.new
|
122
193
|
end
|
123
194
|
end
|
124
195
|
|
125
|
-
test
|
196
|
+
test 'fails if a job class has no `perform` method' do
|
126
197
|
worker = Resque::Worker.new(:perform_less)
|
127
198
|
Resque::Job.create(:perform_less, Object)
|
128
199
|
|
@@ -145,7 +216,7 @@ context "Resque::Worker" do
|
|
145
216
|
assert_equal [], Resque.workers
|
146
217
|
end
|
147
218
|
|
148
|
-
test
|
219
|
+
test 'removes worker with stringified id' do
|
149
220
|
@worker.work(0) do
|
150
221
|
worker_id = Resque.workers[0].to_s
|
151
222
|
Resque.remove_worker(worker_id)
|
@@ -153,38 +224,38 @@ context "Resque::Worker" do
|
|
153
224
|
end
|
154
225
|
end
|
155
226
|
|
156
|
-
test
|
227
|
+
test 'records what it is working on' do
|
157
228
|
@worker.work(0) do
|
158
229
|
task = @worker.job
|
159
|
-
assert_equal({
|
230
|
+
assert_equal({ 'args' => [20, '/tmp'], 'class' => 'SomeJob' }, task['payload'])
|
160
231
|
assert task['run_at']
|
161
232
|
assert_equal 'jobs', task['queue']
|
162
233
|
end
|
163
234
|
end
|
164
235
|
|
165
|
-
test
|
236
|
+
test 'clears its status when not working on anything' do
|
166
237
|
@worker.work(0)
|
167
238
|
assert_equal Hash.new, @worker.job
|
168
239
|
end
|
169
240
|
|
170
|
-
test
|
241
|
+
test 'knows when it is working' do
|
171
242
|
@worker.work(0) do
|
172
243
|
assert @worker.working?
|
173
244
|
end
|
174
245
|
end
|
175
246
|
|
176
|
-
test
|
247
|
+
test 'knows when it is idle' do
|
177
248
|
@worker.work(0)
|
178
249
|
assert @worker.idle?
|
179
250
|
end
|
180
251
|
|
181
|
-
test
|
252
|
+
test 'knows who is working' do
|
182
253
|
@worker.work(0) do
|
183
254
|
assert_equal [@worker], Resque.working
|
184
255
|
end
|
185
256
|
end
|
186
257
|
|
187
|
-
test
|
258
|
+
test 'keeps track of how many jobs it has processed' do
|
188
259
|
Resque::Job.create(:jobs, BadJob)
|
189
260
|
Resque::Job.create(:jobs, BadJob)
|
190
261
|
|
@@ -195,7 +266,7 @@ context "Resque::Worker" do
|
|
195
266
|
assert_equal 3, @worker.processed
|
196
267
|
end
|
197
268
|
|
198
|
-
test
|
269
|
+
test 'keeps track of how many failures it has seen' do
|
199
270
|
Resque::Job.create(:jobs, BadJob)
|
200
271
|
Resque::Job.create(:jobs, BadJob)
|
201
272
|
|
@@ -206,34 +277,34 @@ context "Resque::Worker" do
|
|
206
277
|
assert_equal 2, @worker.failed
|
207
278
|
end
|
208
279
|
|
209
|
-
test
|
280
|
+
test 'stats are erased when the worker goes away' do
|
210
281
|
@worker.work(0)
|
211
282
|
assert_equal 0, @worker.processed
|
212
283
|
assert_equal 0, @worker.failed
|
213
284
|
end
|
214
285
|
|
215
|
-
test
|
286
|
+
test 'knows when it started' do
|
216
287
|
time = Time.now
|
217
288
|
@worker.work(0) do
|
218
|
-
|
289
|
+
assert Time.parse(@worker.started) - time < 0.1
|
219
290
|
end
|
220
291
|
end
|
221
292
|
|
222
|
-
test
|
293
|
+
test 'knows whether it exists or not' do
|
223
294
|
@worker.work(0) do
|
224
295
|
assert Resque::Worker.exists?(@worker)
|
225
296
|
assert !Resque::Worker.exists?('blah-blah')
|
226
297
|
end
|
227
298
|
end
|
228
299
|
|
229
|
-
test
|
300
|
+
test 'sets $0 while working' do
|
230
301
|
@worker.work(0) do
|
231
302
|
ver = Resque::Version
|
232
|
-
assert_equal "resque-#{ver}: Processing jobs since #{Time.now.to_i}", $
|
303
|
+
assert_equal "resque-#{ver}: Processing jobs since #{Time.now.to_i}", $PROGRAM_NAME
|
233
304
|
end
|
234
305
|
end
|
235
306
|
|
236
|
-
test
|
307
|
+
test 'can be found' do
|
237
308
|
@worker.work(0) do
|
238
309
|
found = Resque::Worker.find(@worker.to_s)
|
239
310
|
assert_equal @worker.to_s, found.to_s
|
@@ -249,7 +320,7 @@ context "Resque::Worker" do
|
|
249
320
|
end
|
250
321
|
end
|
251
322
|
|
252
|
-
test
|
323
|
+
test 'cleans up dead worker info on start (crash recovery)' do
|
253
324
|
# first we fake out two dead workers
|
254
325
|
workerA = Resque::Worker.new(:jobs)
|
255
326
|
workerA.instance_variable_set(:@to_s, "#{`hostname`.chomp}:1:jobs")
|
@@ -267,15 +338,20 @@ context "Resque::Worker" do
|
|
267
338
|
end
|
268
339
|
end
|
269
340
|
|
270
|
-
test
|
341
|
+
test 'worker_pids returns pids' do
|
342
|
+
known_workers = @worker.worker_pids
|
343
|
+
assert !known_workers.empty?
|
344
|
+
end
|
345
|
+
|
346
|
+
test 'Processed jobs count' do
|
271
347
|
@worker.work(0)
|
272
348
|
assert_equal 1, Resque.info[:processed]
|
273
349
|
end
|
274
350
|
|
275
|
-
test
|
351
|
+
test 'Will call a before_first_fork hook only once' do
|
276
352
|
Resque.redis.flushall
|
277
353
|
$BEFORE_FORK_CALLED = 0
|
278
|
-
Resque.before_first_fork =
|
354
|
+
Resque.before_first_fork = proc { $BEFORE_FORK_CALLED += 1 }
|
279
355
|
workerA = Resque::Worker.new(:jobs)
|
280
356
|
Resque::Job.create(:jobs, SomeJob, 20, '/tmp')
|
281
357
|
|
@@ -289,10 +365,10 @@ context "Resque::Worker" do
|
|
289
365
|
# assert_equal 1, $BEFORE_FORK_CALLED
|
290
366
|
end
|
291
367
|
|
292
|
-
test
|
368
|
+
test 'Will call a before_fork hook before forking' do
|
293
369
|
Resque.redis.flushall
|
294
370
|
$BEFORE_FORK_CALLED = false
|
295
|
-
Resque.before_fork =
|
371
|
+
Resque.before_fork = proc { $BEFORE_FORK_CALLED = true }
|
296
372
|
workerA = Resque::Worker.new(:jobs)
|
297
373
|
|
298
374
|
assert !$BEFORE_FORK_CALLED
|
@@ -301,23 +377,29 @@ context "Resque::Worker" do
|
|
301
377
|
assert $BEFORE_FORK_CALLED
|
302
378
|
end
|
303
379
|
|
304
|
-
test
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
singleton.send :define_method, :puts, lambda { |thing| $last_puts = thing }
|
380
|
+
test 'very verbose works in the afternoon' do
|
381
|
+
begin
|
382
|
+
require 'time'
|
383
|
+
last_puts = ''
|
384
|
+
Time.fake_time = Time.parse('15:44:33 2011-03-02')
|
310
385
|
|
311
|
-
|
312
|
-
|
386
|
+
@worker.extend(Module.new do
|
387
|
+
define_method(:puts) { |thing| last_puts = thing }
|
388
|
+
end)
|
313
389
|
|
314
|
-
|
390
|
+
@worker.very_verbose = true
|
391
|
+
@worker.log('some log text')
|
392
|
+
|
393
|
+
assert_match /\*\* \[15:44:33 2011-03-02\] \d+: some log text/, last_puts
|
394
|
+
ensure
|
395
|
+
Time.fake_time = nil
|
396
|
+
end
|
315
397
|
end
|
316
398
|
|
317
|
-
test
|
399
|
+
test 'Will call an after_fork hook after forking' do
|
318
400
|
Resque.redis.flushall
|
319
401
|
$AFTER_FORK_CALLED = false
|
320
|
-
Resque.after_fork =
|
402
|
+
Resque.after_fork = proc { $AFTER_FORK_CALLED = true }
|
321
403
|
workerA = Resque::Worker.new(:jobs)
|
322
404
|
|
323
405
|
assert !$AFTER_FORK_CALLED
|
@@ -326,7 +408,181 @@ context "Resque::Worker" do
|
|
326
408
|
assert $AFTER_FORK_CALLED
|
327
409
|
end
|
328
410
|
|
329
|
-
test
|
330
|
-
assert_equal @worker.to_s.split(
|
411
|
+
test 'returns PID of running process' do
|
412
|
+
assert_equal @worker.to_s.split(':')[1].to_i, @worker.pid
|
413
|
+
end
|
414
|
+
|
415
|
+
test 'requeue failed queue' do
|
416
|
+
queue = 'good_job'
|
417
|
+
Resque::Failure.create(exception: Exception.new, worker: Resque::Worker.new(queue), queue: queue, payload: { 'class' => 'GoodJob' })
|
418
|
+
Resque::Failure.create(exception: Exception.new, worker: Resque::Worker.new(queue), queue: 'some_job', payload: { 'class' => 'SomeJob' })
|
419
|
+
Resque::Failure.requeue_queue(queue)
|
420
|
+
assert Resque::Failure.all(0).key?('retried_at')
|
421
|
+
assert !Resque::Failure.all(1).key?('retried_at')
|
422
|
+
end
|
423
|
+
|
424
|
+
test 'remove failed queue' do
|
425
|
+
queue = 'good_job'
|
426
|
+
queue2 = 'some_job'
|
427
|
+
Resque::Failure.create(exception: Exception.new, worker: Resque::Worker.new(queue), queue: queue, payload: { 'class' => 'GoodJob' })
|
428
|
+
Resque::Failure.create(exception: Exception.new, worker: Resque::Worker.new(queue2), queue: queue2, payload: { 'class' => 'SomeJob' })
|
429
|
+
Resque::Failure.create(exception: Exception.new, worker: Resque::Worker.new(queue), queue: queue, payload: { 'class' => 'GoodJob' })
|
430
|
+
Resque::Failure.remove_queue(queue)
|
431
|
+
assert_equal queue2, Resque::Failure.all(0)['queue']
|
432
|
+
assert_equal 1, Resque::Failure.count
|
433
|
+
end
|
434
|
+
|
435
|
+
test 'reconnects to redis after fork' do
|
436
|
+
original_connection = Resque.redis.client.connection.instance_variable_get('@sock')
|
437
|
+
@worker.work(0)
|
438
|
+
assert_not_equal original_connection, Resque.redis.client.connection.instance_variable_get('@sock')
|
439
|
+
end
|
440
|
+
|
441
|
+
if !defined?(RUBY_ENGINE) || defined?(RUBY_ENGINE) && RUBY_ENGINE != 'jruby'
|
442
|
+
test 'old signal handling is the default' do
|
443
|
+
rescue_time = nil
|
444
|
+
|
445
|
+
begin
|
446
|
+
class LongRunningJob
|
447
|
+
@queue = :long_running_job
|
448
|
+
|
449
|
+
def self.perform(run_time, rescue_time = nil)
|
450
|
+
Resque.redis.client.reconnect # get its own connection
|
451
|
+
Resque.redis.rpush('sigterm-test:start', Process.pid)
|
452
|
+
sleep run_time
|
453
|
+
Resque.redis.rpush('sigterm-test:result', 'Finished Normally')
|
454
|
+
rescue Resque::TermException => e
|
455
|
+
Resque.redis.rpush('sigterm-test:result', %Q(Caught SignalException: #{e.inspect}))
|
456
|
+
sleep rescue_time unless rescue_time.nil?
|
457
|
+
ensure
|
458
|
+
puts 'fuuuu'
|
459
|
+
Resque.redis.rpush('sigterm-test:final', 'exiting.')
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
Resque.enqueue(LongRunningJob, 5, rescue_time)
|
464
|
+
|
465
|
+
worker_pid = Kernel.fork do
|
466
|
+
# ensure we actually fork
|
467
|
+
$TESTING = false
|
468
|
+
# reconnect since we just forked
|
469
|
+
Resque.redis.client.reconnect
|
470
|
+
|
471
|
+
worker = Resque::Worker.new(:long_running_job)
|
472
|
+
|
473
|
+
worker.work(0)
|
474
|
+
exit!
|
475
|
+
end
|
476
|
+
|
477
|
+
# ensure the worker is started
|
478
|
+
start_status = Resque.redis.blpop('sigterm-test:start', 5)
|
479
|
+
assert_not_nil start_status
|
480
|
+
child_pid = start_status[1].to_i
|
481
|
+
assert_operator child_pid, :>, 0
|
482
|
+
|
483
|
+
# send signal to abort the worker
|
484
|
+
Process.kill('TERM', worker_pid)
|
485
|
+
Process.waitpid(worker_pid)
|
486
|
+
|
487
|
+
# wait to see how it all came down
|
488
|
+
result = Resque.redis.blpop('sigterm-test:result', 5)
|
489
|
+
assert_nil result
|
490
|
+
|
491
|
+
# ensure that the child pid is no longer running
|
492
|
+
child_still_running = !(`ps -p #{child_pid.to_s} -o pid=`).empty?
|
493
|
+
assert !child_still_running
|
494
|
+
ensure
|
495
|
+
remaining_keys = Resque.redis.keys('sigterm-test:*') || []
|
496
|
+
Resque.redis.del(*remaining_keys) unless remaining_keys.empty?
|
497
|
+
end
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
if !defined?(RUBY_ENGINE) || defined?(RUBY_ENGINE) && RUBY_ENGINE != 'jruby'
|
502
|
+
[SignalException, Resque::TermException].each do |exception|
|
503
|
+
{
|
504
|
+
'cleanup occurs in allotted time' => nil,
|
505
|
+
'cleanup takes too long' => 2
|
506
|
+
}.each do |scenario, rescue_time|
|
507
|
+
test "SIGTERM when #{scenario} while catching #{exception}" do
|
508
|
+
begin
|
509
|
+
eval("class LongRunningJob; @@exception = #{exception}; end")
|
510
|
+
class LongRunningJob
|
511
|
+
@queue = :long_running_job
|
512
|
+
|
513
|
+
def self.perform(run_time, rescue_time = nil)
|
514
|
+
Resque.redis.client.reconnect # get its own connection
|
515
|
+
Resque.redis.rpush('sigterm-test:start', Process.pid)
|
516
|
+
sleep run_time
|
517
|
+
Resque.redis.rpush('sigterm-test:result', 'Finished Normally')
|
518
|
+
rescue @@exception => e
|
519
|
+
Resque.redis.rpush('sigterm-test:result', %Q(Caught SignalException: #{e.inspect}))
|
520
|
+
sleep rescue_time unless rescue_time.nil?
|
521
|
+
ensure
|
522
|
+
Resque.redis.rpush('sigterm-test:final', 'exiting.')
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
Resque.enqueue(LongRunningJob, 5, rescue_time)
|
527
|
+
|
528
|
+
worker_pid = Kernel.fork do
|
529
|
+
# ensure we actually fork
|
530
|
+
$TESTING = false
|
531
|
+
# reconnect since we just forked
|
532
|
+
Resque.redis.client.reconnect
|
533
|
+
|
534
|
+
worker = Resque::Worker.new(:long_running_job)
|
535
|
+
worker.term_timeout = 1
|
536
|
+
worker.term_child = 1
|
537
|
+
|
538
|
+
worker.work(0)
|
539
|
+
exit!
|
540
|
+
end
|
541
|
+
|
542
|
+
# ensure the worker is started
|
543
|
+
start_status = Resque.redis.blpop('sigterm-test:start', 5)
|
544
|
+
assert_not_nil start_status
|
545
|
+
child_pid = start_status[1].to_i
|
546
|
+
assert_operator child_pid, :>, 0
|
547
|
+
|
548
|
+
# send signal to abort the worker
|
549
|
+
Process.kill('TERM', worker_pid)
|
550
|
+
Process.waitpid(worker_pid)
|
551
|
+
|
552
|
+
# wait to see how it all came down
|
553
|
+
result = Resque.redis.blpop('sigterm-test:result', 5)
|
554
|
+
assert_not_nil result
|
555
|
+
assert !result[1].start_with?('Finished Normally'), 'Job Finished normally. Sleep not long enough?'
|
556
|
+
assert result[1].start_with? 'Caught SignalException', 'Signal exception not raised in child.'
|
557
|
+
|
558
|
+
# ensure that the child pid is no longer running
|
559
|
+
child_still_running = !(`ps -p #{child_pid.to_s} -o pid=`).empty?
|
560
|
+
assert !child_still_running
|
561
|
+
|
562
|
+
# see if post-cleanup occurred. This should happen IFF the rescue_time is less than the term_timeout
|
563
|
+
post_cleanup_occurred = Resque.redis.lpop('sigterm-test:final')
|
564
|
+
assert post_cleanup_occurred, 'post cleanup did not occur. SIGKILL sent too early?' if rescue_time.nil?
|
565
|
+
assert !post_cleanup_occurred, 'post cleanup occurred. SIGKILL sent too late?' unless rescue_time.nil?
|
566
|
+
|
567
|
+
ensure
|
568
|
+
remaining_keys = Resque.redis.keys('sigterm-test:*') || []
|
569
|
+
Resque.redis.del(*remaining_keys) unless remaining_keys.empty?
|
570
|
+
end
|
571
|
+
end
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
575
|
+
test 'displays warning when not using term_child' do
|
576
|
+
stderr = capture_stderr { @worker.work(0) }
|
577
|
+
|
578
|
+
assert stderr.match(/^WARNING:/)
|
579
|
+
end
|
580
|
+
|
581
|
+
test 'it does not display warning when using term_child' do
|
582
|
+
@worker.term_child = '1'
|
583
|
+
stderr = capture_stderr { @worker.work(0) }
|
584
|
+
|
585
|
+
assert !stderr.match(/^WARNING:/)
|
586
|
+
end
|
331
587
|
end
|
332
588
|
end
|