2014-08-29 08:52:27 -04:00
|
|
|
|
# encoding: utf-8
|
2014-12-30 15:54:58 -05:00
|
|
|
|
require_relative 'helper'
|
2012-05-25 23:21:42 -04:00
|
|
|
|
require 'sidekiq/scheduled'
|
2012-03-17 21:22:56 -04:00
|
|
|
|
require 'sidekiq/middleware/server/retry_jobs'
|
|
|
|
|
|
2013-09-22 17:38:33 -04:00
|
|
|
|
class TestRetry < Sidekiq::Test
|
2012-03-17 21:22:56 -04:00
|
|
|
|
describe 'middleware' do
|
2015-09-22 15:48:43 -04:00
|
|
|
|
class SomeWorker
|
|
|
|
|
include Sidekiq::Worker
|
|
|
|
|
end
|
|
|
|
|
|
2012-03-17 21:22:56 -04:00
|
|
|
|
before do
|
2015-09-22 15:48:43 -04:00
|
|
|
|
Sidekiq.redis {|c| c.flushdb }
|
|
|
|
|
end
|
2012-03-17 21:22:56 -04:00
|
|
|
|
|
2015-09-22 15:48:43 -04:00
|
|
|
|
def worker
|
|
|
|
|
@worker ||= SomeWorker.new
|
2012-03-17 21:22:56 -04:00
|
|
|
|
end
|
|
|
|
|
|
2015-09-22 15:48:43 -04:00
|
|
|
|
def handler(options={})
|
|
|
|
|
@handler ||= Sidekiq::Middleware::Server::RetryJobs.new(options)
|
2014-05-17 23:47:58 -04:00
|
|
|
|
end
|
|
|
|
|
|
2015-09-22 15:48:43 -04:00
|
|
|
|
def job(options={})
|
|
|
|
|
@job ||= { 'class' => 'Bob', 'args' => [1,2,'foo'], 'retry' => true }.merge(options)
|
2013-06-25 11:07:45 -04:00
|
|
|
|
end
|
|
|
|
|
|
2012-04-01 22:53:45 -04:00
|
|
|
|
it 'allows disabling retry' do
|
|
|
|
|
assert_raises RuntimeError do
|
2015-09-22 15:48:43 -04:00
|
|
|
|
handler.call(worker, job('retry' => false), 'default') do
|
2012-04-01 22:53:45 -04:00
|
|
|
|
raise "kerblammo!"
|
|
|
|
|
end
|
|
|
|
|
end
|
2015-09-22 15:48:43 -04:00
|
|
|
|
assert_equal 0, Sidekiq::RetrySet.new.size
|
2012-04-01 22:53:45 -04:00
|
|
|
|
end
|
|
|
|
|
|
2012-11-10 00:18:02 -05:00
|
|
|
|
it 'allows a numeric retry' do
|
|
|
|
|
assert_raises RuntimeError do
|
2015-09-22 15:48:43 -04:00
|
|
|
|
handler.call(worker, job('retry' => 2), 'default') do
|
2012-11-10 00:18:02 -05:00
|
|
|
|
raise "kerblammo!"
|
|
|
|
|
end
|
|
|
|
|
end
|
2015-09-22 15:48:43 -04:00
|
|
|
|
assert_equal 1, Sidekiq::RetrySet.new.size
|
|
|
|
|
assert_equal 0, Sidekiq::DeadSet.new.size
|
2012-11-10 00:18:02 -05:00
|
|
|
|
end
|
|
|
|
|
|
2015-07-10 06:45:18 -04:00
|
|
|
|
it 'allows 0 retry => no retry and dead queue' do
|
|
|
|
|
assert_raises RuntimeError do
|
2015-09-22 15:48:43 -04:00
|
|
|
|
handler.call(worker, job('retry' => 0), 'default') do
|
2015-07-10 06:45:18 -04:00
|
|
|
|
raise "kerblammo!"
|
|
|
|
|
end
|
|
|
|
|
end
|
2015-09-22 15:48:43 -04:00
|
|
|
|
assert_equal 0, Sidekiq::RetrySet.new.size
|
|
|
|
|
assert_equal 1, Sidekiq::DeadSet.new.size
|
2015-07-10 06:45:18 -04:00
|
|
|
|
end
|
|
|
|
|
|
2014-08-27 12:31:55 -04:00
|
|
|
|
it 'handles zany characters in error message, #1705' do
|
2014-08-29 08:52:27 -04:00
|
|
|
|
skip 'skipped! test requires ruby 2.1+' if RUBY_VERSION <= '2.1.0'
|
2015-09-22 15:48:43 -04:00
|
|
|
|
|
2014-08-27 12:31:55 -04:00
|
|
|
|
assert_raises RuntimeError do
|
2015-09-22 15:48:43 -04:00
|
|
|
|
handler.call(worker, job, 'default') do
|
2014-08-27 12:31:55 -04:00
|
|
|
|
raise "kerblammo! #{195.chr}"
|
|
|
|
|
end
|
|
|
|
|
end
|
2015-09-22 15:48:43 -04:00
|
|
|
|
assert_equal "kerblammo! <20>", job["error_message"]
|
2014-08-27 12:31:55 -04:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
2013-08-01 14:54:30 -04:00
|
|
|
|
it 'allows a max_retries option in initializer' do
|
|
|
|
|
max_retries = 7
|
|
|
|
|
1.upto(max_retries + 1) do
|
|
|
|
|
assert_raises RuntimeError do
|
2015-09-22 15:48:43 -04:00
|
|
|
|
handler(:max_retries => max_retries).call(worker, job, 'default') do
|
2013-08-01 14:54:30 -04:00
|
|
|
|
raise "kerblammo!"
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2015-09-22 15:48:43 -04:00
|
|
|
|
|
|
|
|
|
assert_equal max_retries, Sidekiq::RetrySet.new.size
|
|
|
|
|
assert_equal 1, Sidekiq::DeadSet.new.size
|
2013-08-01 14:54:30 -04:00
|
|
|
|
end
|
|
|
|
|
|
2012-04-27 23:25:46 -04:00
|
|
|
|
it 'saves backtraces' do
|
|
|
|
|
c = nil
|
|
|
|
|
assert_raises RuntimeError do
|
2015-09-22 15:48:43 -04:00
|
|
|
|
handler.call(worker, job('backtrace' => true), 'default') do
|
2012-04-27 23:25:46 -04:00
|
|
|
|
c = caller(0); raise "kerblammo!"
|
|
|
|
|
end
|
|
|
|
|
end
|
2015-09-22 15:48:43 -04:00
|
|
|
|
assert job["error_backtrace"]
|
|
|
|
|
assert_equal c[0], job["error_backtrace"][0]
|
2012-04-27 23:25:46 -04:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'saves partial backtraces' do
|
|
|
|
|
c = nil
|
|
|
|
|
assert_raises RuntimeError do
|
2015-09-22 15:48:43 -04:00
|
|
|
|
handler.call(worker, job('backtrace' => 3), 'default') do
|
2015-01-10 00:07:01 -05:00
|
|
|
|
c = caller(0)[0...3]; raise "kerblammo!"
|
2012-04-27 23:25:46 -04:00
|
|
|
|
end
|
|
|
|
|
end
|
2015-09-22 15:48:43 -04:00
|
|
|
|
assert job["error_backtrace"]
|
|
|
|
|
assert_equal c, job["error_backtrace"]
|
2015-01-10 00:07:01 -05:00
|
|
|
|
assert_equal 3, c.size
|
2012-04-27 23:25:46 -04:00
|
|
|
|
end
|
|
|
|
|
|
2012-03-17 21:22:56 -04:00
|
|
|
|
it 'handles a new failed message' do
|
|
|
|
|
assert_raises RuntimeError do
|
2015-09-22 15:48:43 -04:00
|
|
|
|
handler.call(worker, job, 'default') do
|
2012-03-17 21:22:56 -04:00
|
|
|
|
raise "kerblammo!"
|
|
|
|
|
end
|
|
|
|
|
end
|
2015-09-22 15:48:43 -04:00
|
|
|
|
assert_equal 'default', job["queue"]
|
|
|
|
|
assert_equal 'kerblammo!', job["error_message"]
|
|
|
|
|
assert_equal 'RuntimeError', job["error_class"]
|
|
|
|
|
assert_equal 0, job["retry_count"]
|
|
|
|
|
refute job["error_backtrace"]
|
|
|
|
|
assert job["failed_at"]
|
2012-03-17 21:22:56 -04:00
|
|
|
|
end
|
|
|
|
|
|
2014-06-30 02:58:19 -04:00
|
|
|
|
it 'shuts down without retrying work-in-progress, which will resume' do
|
2015-09-22 15:48:43 -04:00
|
|
|
|
rs = Sidekiq::RetrySet.new
|
|
|
|
|
assert_equal 0, rs.size
|
2014-06-30 02:58:19 -04:00
|
|
|
|
msg = { 'class' => 'Bob', 'args' => [1,2,'foo'], 'retry' => true }
|
|
|
|
|
assert_raises Sidekiq::Shutdown do
|
|
|
|
|
handler.call(worker, msg, 'default') do
|
|
|
|
|
raise Sidekiq::Shutdown
|
|
|
|
|
end
|
|
|
|
|
end
|
2015-09-22 15:48:43 -04:00
|
|
|
|
assert_equal 0, rs.size
|
2014-06-30 02:58:19 -04:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'shuts down cleanly when shutdown causes exception' do
|
2014-06-30 03:09:44 -04:00
|
|
|
|
skip('Not supported in Ruby < 2.1.0') if RUBY_VERSION < '2.1.0'
|
|
|
|
|
|
2015-09-22 15:48:43 -04:00
|
|
|
|
rs = Sidekiq::RetrySet.new
|
|
|
|
|
assert_equal 0, rs.size
|
2014-06-30 02:58:19 -04:00
|
|
|
|
msg = { 'class' => 'Bob', 'args' => [1,2,'foo'], 'retry' => true }
|
|
|
|
|
assert_raises Sidekiq::Shutdown do
|
|
|
|
|
handler.call(worker, msg, 'default') do
|
|
|
|
|
begin
|
|
|
|
|
raise Sidekiq::Shutdown
|
|
|
|
|
rescue Interrupt
|
|
|
|
|
raise "kerblammo!"
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2015-09-22 15:48:43 -04:00
|
|
|
|
assert_equal 0, rs.size
|
2014-06-30 02:58:19 -04:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'shuts down cleanly when shutdown causes chained exceptions' do
|
2014-06-30 03:09:44 -04:00
|
|
|
|
skip('Not supported in Ruby < 2.1.0') if RUBY_VERSION < '2.1.0'
|
|
|
|
|
|
2015-09-22 15:48:43 -04:00
|
|
|
|
rs = Sidekiq::RetrySet.new
|
|
|
|
|
assert_equal 0, rs.size
|
2014-06-30 02:58:19 -04:00
|
|
|
|
assert_raises Sidekiq::Shutdown do
|
2015-09-22 15:48:43 -04:00
|
|
|
|
handler.call(worker, job, 'default') do
|
2014-06-30 02:58:19 -04:00
|
|
|
|
begin
|
|
|
|
|
raise Sidekiq::Shutdown
|
|
|
|
|
rescue Interrupt
|
|
|
|
|
begin
|
|
|
|
|
raise "kerblammo!"
|
|
|
|
|
rescue
|
|
|
|
|
raise "kablooie!"
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2015-09-22 15:48:43 -04:00
|
|
|
|
assert_equal 0, rs.size
|
2014-06-30 02:58:19 -04:00
|
|
|
|
end
|
|
|
|
|
|
2013-01-15 20:28:52 -05:00
|
|
|
|
it 'allows a retry queue' do
|
|
|
|
|
assert_raises RuntimeError do
|
2015-09-22 15:48:43 -04:00
|
|
|
|
handler.call(worker, job("retry_queue" => 'retryx'), 'default') do
|
2013-01-15 20:28:52 -05:00
|
|
|
|
raise "kerblammo!"
|
|
|
|
|
end
|
|
|
|
|
end
|
2015-09-22 15:48:43 -04:00
|
|
|
|
assert_equal 'retryx', job["queue"]
|
|
|
|
|
assert_equal 'kerblammo!', job["error_message"]
|
|
|
|
|
assert_equal 'RuntimeError', job["error_class"]
|
|
|
|
|
assert_equal 0, job["retry_count"]
|
|
|
|
|
refute job["error_backtrace"]
|
|
|
|
|
assert job["failed_at"]
|
2013-01-15 20:28:52 -05:00
|
|
|
|
end
|
|
|
|
|
|
2012-03-17 21:22:56 -04:00
|
|
|
|
it 'handles a recurring failed message' do
|
2014-02-09 18:08:21 -05:00
|
|
|
|
now = Time.now.to_f
|
2015-09-22 15:48:43 -04:00
|
|
|
|
msg = {"queue"=>"default", "error_message"=>"kerblammo!", "error_class"=>"RuntimeError", "failed_at"=>now, "retry_count"=>10}
|
2012-03-17 21:22:56 -04:00
|
|
|
|
assert_raises RuntimeError do
|
2015-09-22 15:48:43 -04:00
|
|
|
|
handler.call(worker, job(msg), 'default') do
|
2012-03-17 21:22:56 -04:00
|
|
|
|
raise "kerblammo!"
|
|
|
|
|
end
|
|
|
|
|
end
|
2015-09-22 15:48:43 -04:00
|
|
|
|
assert_equal 'default', job["queue"]
|
|
|
|
|
assert_equal 'kerblammo!', job["error_message"]
|
|
|
|
|
assert_equal 'RuntimeError', job["error_class"]
|
|
|
|
|
assert_equal 11, job["retry_count"]
|
|
|
|
|
assert job["failed_at"]
|
2012-10-17 18:51:26 -04:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'throws away old messages after too many retries (using the default)' do
|
2015-09-22 15:48:43 -04:00
|
|
|
|
q = Sidekiq::Queue.new
|
|
|
|
|
rs = Sidekiq::RetrySet.new
|
|
|
|
|
ds = Sidekiq::DeadSet.new
|
|
|
|
|
assert_equal 0, q.size
|
|
|
|
|
assert_equal 0, rs.size
|
|
|
|
|
assert_equal 0, ds.size
|
2014-02-09 18:08:21 -05:00
|
|
|
|
now = Time.now.to_f
|
2015-09-22 15:48:43 -04:00
|
|
|
|
msg = {"queue"=>"default", "error_message"=>"kerblammo!", "error_class"=>"RuntimeError", "failed_at"=>now, "retry_count"=>25}
|
2012-10-17 18:51:26 -04:00
|
|
|
|
assert_raises RuntimeError do
|
2015-09-22 15:48:43 -04:00
|
|
|
|
handler.call(worker, job(msg), 'default') do
|
2012-10-17 18:51:26 -04:00
|
|
|
|
raise "kerblammo!"
|
|
|
|
|
end
|
|
|
|
|
end
|
2015-09-22 15:48:43 -04:00
|
|
|
|
assert_equal 0, q.size
|
|
|
|
|
assert_equal 0, rs.size
|
|
|
|
|
assert_equal 1, ds.size
|
2013-03-18 17:20:28 -04:00
|
|
|
|
end
|
2013-06-25 11:07:45 -04:00
|
|
|
|
|
|
|
|
|
describe "custom retry delay" do
|
2013-06-25 12:35:11 -04:00
|
|
|
|
before do
|
|
|
|
|
@old_logger = Sidekiq.logger
|
|
|
|
|
@tmp_log_path = '/tmp/sidekiq-retries.log'
|
|
|
|
|
Sidekiq.logger = Logger.new(@tmp_log_path)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
after do
|
|
|
|
|
Sidekiq.logger = @old_logger
|
|
|
|
|
Sidekiq.options.delete(:logfile)
|
2014-03-19 20:41:11 -04:00
|
|
|
|
File.unlink @tmp_log_path if File.exist?(@tmp_log_path)
|
2013-06-25 12:35:11 -04:00
|
|
|
|
end
|
2013-06-25 11:07:45 -04:00
|
|
|
|
|
2015-10-23 11:54:55 -04:00
|
|
|
|
class CustomWorkerWithoutException
|
2015-09-22 15:48:43 -04:00
|
|
|
|
include Sidekiq::Worker
|
2013-06-25 11:07:45 -04:00
|
|
|
|
|
2015-09-22 15:48:43 -04:00
|
|
|
|
sidekiq_retry_in do |count|
|
|
|
|
|
count * 2
|
2013-06-25 11:07:45 -04:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-10-23 11:54:55 -04:00
|
|
|
|
class CustomWorkerWithException
|
|
|
|
|
include Sidekiq::Worker
|
|
|
|
|
|
|
|
|
|
sidekiq_retry_in do |count, exception|
|
|
|
|
|
case exception
|
|
|
|
|
when ArgumentError
|
|
|
|
|
count * 4
|
|
|
|
|
else
|
|
|
|
|
count * 2
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-09-22 15:48:43 -04:00
|
|
|
|
class ErrorWorker
|
|
|
|
|
include Sidekiq::Worker
|
2013-06-25 12:35:11 -04:00
|
|
|
|
|
2015-09-22 15:48:43 -04:00
|
|
|
|
sidekiq_retry_in do |count|
|
|
|
|
|
count / 0
|
2013-06-25 12:35:11 -04:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2013-06-25 11:07:45 -04:00
|
|
|
|
it "retries with a default delay" do
|
2015-10-23 11:54:55 -04:00
|
|
|
|
refute_equal 4, handler.__send__(:delay_for, worker, 2, StandardError.new)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "retries with a custom delay and exception 1" do
|
|
|
|
|
assert_equal 8, handler.__send__(:delay_for, CustomWorkerWithException, 2, ArgumentError.new)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "retries with a custom delay and exception 2" do
|
|
|
|
|
assert_equal 4, handler.__send__(:delay_for, CustomWorkerWithException, 2, StandardError.new)
|
2013-06-25 11:07:45 -04:00
|
|
|
|
end
|
|
|
|
|
|
2015-10-23 11:54:55 -04:00
|
|
|
|
it "retries with a custom delay without exception" do
|
|
|
|
|
assert_equal 4, handler.__send__(:delay_for, CustomWorkerWithoutException, 2, StandardError.new)
|
2013-06-25 11:07:45 -04:00
|
|
|
|
end
|
2013-06-25 12:35:11 -04:00
|
|
|
|
|
|
|
|
|
it "falls back to the default retry on exception" do
|
2015-10-23 11:54:55 -04:00
|
|
|
|
refute_equal 4, handler.__send__(:delay_for, ErrorWorker, 2, StandardError.new)
|
2013-06-25 12:35:11 -04:00
|
|
|
|
assert_match(/Failure scheduling retry using the defined `sidekiq_retry_in`/,
|
|
|
|
|
File.read(@tmp_log_path), 'Log entry missing for sidekiq_retry_in')
|
|
|
|
|
end
|
2013-06-25 11:07:45 -04:00
|
|
|
|
end
|
2015-04-08 07:08:29 -04:00
|
|
|
|
|
2015-04-08 13:34:49 -04:00
|
|
|
|
describe 'handles errors withouth cause' do
|
|
|
|
|
before do
|
|
|
|
|
@error = nil
|
|
|
|
|
begin
|
|
|
|
|
raise ::StandardError, 'Error'
|
|
|
|
|
rescue ::StandardError => e
|
|
|
|
|
@error = e
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "does not recurse infinitely checking if it's a shutdown" do
|
|
|
|
|
assert(!Sidekiq::Middleware::Server::RetryJobs.new.send(
|
|
|
|
|
:exception_caused_by_shutdown?, @error))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-04-08 07:08:29 -04:00
|
|
|
|
describe 'handles errors with circular causes' do
|
|
|
|
|
before do
|
|
|
|
|
@error = nil
|
|
|
|
|
begin
|
|
|
|
|
begin
|
|
|
|
|
raise ::StandardError, 'Error 1'
|
|
|
|
|
rescue ::StandardError => e1
|
|
|
|
|
begin
|
|
|
|
|
raise ::StandardError, 'Error 2'
|
2015-06-22 14:07:28 -04:00
|
|
|
|
rescue ::StandardError
|
2015-04-08 07:08:29 -04:00
|
|
|
|
raise e1
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
rescue ::StandardError => e
|
|
|
|
|
@error = e
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-04-08 13:34:49 -04:00
|
|
|
|
it "does not recurse infinitely checking if it's a shutdown" do
|
2015-04-08 07:08:29 -04:00
|
|
|
|
assert(!Sidekiq::Middleware::Server::RetryJobs.new.send(
|
|
|
|
|
:exception_caused_by_shutdown?, @error))
|
|
|
|
|
end
|
|
|
|
|
end
|
2012-03-17 21:22:56 -04:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
end
|