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.

Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/HISTORY.md +29 -16
  3. data/README.markdown +60 -6
  4. data/Rakefile +4 -17
  5. data/bin/resque-web +4 -0
  6. data/lib/resque.rb +116 -16
  7. data/lib/resque/errors.rb +1 -0
  8. data/lib/resque/failure.rb +11 -5
  9. data/lib/resque/failure/multiple.rb +6 -1
  10. data/lib/resque/failure/redis.rb +13 -4
  11. data/lib/resque/failure/redis_multi_queue.rb +14 -6
  12. data/lib/resque/helpers.rb +5 -64
  13. data/lib/resque/job.rb +25 -79
  14. data/lib/resque/logging.rb +1 -1
  15. data/lib/resque/plugin.rb +22 -10
  16. data/lib/resque/server.rb +35 -7
  17. data/lib/resque/server/helpers.rb +1 -1
  18. data/lib/resque/server/views/failed.erb +1 -1
  19. data/lib/resque/server/views/failed_job.erb +3 -3
  20. data/lib/resque/server/views/failed_queues_overview.erb +3 -3
  21. data/lib/resque/server/views/workers.erb +2 -0
  22. data/lib/resque/server/views/working.erb +4 -5
  23. data/lib/resque/tasks.rb +7 -25
  24. data/lib/resque/vendor/utf8_util/utf8_util_19.rb +1 -0
  25. data/lib/resque/version.rb +1 -1
  26. data/lib/resque/worker.rb +225 -116
  27. metadata +68 -90
  28. data/test/airbrake_test.rb +0 -27
  29. data/test/dump.rdb +0 -0
  30. data/test/failure_base_test.rb +0 -15
  31. data/test/job_hooks_test.rb +0 -464
  32. data/test/job_plugins_test.rb +0 -230
  33. data/test/logging_test.rb +0 -24
  34. data/test/plugin_test.rb +0 -116
  35. data/test/redis-test-cluster.conf +0 -115
  36. data/test/redis-test.conf +0 -115
  37. data/test/resque-web_test.rb +0 -59
  38. data/test/resque_failure_redis_test.rb +0 -19
  39. data/test/resque_hook_test.rb +0 -165
  40. data/test/resque_test.rb +0 -278
  41. data/test/test_helper.rb +0 -198
  42. data/test/worker_test.rb +0 -1015
@@ -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
@@ -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