resque-retry 1.5.0 → 1.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,10 +3,9 @@ require 'resque/plugins/retry/logging'
3
3
 
4
4
  module Resque
5
5
  module Failure
6
-
7
6
  # A multiple failure backend, with retry suppression
8
7
  #
9
- # For example: if you had a job that could retry 5 times, your failure
8
+ # For example: if you had a job that could retry 5 times, your failure
10
9
  # backends are not notified unless the _final_ retry attempt also fails.
11
10
  #
12
11
  # Example:
@@ -28,17 +27,44 @@ module Resque
28
27
  #
29
28
  # @api private
30
29
  def save
31
- log_message 'failure backend save', args_from(payload), exception
30
+ args = args_from(payload)
31
+
32
+ log_message 'failure backend save', args, exception
32
33
 
33
34
  retryable = retryable?
34
35
  job_being_retried = retryable && retrying?
35
36
 
36
37
  if !job_being_retried
37
- log_message "#{retryable ? '' : 'non-'}retriable job is not being retried - sending failure to superclass", args_from(payload), exception
38
+ log_message(
39
+ "#{retryable ? '' : 'non-'}retryable job is not being retried - sending failure to superclass",
40
+ args,
41
+ exception
42
+ )
43
+
38
44
  cleanup_retry_failure_log!
39
- super
40
- elsif retry_delay > 0
41
- log_message "retry_delay: #{retry_delay} > 0 - saving details in Redis", args_from(payload), exception
45
+ return super
46
+ end
47
+
48
+ # some plugins define retry_delay and have it take no arguments, so rather than break those,
49
+ # we'll just check here to see whether it takes the additional exception class argument or not
50
+ # we also allow all job args to be passed to a custom `retry_delay` method
51
+ retry_delay_arity = klass.method(:retry_delay).arity
52
+
53
+ calculated_retry_delay = if [-2, 2].include?(retry_delay_arity)
54
+ klass.retry_delay(exception.class, *args)
55
+ elsif [-1, 1].include?(retry_delay_arity)
56
+ klass.retry_delay(exception.class)
57
+ else
58
+ klass.retry_delay
59
+ end
60
+
61
+ if calculated_retry_delay > 0
62
+ log_message(
63
+ "retry_delay: #{calculated_retry_delay} > 0 - saving details in Redis",
64
+ args,
65
+ exception
66
+ )
67
+
42
68
  data = {
43
69
  :failed_at => Time.now.strftime("%Y/%m/%d %H:%M:%S"),
44
70
  :payload => payload,
@@ -48,10 +74,19 @@ module Resque
48
74
  :worker => worker.to_s,
49
75
  :queue => queue
50
76
  }
77
+ data = Resque.encode(data)
51
78
 
52
- Resque.redis.setex(failure_key, 2*retry_delay, Resque.encode(data))
79
+ Resque.redis.setex(
80
+ failure_key,
81
+ 2 * calculated_retry_delay,
82
+ data
83
+ )
53
84
  else
54
- log_message "retry_delay: #{retry_delay} <= 0 - ignoring", args_from(payload), exception
85
+ log_message(
86
+ "retry_delay: #{calculated_retry_delay} <= 0 - ignoring",
87
+ args,
88
+ exception
89
+ )
55
90
  end
56
91
  end
57
92
 
@@ -62,25 +97,45 @@ module Resque
62
97
  'failure-' + retry_key
63
98
  end
64
99
 
65
- protected
66
-
67
- # Return the class/module of the failed job.
68
- def klass
69
- Resque::Job.new(nil, nil).constantize(payload['class'])
100
+ # Monkey-patch this in for now since it is a hard requirement for the
101
+ # "retry-all" functionality to work via the Web UI.
102
+ #
103
+ # This can be removed when the following PR has been merged into `Resque`
104
+ # itself:
105
+ #
106
+ # https://github.com/resque/resque/pull/1659
107
+ #
108
+ # @api public
109
+ class_eval do |klass|
110
+ if !klass.respond_to?(:requeue_queue)
111
+ def klass.requeue_queue(queue)
112
+ classes.first.requeue_queue(queue)
113
+ end
114
+ end
70
115
  end
71
116
 
