resque-retry 1.0.0.a → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY.md +3 -2
- data/README.md +3 -3
- data/Rakefile +7 -6
- data/bin/test-rubies.sh +18 -0
- data/lib/resque/failure/multiple_with_retry_suppression.rb +9 -3
- data/lib/resque/plugins/exponential_backoff.rb +9 -3
- data/lib/resque/plugins/retry.rb +81 -21
- data/lib/resque-retry/server.rb +48 -40
- data/test/exponential_backoff_test.rb +1 -1
- data/test/multiple_failure_test.rb +15 -2
- data/test/redis-test.conf +7 -7
- data/test/resque_test.rb +1 -1
- data/test/retry_criteria_test.rb +1 -1
- data/test/retry_exception_delay_test.rb +1 -1
- data/test/retry_inheriting_checks_test.rb +1 -1
- data/test/retry_test.rb +17 -2
- data/test/server_test.rb +1 -1
- data/test/test_helper.rb +14 -26
- data/test/test_jobs.rb +3 -1
- metadata +86 -24
data/HISTORY.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
##
|
1
|
+
## 1.0.0 (2012-09-07)
|
2
2
|
|
3
|
-
**INCLUDES NON-BACKWARDS COMPATIBLE CHANGES**
|
3
|
+
** !!! WARNING !!! INCLUDES NON-BACKWARDS COMPATIBLE CHANGES **
|
4
4
|
|
5
5
|
* Fixed issues related to infinate job retries and v1.20.0 of resque.
|
6
6
|
* Minimum gem dependency versions changed: resque >= 1.10.0, resque-scheduler >= 1.9.9
|
@@ -8,6 +8,7 @@
|
|
8
8
|
* Web interface will work without needing to `require` your job code. (n.b. less details avaialble via web).
|
9
9
|
* IMPORTANT: `#identifier` method has been namedspaced to `#retry_identifier`.
|
10
10
|
* Bugfix: `Remove` button on retry web interface was not working.
|
11
|
+
* Feature: Allow `tagging` exceptions with a module instead of an exception class. (@tils - Tilmann Singer)
|
11
12
|
|
12
13
|
## 0.2.2 (2011-12-08)
|
13
14
|
|
data/README.md
CHANGED
@@ -21,7 +21,7 @@ To install:
|
|
21
21
|
|
22
22
|
$ gem install resque-retry
|
23
23
|
|
24
|
-
If
|
24
|
+
If you're using [Bundler][bundler] to manage your dependencies, you should add `gem
|
25
25
|
'resque-retry'` to your projects `Gemfile`.
|
26
26
|
|
27
27
|
Add this to your `Rakefile`:
|
@@ -63,7 +63,7 @@ For more explanation and examples, please see the remaining documentation.
|
|
63
63
|
Failure Backend & Resque Web Additions
|
64
64
|
--------------------------------------
|
65
65
|
|
66
|
-
Lets say
|
66
|
+
Lets say you're using the Redis failure backend of resque (the default).
|
67
67
|
Every time a job fails, the failure queue is populated with the job and
|
68
68
|
exception details.
|
69
69
|
|
@@ -95,7 +95,7 @@ the Redis failed queue, like a normal failure *(without retry)* would.
|
|
95
95
|
|
96
96
|
### Resque Web Additions
|
97
97
|
|
98
|
-
If
|
98
|
+
If you're using the `MultipleWithRetrySuppression` failure backend, you should
|
99
99
|
also checkout the resque-web additions!
|
100
100
|
|
101
101
|
The new Retry tab displays delayed jobs with retry information; the number of
|
data/Rakefile
CHANGED
@@ -10,16 +10,17 @@ task :default => :test
|
|
10
10
|
##
|
11
11
|
# Test task.
|
12
12
|
Rake::TestTask.new(:test) do |task|
|
13
|
+
task.libs << 'test'
|
13
14
|
task.test_files = FileList['test/*_test.rb']
|
14
15
|
task.verbose = true
|
15
16
|
end
|
16
17
|
|
17
18
|
##
|
18
19
|
# docs task.
|
19
|
-
YARD::Rake::YardocTask.new :yardoc do |
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
YARD::Rake::YardocTask.new :yardoc do |task|
|
21
|
+
task.files = ['lib/**/*.rb']
|
22
|
+
task.options = ['--output-dir', 'doc/',
|
23
|
+
'--files', 'LICENSE HISTORY.md',
|
24
|
+
'--readme', 'README.md',
|
25
|
+
'--title', 'resque-retry documentation']
|
25
26
|
end
|
data/bin/test-rubies.sh
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# Perform some loose sanity checking that were in the correct directory first.
|
4
|
+
if [ ! -f "Gemfile" ] || [ ! -f "resque-retry.gemspec" ]; then
|
5
|
+
echo " ERROR: You may only run this script from the ROOT directory of the gem. Aborting."
|
6
|
+
exit 1
|
7
|
+
fi
|
8
|
+
|
9
|
+
# exit if anything fails.
|
10
|
+
set -e
|
11
|
+
# echo the commands we execute
|
12
|
+
set -o verbose
|
13
|
+
# ruby versions to test.
|
14
|
+
TEST_RUBIES="ruby-1.8.7-p358,ruby-1.9.2-p320,ruby-1.9.3-p0,jruby-1.6.7.2,rbx-2.0.testing"
|
15
|
+
# install dependencies.
|
16
|
+
rvm $TEST_RUBIES do bundle
|
17
|
+
# run unit tests
|
18
|
+
rvm $TEST_RUBIES do bundle exec rake
|
@@ -3,7 +3,7 @@ require 'resque/failure/multiple'
|
|
3
3
|
module Resque
|
4
4
|
module Failure
|
5
5
|
|
6
|
-
# A multiple failure backend, with retry suppression
|
6
|
+
# A multiple failure backend, with retry suppression
|
7
7
|
#
|
8
8
|
# For example: if you had a job that could retry 5 times, your failure
|
9
9
|
# backends are not notified unless the _final_ retry attempt also fails.
|
@@ -19,11 +19,13 @@ module Resque
|
|
19
19
|
class MultipleWithRetrySuppression < Multiple
|
20
20
|
include Resque::Helpers
|
21
21
|
|
22
|
-
# Called when the job fails
|
22
|
+
# Called when the job fails
|
23
23
|
#
|
24
24
|
# If the job will retry, suppress the failure from the other backends.
|
25
25
|
# Store the lastest failure information in redis, used by the web
|
26
26
|
# interface.
|
27
|
+
#
|
28
|
+
# @api private
|
27
29
|
def save
|
28
30
|
if !(retryable? && retrying?)
|
29
31
|
cleanup_retry_failure_log!
|
@@ -43,12 +45,16 @@ module Resque
|
|
43
45
|
end
|
44
46
|
end
|
45
47
|
|
46
|
-
# Expose this for the hook's use
|
48
|
+
# Expose this for the hook's use
|
49
|
+
#
|
50
|
+
# @api public
|
47
51
|
def self.failure_key(retry_key)
|
48
52
|
'failure-' + retry_key
|
49
53
|
end
|
50
54
|
|
51
55
|
protected
|
56
|
+
|
57
|
+
# Return the class/module of the failed job.
|
52
58
|
def klass
|
53
59
|
constantize(payload['class'])
|
54
60
|
end
|
@@ -37,24 +37,30 @@ module Resque
|
|
37
37
|
module ExponentialBackoff
|
38
38
|
include Resque::Plugins::Retry
|
39
39
|
|
40
|
-
# Defaults to the number of delays in the backoff strategy
|
40
|
+
# Defaults to the number of delays in the backoff strategy
|
41
41
|
#
|
42
42
|
# @return [Number] maximum number of retries
|
43
|
+
#
|
44
|
+
# @api private
|
43
45
|
def retry_limit
|
44
46
|
@retry_limit ||= backoff_strategy.length
|
45
47
|
end
|
46
48
|
|
47
|
-
# Selects the delay from the backoff strategy
|
49
|
+
# Selects the delay from the backoff strategy
|
48
50
|
#
|
49
51
|
# @return [Number] seconds to delay until the next retry.
|
52
|
+
#
|
53
|
+
# @api private
|
50
54
|
def retry_delay
|
51
55
|
backoff_strategy[retry_attempt] || backoff_strategy.last
|
52
56
|
end
|
53
57
|
|
54
58
|
# @abstract
|
55
|
-
# The backoff strategy is used to vary the delay between retry attempts
|
59
|
+
# The backoff strategy is used to vary the delay between retry attempts
|
56
60
|
#
|
57
61
|
# @return [Array] array of delays. index = retry attempt
|
62
|
+
#
|
63
|
+
# @api public
|
58
64
|
def backoff_strategy
|
59
65
|
@backoff_strategy ||= [0, 60, 600, 3600, 10_800, 21_600]
|
60
66
|
end
|
data/lib/resque/plugins/retry.rb
CHANGED
@@ -35,6 +35,8 @@ module Resque
|
|
35
35
|
module Retry
|
36
36
|
|
37
37
|
# Copy retry criteria checks on inheritance.
|
38
|
+
#
|
39
|
+
# @api private
|
38
40
|
def inherited(subclass)
|
39
41
|
super(subclass)
|
40
42
|
subclass.instance_variable_set("@retry_criteria_checks", retry_criteria_checks.dup)
|
@@ -45,10 +47,12 @@ module Resque
|
|
45
47
|
# are many/long or may not cleanly convert to strings.
|
46
48
|
#
|
47
49
|
# Builds a retry identifier using the job arguments. This identifier
|
48
|
-
# is used as part of the redis key
|
50
|
+
# is used as part of the redis key
|
49
51
|
#
|
50
52
|
# @param [Array] args job arguments
|
51
53
|
# @return [String] job identifier
|
54
|
+
#
|
55
|
+
# @api public
|
52
56
|
def retry_identifier(*args)
|
53
57
|
args_string = args.join('-')
|
54
58
|
args_string.empty? ? nil : args_string
|
@@ -58,34 +62,42 @@ module Resque
|
|
58
62
|
# attempts.
|
59
63
|
#
|
60
64
|
# @return [String] redis key
|
65
|
+
#
|
66
|
+
# @api public
|
61
67
|
def redis_retry_key(*args)
|
62
68
|
['resque-retry', name, retry_identifier(*args)].compact.join(":").gsub(/\s/, '')
|
63
69
|
end
|
64
70
|
|
65
|
-
# Maximum number of retrys we can attempt to successfully perform the job
|
71
|
+
# Maximum number of retrys we can attempt to successfully perform the job
|
66
72
|
#
|
67
73
|
# A retry limit of 0 will *never* retry.
|
68
74
|
# A retry limit of -1 or below will retry forever.
|
69
75
|
#
|
70
76
|
# @return [Fixnum]
|
77
|
+
#
|
78
|
+
# @api public
|
71
79
|
def retry_limit
|
72
80
|
@retry_limit ||= 1
|
73
81
|
end
|
74
82
|
|
75
|
-
# Number of retry attempts used to try and perform the job
|
83
|
+
# Number of retry attempts used to try and perform the job
|
76
84
|
#
|
77
85
|
# The real value is kept in Redis, it is accessed and incremented using
|
78
86
|
# a before_perform hook.
|
79
87
|
#
|
80
88
|
# @return [Fixnum] number of attempts
|
89
|
+
#
|
90
|
+
# @api public
|
81
91
|
def retry_attempt
|
82
92
|
@retry_attempt ||= 0
|
83
93
|
end
|
84
94
|
|
85
95
|
# @abstract
|
86
|
-
# Number of seconds to delay until the job is retried
|
87
|
-
#
|
96
|
+
# Number of seconds to delay until the job is retried
|
97
|
+
#
|
88
98
|
# @return [Number] number of seconds to delay
|
99
|
+
#
|
100
|
+
# @api public
|
89
101
|
def retry_delay(exception_class = nil)
|
90
102
|
if @retry_exceptions.is_a?(Hash)
|
91
103
|
delay = @retry_exceptions[exception_class] || 0
|
@@ -100,37 +112,62 @@ module Resque
|
|
100
112
|
# Number of seconds to sleep after job is requeued
|
101
113
|
#
|
102
114
|
# @return [Number] number of seconds to sleep
|
115
|
+
#
|
116
|
+
# @api public
|
103
117
|
def sleep_after_requeue
|
104
118
|
@sleep_after_requeue ||= 0
|
105
119
|
end
|
106
120
|
|
121
|
+
# @abstract
|
122
|
+
# Specify another resque job (module or class) to delegate retry duties
|
123
|
+
# to upon failure
|
124
|
+
#
|
125
|
+
# @return [Object, nil] class or module if delegate on failure, otherwise nil
|
126
|
+
#
|
127
|
+
# @api public
|
107
128
|
def retry_job_delegate
|
108
129
|
@retry_job_delegate ||= nil
|
109
130
|
end
|
110
131
|
|
111
132
|
# @abstract
|
112
133
|
# Modify the arguments used to retry the job. Use this to do something
|
113
|
-
# other than try the exact same job again
|
134
|
+
# other than try the exact same job again
|
114
135
|
#
|
115
136
|
# @return [Array] new job arguments
|
137
|
+
#
|
138
|
+
# @api public
|
116
139
|
def args_for_retry(*args)
|
117
140
|
args
|
118
141
|
end
|
119
142
|
|
120
|
-
# Convenience method to test whether you may retry on a given
|
143
|
+
# Convenience method to test whether you may retry on a given
|
144
|
+
# exception
|
145
|
+
#
|
146
|
+
# @param [Exception] an instance of Exception. Deprecated: can
|
147
|
+
# also be a Class
|
121
148
|
#
|
122
149
|
# @return [Boolean]
|
150
|
+
#
|
151
|
+
# @api public
|
123
152
|
def retry_exception?(exception)
|
124
153
|
return true if retry_exceptions.nil?
|
125
|
-
!! retry_exceptions.any?
|
154
|
+
!! retry_exceptions.any? do |ex|
|
155
|
+
if exception.is_a?(Class)
|
156
|
+
ex >= exception
|
157
|
+
else
|
158
|
+
ex === exception
|
159
|
+
end
|
160
|
+
end
|
126
161
|
end
|
127
162
|
|
128
163
|
# @abstract
|
129
|
-
# Controls what exceptions may be retried
|
164
|
+
# Controls what exceptions may be retried
|
130
165
|
#
|
131
166
|
# Default: `nil` - this will retry all exceptions.
|
132
|
-
#
|
167
|
+
#
|
133
168
|
# @return [Array, nil]
|
169
|
+
#
|
170
|
+
# @api public
|
134
171
|
def retry_exceptions
|
135
172
|
if @retry_exceptions.is_a?(Hash)
|
136
173
|
@retry_exceptions.keys
|
@@ -139,17 +176,19 @@ module Resque
|
|
139
176
|
end
|
140
177
|
end
|
141
178
|
|
142
|
-
# Test if the retry criteria is valid
|
179
|
+
# Test if the retry criteria is valid
|
143
180
|
#
|
144
181
|
# @param [Exception] exception
|
145
182
|
# @param [Array] args job arguments
|
146
183
|
# @return [Boolean]
|
184
|
+
#
|
185
|
+
# @api public
|
147
186
|
def retry_criteria_valid?(exception, *args)
|
148
187
|
# if the retry limit was reached, dont bother checking anything else.
|
149
188
|
return false if retry_limit_reached?
|
150
189
|
|
151
190
|
# We always want to retry if the exception matches.
|
152
|
-
should_retry = retry_exception?(exception
|
191
|
+
should_retry = retry_exception?(exception)
|
153
192
|
|
154
193
|
# call user retry criteria check blocks.
|
155
194
|
retry_criteria_checks.each do |criteria_check|
|
@@ -159,17 +198,20 @@ module Resque
|
|
159
198
|
should_retry
|
160
199
|
end
|
161
200
|
|
162
|
-
# Retry criteria checks
|
201
|
+
# Retry criteria checks
|
163
202
|
#
|
164
203
|
# @return [Array]
|
204
|
+
#
|
205
|
+
# @api public
|
165
206
|
def retry_criteria_checks
|
166
207
|
@retry_criteria_checks ||= []
|
167
|
-
@retry_criteria_checks
|
168
208
|
end
|
169
209
|
|
170
|
-
# Test if the retry limit has been reached
|
210
|
+
# Test if the retry limit has been reached
|
171
211
|
#
|
172
212
|
# @return [Boolean]
|
213
|
+
#
|
214
|
+
# @api public
|
173
215
|
def retry_limit_reached?
|
174
216
|
if retry_limit == 0
|
175
217
|
true
|
@@ -181,7 +223,7 @@ module Resque
|
|
181
223
|
end
|
182
224
|
|
183
225
|
# Register a retry criteria check callback to be run before retrying
|
184
|
-
# the job again
|
226
|
+
# the job again
|
185
227
|
#
|
186
228
|
# If any callback returns `true`, the job will be retried.
|
187
229
|
#
|
@@ -200,11 +242,15 @@ module Resque
|
|
200
242
|
# @yieldparam exception [Exception] the exception that was raised
|
201
243
|
# @yieldparam args [Array] job arguments
|
202
244
|
# @yieldreturn [Boolean] false == dont retry, true = can retry
|
245
|
+
#
|
246
|
+
# @api public
|
203
247
|
def retry_criteria_check(&block)
|
204
248
|
retry_criteria_checks << block
|
205
249
|
end
|
206
250
|
|
207
|
-
# Retries the job
|
251
|
+
# Retries the job
|
252
|
+
#
|
253
|
+
# @api private
|
208
254
|
def try_again(exception, *args)
|
209
255
|
# some plugins define retry_delay and have it take no arguments, so rather than break those,
|
210
256
|
# we'll just check here to see whether it takes the additional exception class argument or not
|
@@ -225,9 +271,11 @@ module Resque
|
|
225
271
|
sleep(sleep_after_requeue) if sleep_after_requeue > 0
|
226
272
|
end
|
227
273
|
|
228
|
-
# Resque before_perform hook
|
274
|
+
# Resque before_perform hook
|
229
275
|
#
|
230
276
|
# Increments and sets the `@retry_attempt` count.
|
277
|
+
#
|
278
|
+
# @api private
|
231
279
|
def before_perform_retry(*args)
|
232
280
|
@on_failure_retry_hook_already_called = false
|
233
281
|
|
@@ -237,21 +285,25 @@ module Resque
|
|
237
285
|
@retry_attempt = Resque.redis.incr(retry_key) # increment by 1.
|
238
286
|
end
|
239
287
|
|
240
|
-
# Resque after_perform hook
|
288
|
+
# Resque after_perform hook
|
241
289
|
#
|
242
290
|
# Deletes retry attempt count from Redis.
|
291
|
+
#
|
292
|
+
# @api private
|
243
293
|
def after_perform_retry(*args)
|
244
294
|
clean_retry_key(*args)
|
245
295
|
end
|
246
296
|
|
247
|
-
# Resque on_failure hook
|
297
|
+
# Resque on_failure hook
|
248
298
|
#
|
249
299
|
# Checks if our retry criteria is valid, if it is we try again.
|
250
300
|
# Otherwise the retry attempt count is deleted from Redis.
|
251
301
|
#
|
252
|
-
#
|
302
|
+
# @note This hook will only allow execution once per job perform attempt.
|
253
303
|
# This was added because Resque v1.20.0 calls the hook twice.
|
254
304
|
# IMO; this isn't something resque-retry should have to worry about!
|
305
|
+
#
|
306
|
+
# @api private
|
255
307
|
def on_failure_retry(exception, *args)
|
256
308
|
return if @on_failure_retry_hook_already_called
|
257
309
|
|
@@ -264,6 +316,11 @@ module Resque
|
|
264
316
|
@on_failure_retry_hook_already_called = true
|
265
317
|
end
|
266
318
|
|
319
|
+
# Used to perform retry criteria check blocks under the job instance's context
|
320
|
+
#
|
321
|
+
# @return [Object] return value of the criteria check
|
322
|
+
#
|
323
|
+
# @api private
|
267
324
|
def instance_exec(*args, &block)
|
268
325
|
mname = "__instance_exec_#{Thread.current.object_id.abs}"
|
269
326
|
class << self; self end.class_eval{ define_method(mname, &block) }
|
@@ -275,6 +332,9 @@ module Resque
|
|
275
332
|
ret
|
276
333
|
end
|
277
334
|
|
335
|
+
# Clean up retry state from redis once done
|
336
|
+
#
|
337
|
+
# @api private
|
278
338
|
def clean_retry_key(*args)
|
279
339
|
Resque.redis.del(redis_retry_key(*args))
|
280
340
|
end
|
data/lib/resque-retry/server.rb
CHANGED
@@ -1,51 +1,16 @@
|
|
1
1
|
require 'cgi'
|
2
|
+
require 'resque/server'
|
3
|
+
require 'resque_scheduler/server'
|
2
4
|
|
3
5
|
# Extend Resque::Server to add tabs.
|
4
6
|
module ResqueRetry
|
5
7
|
module Server
|
6
8
|
|
9
|
+
# Adds `resque-retry` web interface elements to `resque-web`
|
10
|
+
#
|
11
|
+
# @api private
|
7
12
|
def self.included(base)
|
8
13
|
base.class_eval {
|
9
|
-
helpers do
|
10
|
-
# builds a retry key for the specified job.
|
11
|
-
def retry_key_for_job(job)
|
12
|
-
begin
|
13
|
-
klass = Resque.constantize(job['class'])
|
14
|
-
if klass.respond_to?(:redis_retry_key)
|
15
|
-
klass.redis_retry_key(job['args'])
|
16
|
-
else
|
17
|
-
nil
|
18
|
-
end
|
19
|
-
rescue NameError
|
20
|
-
nil
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
# gets the number of retry attempts for a job.
|
25
|
-
def retry_attempts_for_job(job)
|
26
|
-
Resque.redis.get(retry_key_for_job(job))
|
27
|
-
end
|
28
|
-
|
29
|
-
# gets the failure details hash for a job.
|
30
|
-
def retry_failure_details(retry_key)
|
31
|
-
Resque.decode(Resque.redis.get("failure_#{retry_key}"))
|
32
|
-
end
|
33
|
-
|
34
|
-
# reads a 'local' template file.
|
35
|
-
def local_template(path)
|
36
|
-
# Is there a better way to specify alternate template locations with sinatra?
|
37
|
-
File.read(File.join(File.dirname(__FILE__), "server/views/#{path}"))
|
38
|
-
end
|
39
|
-
|
40
|
-
# cancels job retry
|
41
|
-
def cancel_retry(job)
|
42
|
-
klass = Resque.constantize(job['class'])
|
43
|
-
retry_key = retry_key_for_job(job)
|
44
|
-
Resque.remove_delayed(klass, *job['args'])
|
45
|
-
Resque.redis.del("failure_#{retry_key}")
|
46
|
-
Resque.redis.del(retry_key)
|
47
|
-
end
|
48
|
-
end
|
49
14
|
|
50
15
|
get '/retry' do
|
51
16
|
erb local_template('retry.erb')
|
@@ -70,10 +35,53 @@ module ResqueRetry
|
|
70
35
|
}
|
71
36
|
end
|
72
37
|
|
38
|
+
# Helper methods used by retry tab.
|
39
|
+
module Helpers
|
40
|
+
# builds a retry key for the specified job.
|
41
|
+
def retry_key_for_job(job)
|
42
|
+
begin
|
43
|
+
klass = Resque.constantize(job['class'])
|
44
|
+
if klass.respond_to?(:redis_retry_key)
|
45
|
+
klass.redis_retry_key(job['args'])
|
46
|
+
else
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
rescue NameError
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# gets the number of retry attempts for a job.
|
55
|
+
def retry_attempts_for_job(job)
|
56
|
+
Resque.redis.get(retry_key_for_job(job))
|
57
|
+
end
|
58
|
+
|
59
|
+
# gets the failure details hash for a job.
|
60
|
+
def retry_failure_details(retry_key)
|
61
|
+
Resque.decode(Resque.redis.get("failure-#{retry_key}"))
|
62
|
+
end
|
63
|
+
|
64
|
+
# reads a 'local' template file.
|
65
|
+
def local_template(path)
|
66
|
+
# Is there a better way to specify alternate template locations with sinatra?
|
67
|
+
File.read(File.join(File.dirname(__FILE__), "server/views/#{path}"))
|
68
|
+
end
|
69
|
+
|
70
|
+
# cancels job retry
|
71
|
+
def cancel_retry(job)
|
72
|
+
klass = Resque.constantize(job['class'])
|
73
|
+
retry_key = retry_key_for_job(job)
|
74
|
+
Resque.remove_delayed(klass, *job['args'])
|
75
|
+
Resque.redis.del("failure-#{retry_key}")
|
76
|
+
Resque.redis.del(retry_key)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
73
80
|
end
|
74
81
|
end
|
75
82
|
|
76
83
|
Resque::Server.tabs << 'Retry'
|
77
84
|
Resque::Server.class_eval do
|
78
85
|
include ResqueRetry::Server
|
86
|
+
helpers ResqueRetry::Server::Helpers
|
79
87
|
end
|
@@ -1,4 +1,17 @@
|
|
1
|
-
require
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
# Mock failure backend for testing MultipleWithRetrySuppression
|
4
|
+
class MockFailureBackend < Resque::Failure::Base
|
5
|
+
class << self
|
6
|
+
attr_accessor :errors
|
7
|
+
end
|
8
|
+
|
9
|
+
def save
|
10
|
+
self.class.errors << exception.to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
self.errors = []
|
14
|
+
end
|
2
15
|
|
3
16
|
class MultipleFailureTest < MiniTest::Unit::TestCase
|
4
17
|
|
@@ -19,7 +32,7 @@ class MultipleFailureTest < MiniTest::Unit::TestCase
|
|
19
32
|
end
|
20
33
|
|
21
34
|
def test_failure_is_passed_on_when_job_class_not_found
|
22
|
-
skip 'commit 7113b0df to `resque` gem means the failure backend is never called'
|
35
|
+
skip 'commit 7113b0df to `resque` gem means the failure backend is never called. effects resque v1.20.0'
|
23
36
|
new_job_class = Class.new(LimitThreeJob).tap { |klass| klass.send(:instance_variable_set, :@queue, LimitThreeJob.instance_variable_get(:@queue)) }
|
24
37
|
Object.send(:const_set, 'LimitThreeJobTemp', new_job_class)
|
25
38
|
Resque.enqueue(LimitThreeJobTemp)
|
data/test/redis-test.conf
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Redis configuration file
|
1
|
+
# Redis configuration file
|
2
2
|
|
3
3
|
# By default Redis does not run as a daemon. Use 'yes' if you need it.
|
4
4
|
# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
|
@@ -6,7 +6,7 @@ daemonize yes
|
|
6
6
|
|
7
7
|
# When run as a daemon, Redis write a pid file in /var/run/redis.pid by default.
|
8
8
|
# You can specify a custom pid file location here.
|
9
|
-
pidfile ./test/redis-test.pid
|
9
|
+
#pidfile ./test/redis-test.pid
|
10
10
|
|
11
11
|
# Accept connections on the specified port, default is 6379
|
12
12
|
port 9736
|
@@ -30,16 +30,16 @@ timeout 300
|
|
30
30
|
# after 900 sec (15 min) if at least 1 key changed
|
31
31
|
# after 300 sec (5 min) if at least 10 keys changed
|
32
32
|
# after 60 sec if at least 10000 keys changed
|
33
|
-
save 900 1
|
34
|
-
save 300 10
|
35
|
-
save 60 10000
|
33
|
+
#save 900 1
|
34
|
+
#save 300 10
|
35
|
+
#save 60 10000
|
36
36
|
|
37
37
|
# The filename where to dump the DB
|
38
|
-
dbfilename dump.rdb
|
38
|
+
#dbfilename dump.rdb
|
39
39
|
|
40
40
|
# For default save/load DB in/from the working directory
|
41
41
|
# Note that you must specify a directory not a file name.
|
42
|
-
dir ./test/
|
42
|
+
#dir ./test/
|
43
43
|
|
44
44
|
# Set server verbosity to 'debug'
|
45
45
|
# it can be one of:
|
data/test/resque_test.rb
CHANGED
data/test/retry_criteria_test.rb
CHANGED
data/test/retry_test.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'test_helper'
|
2
2
|
|
3
3
|
class RetryTest < MiniTest::Unit::TestCase
|
4
4
|
def setup
|
@@ -108,13 +108,17 @@ class RetryTest < MiniTest::Unit::TestCase
|
|
108
108
|
assert_equal 0, Resque.info[:pending], 'pending jobs'
|
109
109
|
end
|
110
110
|
|
111
|
-
def
|
111
|
+
def test_can_determine_if_exception_may_be_retried_with_defaults
|
112
112
|
assert_equal true, RetryDefaultsJob.retry_exception?(StandardError), 'StandardError may retry'
|
113
113
|
assert_equal true, RetryDefaultsJob.retry_exception?(CustomException), 'CustomException may retry'
|
114
114
|
assert_equal true, RetryDefaultsJob.retry_exception?(HierarchyCustomException), 'HierarchyCustomException may retry'
|
115
|
+
end
|
115
116
|
|
117
|
+
def test_can_determine_if_exception_may_be_retried_with_custom_retry_exceptions
|
118
|
+
assert_equal false, RetryCustomExceptionsJob.retry_exception?(StandardError), 'StandardError may not retry'
|
116
119
|
assert_equal true, RetryCustomExceptionsJob.retry_exception?(CustomException), 'CustomException may retry'
|
117
120
|
assert_equal true, RetryCustomExceptionsJob.retry_exception?(HierarchyCustomException), 'HierarchyCustomException may retry'
|
121
|
+
assert_equal true, RetryCustomExceptionsJob.retry_exception?(RuntimeError.new.extend(CustomExceptionModule)), 'Extended with CustomExceptionModule may retry'
|
118
122
|
assert_equal false, RetryCustomExceptionsJob.retry_exception?(AnotherCustomException), 'AnotherCustomException may not retry'
|
119
123
|
end
|
120
124
|
|
@@ -130,6 +134,17 @@ class RetryTest < MiniTest::Unit::TestCase
|
|
130
134
|
assert_equal 2, Resque.info[:pending], 'pending jobs'
|
131
135
|
end
|
132
136
|
|
137
|
+
def test_retry_if_failed_and_exception_may_retry_because_of_included_module
|
138
|
+
Resque.enqueue(RetryCustomExceptionsJob, 'tagged CustomException')
|
139
|
+
4.times do
|
140
|
+
perform_next_job(@worker)
|
141
|
+
end
|
142
|
+
|
143
|
+
assert_equal 4, Resque.info[:failed], 'failed jobs'
|
144
|
+
assert_equal 4, Resque.info[:processed], 'processed job'
|
145
|
+
assert_equal 1, Resque.info[:pending], 'pending jobs'
|
146
|
+
end
|
147
|
+
|
133
148
|
def test_do_not_retry_if_failed_and_exception_does_not_allow_retry
|
134
149
|
Resque.enqueue(RetryCustomExceptionsJob, AnotherCustomException)
|
135
150
|
Resque.enqueue(RetryCustomExceptionsJob, RuntimeError)
|
data/test/server_test.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -3,12 +3,14 @@ $LOAD_PATH.unshift dir + '/../lib'
|
|
3
3
|
$TESTING = true
|
4
4
|
|
5
5
|
require 'rubygems'
|
6
|
+
require 'timeout'
|
6
7
|
require 'minitest/unit'
|
7
8
|
require 'minitest/pride'
|
8
9
|
require 'rack/test'
|
9
10
|
require 'mocha'
|
10
11
|
|
11
|
-
|
12
|
+
# Run code coverage in MRI 1.9 only.
|
13
|
+
if RUBY_VERSION >= '1.9' && RUBY_ENGINE == 'ruby'
|
12
14
|
require 'simplecov'
|
13
15
|
SimpleCov.start do
|
14
16
|
add_filter '/test/'
|
@@ -18,45 +20,31 @@ end
|
|
18
20
|
require 'resque-retry'
|
19
21
|
require dir + '/test_jobs'
|
20
22
|
|
21
|
-
# make sure we can run redis
|
22
|
-
if !system(
|
23
|
-
puts '', "**
|
24
|
-
puts "** try running `sudo rake install`"
|
23
|
+
# make sure we can run redis-server
|
24
|
+
if !system('which redis-server')
|
25
|
+
puts '', "** `redis-server` was not found in your PATH"
|
25
26
|
abort ''
|
26
27
|
end
|
27
28
|
|
29
|
+
# make sure we can shutdown the server using cli.
|
30
|
+
if !system('which redis-cli')
|
31
|
+
puts '', "** `redis-cli` was not found in your PATH"
|
32
|
+
abort ''
|
33
|
+
end
|
28
34
|
|
29
|
-
#
|
30
|
-
#
|
35
|
+
# This code is run `at_exit` to setup everything before running the tests.
|
36
|
+
# Redis server is started before this code block runs.
|
31
37
|
at_exit do
|
32
38
|
next if $!
|
33
39
|
|
34
40
|
exit_code = MiniTest::Unit.new.run(ARGV)
|
35
|
-
|
36
|
-
pid = `ps -e -o pid,command | grep [r]edis-test`.split(" ")[0]
|
37
|
-
puts "Killing test redis server..."
|
38
|
-
`rm -f #{dir}/dump.rdb`
|
39
|
-
`kill -9 #{pid}`
|
40
|
-
exit exit_code
|
41
|
+
`redis-cli -p 9736 shutdown nosave`
|
41
42
|
end
|
42
43
|
|
43
44
|
puts "Starting redis for testing at localhost:9736..."
|
44
45
|
`redis-server #{dir}/redis-test.conf`
|
45
46
|
Resque.redis = '127.0.0.1:9736'
|
46
47
|
|
47
|
-
# Mock failure backend for testing MultipleWithRetrySuppression
|
48
|
-
class MockFailureBackend < Resque::Failure::Base
|
49
|
-
class << self
|
50
|
-
attr_accessor :errors
|
51
|
-
end
|
52
|
-
|
53
|
-
def save
|
54
|
-
self.class.errors << exception.to_s
|
55
|
-
end
|
56
|
-
|
57
|
-
self.errors = []
|
58
|
-
end
|
59
|
-
|
60
48
|
# Test helpers
|
61
49
|
class MiniTest::Unit::TestCase
|
62
50
|
def perform_next_job(worker, &block)
|
data/test/test_jobs.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
CustomException = Class.new(StandardError)
|
2
|
+
CustomExceptionModule = Module.new
|
2
3
|
HierarchyCustomException = Class.new(CustomException)
|
3
4
|
AnotherCustomException = Class.new(StandardError)
|
4
5
|
|
@@ -155,12 +156,13 @@ class RetryCustomExceptionsJob < RetryDefaultsJob
|
|
155
156
|
@queue = :testing
|
156
157
|
|
157
158
|
@retry_limit = 5
|
158
|
-
@retry_exceptions = [CustomException, HierarchyCustomException]
|
159
|
+
@retry_exceptions = [CustomException, CustomExceptionModule, HierarchyCustomException]
|
159
160
|
|
160
161
|
def self.perform(exception)
|
161
162
|
case exception
|
162
163
|
when 'CustomException' then raise CustomException
|
163
164
|
when 'HierarchyCustomException' then raise HierarchyCustomException
|
165
|
+
when 'tagged CustomException' then raise AnotherCustomException.new.extend(CustomExceptionModule)
|
164
166
|
when 'AnotherCustomException' then raise AnotherCustomException
|
165
167
|
else raise StandardError
|
166
168
|
end
|
metadata
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: resque-retry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0
|
5
|
-
prerelease:
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Luke Antins
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-
|
13
|
+
date: 2012-09-07 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: resque
|
17
|
-
requirement:
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,10 +22,15 @@ dependencies:
|
|
22
22
|
version: 1.10.0
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements:
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ! '>='
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: 1.10.0
|
26
31
|
- !ruby/object:Gem::Dependency
|
27
32
|
name: resque-scheduler
|
28
|
-
requirement:
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
29
34
|
none: false
|
30
35
|
requirements:
|
31
36
|
- - ! '>='
|
@@ -33,10 +38,15 @@ dependencies:
|
|
33
38
|
version: 1.9.9
|
34
39
|
type: :runtime
|
35
40
|
prerelease: false
|
36
|
-
version_requirements:
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 1.9.9
|
37
47
|
- !ruby/object:Gem::Dependency
|
38
48
|
name: rake
|
39
|
-
requirement:
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
40
50
|
none: false
|
41
51
|
requirements:
|
42
52
|
- - ! '>='
|
@@ -44,10 +54,15 @@ dependencies:
|
|
44
54
|
version: '0'
|
45
55
|
type: :development
|
46
56
|
prerelease: false
|
47
|
-
version_requirements:
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
48
63
|
- !ruby/object:Gem::Dependency
|
49
64
|
name: minitest
|
50
|
-
requirement:
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
51
66
|
none: false
|
52
67
|
requirements:
|
53
68
|
- - ! '>='
|
@@ -55,10 +70,15 @@ dependencies:
|
|
55
70
|
version: '0'
|
56
71
|
type: :development
|
57
72
|
prerelease: false
|
58
|
-
version_requirements:
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
59
79
|
- !ruby/object:Gem::Dependency
|
60
80
|
name: rack-test
|
61
|
-
requirement:
|
81
|
+
requirement: !ruby/object:Gem::Requirement
|
62
82
|
none: false
|
63
83
|
requirements:
|
64
84
|
- - ! '>='
|
@@ -66,10 +86,31 @@ dependencies:
|
|
66
86
|
version: '0'
|
67
87
|
type: :development
|
68
88
|
prerelease: false
|
69
|
-
version_requirements:
|
89
|
+
version_requirements: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
70
95
|
- !ruby/object:Gem::Dependency
|
71
96
|
name: yard
|
72
|
-
requirement:
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ! '>='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
type: :development
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rdiscount
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
73
114
|
none: false
|
74
115
|
requirements:
|
75
116
|
- - ! '>='
|
@@ -77,10 +118,15 @@ dependencies:
|
|
77
118
|
version: '0'
|
78
119
|
type: :development
|
79
120
|
prerelease: false
|
80
|
-
version_requirements:
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ! '>='
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
81
127
|
- !ruby/object:Gem::Dependency
|
82
128
|
name: json
|
83
|
-
requirement:
|
129
|
+
requirement: !ruby/object:Gem::Requirement
|
84
130
|
none: false
|
85
131
|
requirements:
|
86
132
|
- - ! '>='
|
@@ -88,10 +134,15 @@ dependencies:
|
|
88
134
|
version: '0'
|
89
135
|
type: :development
|
90
136
|
prerelease: false
|
91
|
-
version_requirements:
|
137
|
+
version_requirements: !ruby/object:Gem::Requirement
|
138
|
+
none: false
|
139
|
+
requirements:
|
140
|
+
- - ! '>='
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
92
143
|
- !ruby/object:Gem::Dependency
|
93
144
|
name: simplecov
|
94
|
-
requirement:
|
145
|
+
requirement: !ruby/object:Gem::Requirement
|
95
146
|
none: false
|
96
147
|
requirements:
|
97
148
|
- - ! '>='
|
@@ -99,10 +150,15 @@ dependencies:
|
|
99
150
|
version: 0.3.0
|
100
151
|
type: :development
|
101
152
|
prerelease: false
|
102
|
-
version_requirements:
|
153
|
+
version_requirements: !ruby/object:Gem::Requirement
|
154
|
+
none: false
|
155
|
+
requirements:
|
156
|
+
- - ! '>='
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: 0.3.0
|
103
159
|
- !ruby/object:Gem::Dependency
|
104
160
|
name: mocha
|
105
|
-
requirement:
|
161
|
+
requirement: !ruby/object:Gem::Requirement
|
106
162
|
none: false
|
107
163
|
requirements:
|
108
164
|
- - ! '>='
|
@@ -110,7 +166,12 @@ dependencies:
|
|
110
166
|
version: '0'
|
111
167
|
type: :development
|
112
168
|
prerelease: false
|
113
|
-
version_requirements:
|
169
|
+
version_requirements: !ruby/object:Gem::Requirement
|
170
|
+
none: false
|
171
|
+
requirements:
|
172
|
+
- - ! '>='
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: '0'
|
114
175
|
description: ! " resque-retry provides retry, delay and exponential backoff support
|
115
176
|
for\n resque jobs.\n\n Features:\n\n * Redis backed retry count/limit.\n * Retry
|
116
177
|
on all or specific exceptions.\n * Exponential backoff (varying the delay between
|
@@ -125,6 +186,7 @@ files:
|
|
125
186
|
- Rakefile
|
126
187
|
- README.md
|
127
188
|
- HISTORY.md
|
189
|
+
- bin/test-rubies.sh
|
128
190
|
- test/exponential_backoff_test.rb
|
129
191
|
- test/multiple_failure_test.rb
|
130
192
|
- test/redis-test.conf
|
@@ -158,12 +220,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
158
220
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
159
221
|
none: false
|
160
222
|
requirements:
|
161
|
-
- - ! '
|
223
|
+
- - ! '>='
|
162
224
|
- !ruby/object:Gem::Version
|
163
|
-
version:
|
225
|
+
version: '0'
|
164
226
|
requirements: []
|
165
227
|
rubyforge_project:
|
166
|
-
rubygems_version: 1.8.
|
228
|
+
rubygems_version: 1.8.24
|
167
229
|
signing_key:
|
168
230
|
specification_version: 3
|
169
231
|
summary: A resque plugin; provides retry, delay and exponential backoff support for
|