resque-retry 1.0.0.a → 1.0.0
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.
- 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
|