72
- def retry_delay
73
- klass.retry_delay
117
+ protected
118
+
119
+ def args_from(payload)
120
+ (payload || {})['args'].dup
74
121
  end
75
122
 
76
- def retry_key
77
- klass.redis_retry_key(*payload['args'])
123
+ def cleanup_retry_failure_log!
124
+ Resque.redis.del(failure_key) if retryable?
78
125
  end
79
126
 
80
127
  def failure_key
81
128
  self.class.failure_key(retry_key)
82
129
  end
83
130
 
131
+ def klass
132
+ Resque::Job.new(nil, nil).constantize(payload['class'])
133
+ end
134
+
135
+ def retry_key
136
+ klass.redis_retry_key(*payload['args'])
137
+ end
138
+
84
139
  def retryable?
85
140
  klass.respond_to?(:redis_retry_key)
86
141
  rescue NameError
@@ -88,15 +143,17 @@ module Resque
88
143
  end
89
144
 
90
145
  def retrying?
91
- Resque.redis.exists(retry_key)
146
+ redis_key_exists?(retry_key)
92
147
  end
93
148
 
94
- def cleanup_retry_failure_log!
95
- Resque.redis.del(failure_key) if retryable?
96
- end
149
+ private
97
150
 
98
- def args_from(payload)
99
- (payload || {})['args']
151
+ def redis_key_exists?(key)
152
+ if Resque.redis.respond_to?(:exists?)
153
+ Resque.redis.exists?(key)
154
+ else
155
+ ![false, 0].include?(Resque.redis.exists(key) || false)
156
+ end
100
157
  end
101
158
  end
102
159
  end
@@ -52,12 +52,16 @@ module Resque
52
52
  #
53
53
  # @api private
54
54
  def self.extended(receiver)
55
+ retry_delay_multiplicand_min = DEFAULT_RETRY_DELAY_MULTIPLICAND_MIN
55
56
  retry_delay_multiplicand_min = \
56
- receiver.instance_variable_get("@retry_delay_multiplicand_min") || \
57
- DEFAULT_RETRY_DELAY_MULTIPLICAND_MIN
57
+ receiver.instance_variable_get(:@retry_delay_multiplicand_min) \
58
+ if receiver.instance_variable_defined?(:@retry_delay_multiplicand_min)
59
+
60
+ retry_delay_multiplicand_max = DEFAULT_RETRY_DELAY_MULTIPLICAND_MAX
58
61
  retry_delay_multiplicand_max = \
59
- receiver.instance_variable_get("@retry_delay_multiplicand_max") || \
60
- DEFAULT_RETRY_DELAY_MULTIPLICAND_MAX
62
+ receiver.instance_variable_get(:@retry_delay_multiplicand_max) \
63
+ if receiver.instance_variable_defined?(:@retry_delay_multiplicand_max)
64
+
61
65
  if retry_delay_multiplicand_min > retry_delay_multiplicand_max
62
66
  raise InvalidRetryDelayMultiplicandConfigurationException.new(
63
67
  %{"@retry_delay_multiplicand_min" must be less than or equal to "@retry_delay_multiplicand_max"}
@@ -76,10 +80,11 @@ module Resque
76
80
 
77
81
  # Selects the delay from the backoff strategy
78
82
  #
83
+ # @param _ [Exception] unused exception argument for signature parity
79
84
  # @return [Number] seconds to delay until the next retry.
80
85
  #
81
86
  # @api private
82
- def retry_delay
87
+ def retry_delay(_ = nil)
83
88
  delay = backoff_strategy[retry_attempt] || backoff_strategy.last
84
89
  # if the values are the same don't bother generating a random number, if
85
90
  # the delta is zero, some platforms will raise an error
@@ -43,11 +43,24 @@ module Resque
43
43
  # @api public
44
44
  class AmbiguousRetryStrategyException < StandardError; end
45
45
 
46
+ # Raised if there is a problem with the configuration of resque-retry.
47
+ #
48
+ # @api public
49
+ class RetryConfigurationException < StandardError; end
50
+
46
51
  # Fail fast, when extended, if the "receiver" is misconfigured
47
52
  #
48
53
  # @api private
49
54
  def self.extended(receiver)
50
- if receiver.instance_variable_get('@fatal_exceptions') && receiver.instance_variable_get('@retry_exceptions')
55
+ retry_exceptions = nil
56
+ retry_exceptions = receiver.instance_variable_get(:@retry_exceptions) \
57
+ if receiver.instance_variable_defined?(:@retry_exceptions)
58
+
59
+ fatal_exceptions = nil
60
+ fatal_exceptions = receiver.instance_variable_get(:@fatal_exceptions) \
61
+ if receiver.instance_variable_defined?(:@fatal_exceptions)
62
+
63
+ if fatal_exceptions && retry_exceptions
51
64
  raise AmbiguousRetryStrategyException.new(%{You can't define both "@fatal_exceptions" and "@retry_exceptions"})
52
65
  end
53
66
  end
@@ -94,11 +107,27 @@ module Resque
94
107
  # A retry limit of 0 will *never* retry.
95
108
  # A retry limit of -1 or below will retry forever.
96
109
  #
110
+ # The default value is: `1` or in the case of where `@retry_exceptions` is
111
+ # specified, and it contains one or more `Array` values, the maximum
112
+ # length will be used (e.g. `@retry_exceptions = { NetworkError => 30, SystemCallError => [120, 240] }`
113
+ # would return `2` because `SystemCallError` _should_ be attempted at
114
+ # least twice to respect the specified configuration).
115
+ #
97
116
  # @return [Fixnum]
98
117
  #
99
118
  # @api public
100
119
  def retry_limit
101
- @retry_limit ||= 1
120
+ @retry_limit ||= begin
121
+ default_retry_limit = 1
122
+ if instance_variable_defined?(:@retry_exceptions) && @retry_exceptions.is_a?(Hash)
123
+ @retry_exceptions.values.each do |value|
124
+ if value.is_a?(Array) && value.length > default_retry_limit
125
+ default_retry_limit = value.length
126
+ end
127
+ end
128
+ end
129
+ default_retry_limit
130
+ end
102
131
  end
103
132
 
104
133
  # Number of retry attempts used to try and perform the job
@@ -115,13 +144,22 @@ module Resque
115
144
 
116
145
  # @abstract
117
146
  # Number of seconds to delay until the job is retried
147
+ # If @retry_exceptions is a Hash and there is no delay defined for exception_class,
148
+ # looks for closest superclass and assigns it's delay to @retry_exceptions[exception_class]
118
149
  #
119
150
  # @return [Number] number of seconds to delay
120
151
  #
121
152
  # @api public
122
153
  def retry_delay(exception_class = nil)
123
- if @retry_exceptions.is_a?(Hash)
124
- delay = @retry_exceptions[exception_class] || 0
154
+ if \
155
+ !exception_class.nil? && \
156
+ instance_variable_defined?(:@retry_exceptions) && \
157
+ @retry_exceptions.is_a?(Hash)
158
+ delay = @retry_exceptions[exception_class] ||= begin
159
+ relevant_definitions = \
160
+ @retry_exceptions.select { |ex| exception_class <= ex }
161
+ relevant_definitions.any? ? relevant_definitions.sort.first[1] : 0
162
+ end
125
163
  # allow an array of delays.
126
164
  delay.is_a?(Array) ? delay[retry_attempt] || delay.last : delay
127
165
  else
@@ -131,7 +169,7 @@ module Resque
131
169
 
132
170
  # @abstract
133
171
  # Number of seconds to sleep after job is requeued
134
- #
172
+ #
135
173
  # @return [Number] number of seconds to sleep
136
174
  #
137
175
  # @api public
@@ -150,6 +188,16 @@ module Resque
150
188
  @retry_job_delegate ||= nil
151
189
  end
152
190
 
191
+ # @abstract
192
+ # Specify the queue that the job should be placed in upon failure
193
+ #
194
+ # @return [Symbol] Symbol representing queue that job should be placed in
195
+ #
196
+ # @api public
197
+ def retry_queue(exception, *args)
198
+ nil
199
+ end
200
+
153
201
  # @abstract
154
202
  # Modify the arguments used to retry the job. Use this to do something
155
203
  # other than try the exact same job again
@@ -158,15 +206,7 @@ module Resque
158
206
  #
159
207
  # @api public
160
208
  def retry_args(*args)
161
- # Here for backwards compatibility. If an "args_for_retry" method exists
162
- # invoke it, but warn that it is deprecated (and will be removed in a
163
- # future revision)
164
- if respond_to?(:args_for_retry)
165
- warn "`Resque::Plugins::Retry#args_for_retry` is deprecated, please use `Resque::Plugins::Retry#retry_args` instead."
166
- args_for_retry(*args)
167
- else
168
- args
169
- end
209
+ args.dup
170
210
  end
171
211
 
172
212
  # @abstract
@@ -238,7 +278,7 @@ module Resque
238
278
  #
239
279
  # @api public
240
280
  def retry_exceptions
241
- if @retry_exceptions.is_a?(Hash)
281
+ if instance_variable_defined?(:@retry_exceptions) && @retry_exceptions.is_a?(Hash)
242
282
  @retry_exceptions.keys
243
283
  else
244
284
  @retry_exceptions ||= nil
@@ -264,7 +304,7 @@ module Resque
264
304
  # if the retry limit was reached, dont bother checking anything else.
265
305
  if retry_limit_reached?
266
306
  log_message 'retry limit reached', args, exception
267
- return false
307
+ return false
268
308
  end
269
309
 
270
310
  # We always want to retry if the exception matches.
@@ -362,14 +402,27 @@ module Resque
362
402
 
363
403
  # some plugins define retry_delay and have it take no arguments, so rather than break those,
364
404
  # we'll just check here to see whether it takes the additional exception class argument or not
365
- temp_retry_delay = ([-1, 1].include?(method(:retry_delay).arity) ? retry_delay(exception.class) : retry_delay)
405
+ # we also allow all job args to be passed to a custom `retry_delay` method
406
+ retry_delay_arity = method(:retry_delay).arity
407
+
408
+ temp_retry_delay = if [-2, 2].include?(retry_delay_arity)
409
+ retry_delay(exception.class, *args)
410
+ elsif [-1, 1].include?(retry_delay_arity)
411
+ retry_delay(exception.class)
412
+ else
413
+ retry_delay
414
+ end
415
+
416
+ retry_job_class = retry_job_delegate ? retry_job_delegate : self
366
417
 
367
- retry_in_queue = retry_job_delegate ? retry_job_delegate : self
368
- log_message "retry delay: #{temp_retry_delay} for class: #{retry_in_queue}", args, exception
418
+ retry_in_queue = retry_queue(exception, *args)
419
+ retry_in_queue ||= Resque.queue_from_class(retry_job_class)
420
+
421
+ log_message "retry delay: #{temp_retry_delay} for queue: #{retry_in_queue}", args, exception
369
422
 
370
423
  # remember that this job is now being retried. before_perform_retry will increment
371
424
  # this so it represents the retry count, and MultipleWithRetrySuppression uses
372
- # the existence of this to determine if the job should be sent to the
425
+ # the existence of this to determine if the job should be sent to the
373
426
  # parent failure backend (e.g. failed queue) or not. Removing this means
374
427
  # jobs that fail before ::perform will be both retried and sent to the failed queue.
375
428
  Resque.redis.setnx(redis_retry_key(*args), -1)
@@ -378,9 +431,9 @@ module Resque
378
431
 
379
432
  if temp_retry_delay <= 0
380
433
  # If the delay is 0, no point passing it through the scheduler
381
- Resque.enqueue(retry_in_queue, *retry_args)
434
+ Resque.enqueue_to(retry_in_queue, retry_job_class, *retry_args)
382
435
  else
383
- Resque.enqueue_in(temp_retry_delay, retry_in_queue, *retry_args)
436
+ Resque.enqueue_in_with_queue(retry_in_queue, temp_retry_delay, retry_job_class, *retry_args)
384
437
  end
385
438
 
386
439
  # remove retry key from redis if we handed retry off to another queue.
@@ -406,6 +459,7 @@ module Resque
406
459
  #
407
460
  # @api private
408
461
  def before_perform_retry(*args)
462
+ return if Resque.inline?
409
463
  log_message 'before_perform_retry', args
410
464
  @on_failure_retry_hook_already_called = false
411
465
 
@@ -418,7 +472,8 @@ module Resque
418
472
  # set/update the "retry_key" expiration
419
473
  if expire_retry_key_after
420
474
  log_message "updating expiration for retry key: #{retry_key}", args
421
- Resque.redis.expire(retry_key, retry_delay + expire_retry_key_after)
475
+ exception_class = Object.const_get(args[0]) rescue nil
476
+ Resque.redis.expire(retry_key, retry_delay(exception_class) + expire_retry_key_after)
422
477
  end
423
478
  end
424
479
 
@@ -428,6 +483,7 @@ module Resque
428
483
  #
429
484
  # @api private
430
485
  def after_perform_retry(*args)
486
+ return if Resque.inline?
431
487
  log_message 'after_perform_retry, clearing retry key', args
432
488
  clean_retry_key(*args)
433
489
  end
@@ -443,16 +499,25 @@ module Resque
443
499
  #
444
500
  # @api private
445
501
  def on_failure_retry(exception, *args)
502
+ return if Resque.inline?
446
503
  log_message 'on_failure_retry', args, exception
447
504
  if exception.is_a?(Resque::DirtyExit)
448
505
  # This hook is called from a worker processes, not the job process
449
506
  # that failed with a DirtyExit, so @retry_attempt wasn't set yet
450
507
  @retry_attempt = Resque.redis.get(redis_retry_key(*args)).to_i
451
- elsif @on_failure_retry_hook_already_called
508
+ elsif instance_variable_defined?(:@on_failure_retry_hook_already_called) && \
509
+ @on_failure_retry_hook_already_called
452
510
  log_message 'on_failure_retry_hook_already_called', args, exception
453
511
  return
454
512
  end
455
513
 
514
+ # If we are "ignoring" the exception, then we decrement the retry
515
+ # counter, so that the current attempt didn't count toward the retry
516
+ # counter.
517
+ if ignore_exceptions.include?(exception.class)
518
+ @retry_attempt = Resque.redis.decr(redis_retry_key(*args))
519
+ end
520
+
456
521
  if retry_criteria_valid?(exception, *args)
457
522
  try_again(exception, *args)
458
523
  else
@@ -584,6 +649,10 @@ module Resque
584
649
  end
585
650
  end
586
651
 
652
+ def ignore_exceptions
653
+ @ignore_exceptions ||= []
654
+ end
655
+
587
656
  # Helper to call functions that may be passed as Symbols or Procs. If
588
657
  # a symbol, it is assumed to refer to a method that is already defined
589
658
  # on this class.
@@ -66,15 +66,23 @@ module ResqueRetry
66
66
  # cancels job retry
67
67
  def cancel_retry(job)
68
68
  klass = get_class(job)
69
- retry_key = retry_key_for_job(job)
70
- Resque.remove_delayed(klass, *job['args'])
71
- Resque.redis.del("failure-#{retry_key}")
72
- Resque.redis.del(retry_key)
69
+ if klass
70
+ retry_key = retry_key_for_job(job)
71
+ Resque.remove_delayed(klass, *job['args'])
72
+ Resque.redis.del("failure-#{retry_key}")
73
+ Resque.redis.del(retry_key)
74
+ else
75
+ raise 'cannot cancel, job not found'
76
+ end
73
77
  end
74
78
 
75
79
  private
76
80
  def get_class(job)
77
- Resque::Job.new(nil, nil).constantize(job['class'])
81
+ begin
82
+ Resque::Job.new(nil, nil).constantize(job['class'])
83
+ rescue
84
+ nil
85
+ end
78
86
  end
79
87
  end
80
88
 
@@ -1,3 +1,3 @@
1
1
  module ResqueRetry
2
- VERSION = '1.5.0'
2
+ VERSION = '1.7.6'
3
3
  end
data/resque-retry.gemspec CHANGED
@@ -25,11 +25,10 @@ Gem::Specification.new do |s|
25
25
  s.homepage = 'http://github.com/lantins/resque-retry'
26
26
  s.license = 'MIT'
27
27
 
28
- s.has_rdoc = false
29
28
  s.files = `git ls-files`.split($/)
30
29
  s.require_paths = %w[lib]
31
30
 
32
- s.add_dependency('resque', '~> 1.25')
31
+ s.add_dependency('resque', '>= 1.25', '< 3.0')
33
32
  s.add_dependency('resque-scheduler', '~> 4.0')
34
33
 
35
34
  s.add_development_dependency('rake', '~> 10.3')
@@ -128,4 +128,11 @@ class ExponentialBackoffTest < Minitest::Test
128
128
  assert_equal 4, Resque.info[:failed], 'failed jobs'
129
129
  assert_equal 0, Resque.info[:pending], 'pending jobs'
130
130
  end
131
+
132
+ def test_backoff_with_expiration
133
+ Resque.redis.expects(:expire)
134
+
135
+ Resque.enqueue(ExponentialBackoffWithExpiryJob)
136
+ perform_next_job(@worker)
137
+ end
131
138
  end
@@ -0,0 +1,26 @@
1
+ require 'test_helper'
2
+
3
+ class IgnoreExceptionsTest < Minitest::Test
4
+ def setup
5
+ Resque.redis.flushall
6
+ @worker = Resque::Worker.new(:testing)
7
+ @worker.register_worker
8
+ end
9
+
10
+ def test_ignore_exceptions
11
+ Resque.enqueue(IgnoreExceptionsJob)
12
+ retry_key = IgnoreExceptionsJob.redis_retry_key
13
+
14
+ IgnoreExceptionsJob.stubs(:perform).raises(AnotherCustomException)
15
+ perform_next_job(@worker)
16
+ assert_equal '0', Resque.redis.get(retry_key), 'retry counter'
17
+
18
+ IgnoreExceptionsJob.stubs(:perform).raises(AnotherCustomException)
19
+ perform_next_job(@worker)
20
+ assert_equal '1', Resque.redis.get(retry_key), 'retry counter'
21
+
22
+ IgnoreExceptionsJob.stubs(:perform).raises(CustomException)
23
+ perform_next_job(@worker)
24
+ assert_equal '1', Resque.redis.get(retry_key), 'retry counter'
25
+ end
26
+ end
@@ -1,4 +1,4 @@
1
- require 'test_helper'
1
+ require_relative './test_helper'
2
2
 
3
3
  # Mock failure backend for testing MultipleWithRetrySuppression
4
4
  class MockFailureBackend < Resque::Failure::Base
@@ -14,7 +14,6 @@ class MockFailureBackend < Resque::Failure::Base
14
14
  end
15
15
 
16
16
  class MultipleFailureTest < Minitest::Test
17
-
18
17
  def setup
19
18
  Resque.redis.flushall
20
19
  @worker = Resque::Worker.new(:testing)
@@ -26,9 +25,8 @@ class MultipleFailureTest < Minitest::Test
26
25
  Resque::Failure.backend = Resque::Failure::MultipleWithRetrySuppression
27
26
  end
28
27
 
29
- def failure_key_for(klass)
30
- args = []
31
- key = 'failure-' + klass.redis_retry_key(args)
28
+ def failure_key_for(klass, *args)
29
+ Resque::Failure::MultipleWithRetrySuppression.failure_key(klass.redis_retry_key(args))
32
30
  end
33
31
 
34
32
  def test_failure_is_passed_on_when_job_class_not_found
@@ -41,7 +39,9 @@ class MultipleFailureTest < Minitest::Test
41
39
  perform_next_job(@worker)
42
40
 
43
41
  assert_equal 1, MockFailureBackend.errors.count, 'should have one error'
44
- assert_match /uninitialized constant.* LimitThreeJobTemp/, MockFailureBackend.errors.first
42
+
43
+ uninitialized_constant_pattern = /uninitialized constant.* LimitThreeJobTemp/
44
+ assert_match uninitialized_constant_pattern, MockFailureBackend.errors.first
45
45
  end
46
46
 
47
47
  def test_last_failure_is_saved_in_redis_if_delay
@@ -53,6 +53,22 @@ class MultipleFailureTest < Minitest::Test
53
53
  assert Resque.redis.exists(key)
54
54
  end
55
55
 
56
+ def test_retry_delay_is_calculated_with_custom_calculation
57
+ delay = 5
58
+ Resque.enqueue(DynamicDelayedJobOnExceptionAndArgs, delay.to_s)
59
+ perform_next_job(@worker)
60
+
61
+ key = failure_key_for(DynamicDelayedJobOnExceptionAndArgs, delay.to_s)
62
+ ttl = Resque.redis.ttl(key)
63
+ assert Resque.redis.exists(key)
64
+ assert MockFailureBackend.errors.size == 0
65
+
66
+ # expiration on failure_key is set to 2x the delay
67
+ # to ensure the customized delay is properly calculated using
68
+ # dynamic retry_delay method on the job
69
+ assert ttl > delay && delay <= (delay * 2)
70
+ end
71
+
56
72
  def test_retry_key_splatting_args
57
73
  # were expecting this to be called three times:
58
74
  # - once when we queue the job to try again
@@ -141,6 +157,20 @@ class MultipleFailureTest < Minitest::Test
141
157
  end
142
158
  end
143
159
 
160
+ def test_redis_exists_returns_integer
161
+ Resque.enqueue(RetryDefaultsJob)
162
+ original = Redis.exists_returns_integer
163
+ Redis.exists_returns_integer = true
164
+
165
+ 3.times do
166
+ perform_next_job(@worker)
167
+ end
168
+
169
+ Redis.exists_returns_integer = original
170
+
171
+ assert_equal 1, MockFailureBackend.errors.size
172
+ end
173
+
144
174
  def teardown
145
175
  Resque::Failure.backend = @old_failure_backend
146
176
  end
@@ -0,0 +1,23 @@
1
+ require 'test_helper'
2
+
3
+ class ResqueInlineTest < Minitest::Test
4
+ def setup
5
+ Resque.inline = true
6
+ Resque.expects(:redis).never
7
+ end
8
+
9
+ def teardown
10
+ Resque.inline = false
11
+ end
12
+
13
+ def test_runs_inline
14
+ GoodJob.expects :perform
15
+ Resque.enqueue(GoodJob)
16
+ end
17
+
18
+ def test_fails_inline
19
+ assert_raises CustomException do
20
+ Resque.enqueue(RetryCustomExceptionsJob, 'CustomException')
21
+ end
22
+ end
23
+ end
data/test/resque_test.rb CHANGED
@@ -1,11 +1,15 @@
1
1
  require 'test_helper'
2
2
 
3
- # make sure the worlds not fallen from beneith us.
4
3
  class ResqueTest < Minitest::Test
5
4
  def test_resque_version
6
- major, minor, patch = Resque::Version.split('.')
7
- assert_equal 1, major.to_i, 'major version does not match'
8
- assert_operator minor.to_i, :>=, 8, 'minor version is too low'
5
+ major, minor, _ = Resque::Version.split('.')
6
+ assert [1, 2].include?(major.to_i), 'major version does not match'
7
+
8
+ if major.to_i == 1
9
+ assert_operator minor.to_i, :>=, 25, 'minor version is too low'
10
+ else
11
+ assert_operator minor.to_i, :>=, 0, 'minor version is too low'
12
+ end
9
13
  end
10
14
 
11
15
  def test_good_job
@@ -0,0 +1,23 @@
1
+ require 'test_helper'
2
+
3
+ class RetryCustomDelayTest < Minitest::Test
4
+ def setup
5
+ Resque.redis.flushall
6
+ @worker = Resque::Worker.new(:testing)
7
+ @worker.register_worker
8
+ end
9
+
10
+ def test_delay_with_exception
11
+ Resque.enqueue(DynamicDelayedJobOnException, 'arg1')
12
+ Resque.expects(:enqueue_in_with_queue).with(:testing, 4, DynamicDelayedJobOnException, 'arg1')
13
+
14
+ perform_next_job(@worker)
15
+ end
16
+
17
+ def test_delay_with_exception_and_args
18
+ Resque.enqueue(DynamicDelayedJobOnExceptionAndArgs, '3')
19
+ Resque.expects(:enqueue_in_with_queue).with(:testing, 3, DynamicDelayedJobOnExceptionAndArgs, '3')
20
+
21
+ perform_next_job(@worker)
22
+ end
23
+ end