resque-retry 0.0.6 → 0.1.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 CHANGED
@@ -1,4 +1,10 @@
1
- ## 0.0.6 (2010-07-11)
1
+ ## 0.1.0 (2010-08-29)
2
+
3
+ * Feature: Multiple failure backend with retry suppression.
4
+ * Feature: resque-web tab showing retry information.
5
+ * Improved README documentation, added a 'Quick Start' section.
6
+
7
+ ## 0.0.6 (2010-07-12)
2
8
 
3
9
  * Feature: Added support for custom retry criteria check callbacks.
4
10
 
data/README.md CHANGED
@@ -1,27 +1,120 @@
1
1
  resque-retry
2
2
  ============
3
3
 
4
- A [Resque][rq] plugin. Requires Resque 1.8.0 & [resque-scheduler][rqs]
4
+ A [Resque][rq] plugin. Requires Resque 1.8.0 & [resque-scheduler][rqs].
5
5
 
6
6
  resque-retry provides retry, delay and exponential backoff support for
7
7
  resque jobs.
8
8
 
9
- ### Features
9
+ * Redis backed retry count/limit.
10
+ * Retry on all or specific exceptions.
11
+ * Exponential backoff (varying the delay between retrys).
12
+ * Multiple failure backend with retry suppression & resque-web tab.
13
+ * Small & Extendable - plenty of places to override retry logic/settings.
10
14
 
11
- - Redis backed retry count/limit.
12
- - Retry on all or specific exceptions.
13
- - Exponential backoff (varying the delay between retrys).
14
- - Small & Extendable - plenty of places to override retry logic/settings.
15
+ Install & Quick Start
16
+ ---------------------
15
17
 
16
- Usage / Examples
17
- ----------------
18
+ To install:
18
19
 
19
- Just extend your module/class with this module, and your ready to retry!
20
+ $ gem install resque-retry
21
+
22
+ You'll want add this to your `Rakefile`:
23
+
24
+ require 'resque/tasks'
25
+ require 'resque_scheduler/tasks'
26
+
27
+ The delay between retry attempts is provided by [resque-scheduler][rqs].
28
+ You'll want to run the scheduler process, otherwise delayed retry attempts
29
+ will never perform:
30
+
31
+ $ rake resque:scheduler
32
+
33
+ Use the plugin:
34
+
35
+ require 'resque-retry'
36
+
37
+ class ExampleRetryJob
38
+ extend Resque::Plugins::Retry
39
+ @queue = :example_queue
40
+
41
+ @retry_limit = 3
42
+ @retry_delay = 60
43
+
44
+ def self.perform(*args)
45
+ # your magic/heavy lifting goes here.
46
+ end
47
+ end
48
+
49
+ Then start up a resque worker as normal:
50
+
51
+ $ QUEUE=* rake resque:work
52
+
53
+ Now if you ExampleRetryJob fails, it will be retried 3 times, with a 60 second
54
+ delay between attempts.
55
+
56
+ For more explanation and examples, please see the remaining documentation.
57
+
58
+ Failure Backend & Resque Web Additions
59
+ --------------------------------------
60
+
61
+ Lets say your using the Redis failure backend of resque (the default).
62
+ Every time a job fails, the failure queue is populated with the job and
63
+ exception details.
64
+
65
+ Normally this is useful, but if your jobs retry... it can cause a bit of a mess.
66
+
67
+ For example: given a job that retried 4 times before completing successful.
68
+ You'll have a lot of failures for the same job and you wont be sure if it
69
+ actually completed successfully just by just using the resque-web interface.
70
+
71
+ ### Failure Backend
72
+
73
+ `MultipleWithRetrySuppression` is a multiple failure backend, with retry suppression.
74
+
75
+ Here's an example, using the Redis failure backend:
76
+
77
+ require 'resque-retry'
78
+ require 'resque/failure/redis'
79
+
80
+ # require your jobs & application code.
81
+
82
+ Resque::Failure::MultipleWithRetrySuppression.classes = [Resque::Failure::Redis]
83
+ Resque::Failure.backend = Resque::Failure::MultipleWithRetrySuppression
84
+
85
+ If a job fails, but **can and will** retry, the failure details wont be
86
+ logged in the Redis failed queue *(visible via resque-web)*.
87
+
88
+ If the job fails, but **can't or won't** retry, the failure will be logged in
89
+ the Redis failed queue, like a normal failure *(without retry)* would.
90
+
91
+ ### Resque Web Additions
92
+
93
+ If your using the `MultipleWithRetrySuppression` failure backend, you should
94
+ also checkout the resque-web additions!
95
+
96
+ The new Retry tab displays delayed jobs with retry information; the number of
97
+ attempts and the exception details from the last failure.
98
+
99
+ Make sure you include this in your `config.ru` or similar file:
100
+
101
+ require 'resque-retry'
102
+ require 'resque-retry/server'
103
+
104
+ # require your jobs & application code.
105
+
106
+ run Resque::Server.new
107
+
108
+ Retry Options & Logic
109
+ ---------------------
110
+
111
+ Please take a look at the yardoc/code for more details on methods you may
112
+ wish to override.
20
113
 
21
114
  Customisation is pretty easy, the below examples should give you
22
115
  some ideas =), adapt for your own usage and feel free to pick and mix!
23
116
 
24
- ### Retry
117
+ ### Retry Defaults
25
118
 
26
119
  Retry the job **once** on failure, with zero delay.
27
120
 
@@ -135,13 +228,24 @@ return true.
135
228
  Use `@retry_exceptions = []` to **only** use callbacks, to determine if the
136
229
  job should retry.
137
230
 
138
- Customise & Extend
139
- ------------------
231
+ ### Retry Arguments
140
232
 
141
- Please take a look at the yardoc/code for more details on methods you may
142
- wish to override.
233
+ You may override `args_for_retry`, which is passed the current
234
+ job arguments, to modify the arguments for the next retry attempt.
235
+
236
+ class DeliverViaSMSC
237
+ extend Resque::Plugins::Retry
238
+ @queue = :mt_smsc_messages
143
239
 
144
- Some things worth noting:
240
+ # retry using the emergency SMSC.
241
+ def self.args_for_retry(smsc_id, mt_message)
242
+ [999, mt_message]
243
+ end
244
+
245
+ self.perform(smsc_id, mt_message)
246
+ heavy_lifting
247
+ end
248
+ end
145
249
 
146
250
  ### Job Identifier/Key
147
251
 
@@ -170,29 +274,16 @@ Or you can define the entire key by overriding `redis_retry_key`.
170
274
  end
171
275
  end
172
276
 
173
- ### Retry Arguments
277
+ Contributing/Pull Requests
278
+ --------------------------
174
279
 
175
- You may override `args_for_retry`, which is passed the current
176
- job arguments, to modify the arguments for the next retry attempt.
177
-
178
- class DeliverViaSMSC
179
- extend Resque::Plugins::Retry
180
- @queue = :mt_smsc_messages
181
-
182
- # retry using the emergency SMSC.
183
- def self.args_for_retry(smsc_id, mt_message)
184
- [999, mt_message]
185
- end
186
-
187
- self.perform(smsc_id, mt_message)
188
- heavy_lifting
189
- end
190
- end
191
-
192
- Install
193
- -------
194
-
195
- $ gem install resque-retry
280
+ * Yes please!
281
+ * Fork the project.
282
+ * Make your feature addition or bug fix.
283
+ * Add tests for it.
284
+ * Commit.
285
+ * Send me a pull request. Bonus points for topic branches.
286
+ * If you edit the gemspec/version etc, do it in another commit please.
196
287
 
197
288
  [rq]: http://github.com/defunkt/resque
198
- [rqs]: http://github.com/bvandenbos/resque-scheduler
289
+ [rqs]: http://github.com/bvandenbos/resque-scheduler
data/lib/resque-retry.rb CHANGED
@@ -2,4 +2,5 @@ require 'resque'
2
2
  require 'resque_scheduler'
3
3
 
4
4
  require 'resque/plugins/retry'
5
- require 'resque/plugins/exponential_backoff'
5
+ require 'resque/plugins/exponential_backoff'
6
+ require 'resque/failure/multiple_with_retry_suppression'
@@ -0,0 +1,51 @@
1
+ # Extend Resque::Server to add tabs.
2
+ module ResqueRetry
3
+ module Server
4
+
5
+ def self.included(base)
6
+ base.class_eval {
7
+ helpers do
8
+ # builds a retry key for the specified job.
9
+ def retry_key_for_job(job)
10
+ klass = Resque.constantize(job['class'])
11
+ if klass.respond_to?(:redis_retry_key)
12
+ klass.redis_retry_key(job['args'])
13
+ else
14
+ nil
15
+ end
16
+ end
17
+
18
+ # gets the number of retry attempts for a job.
19
+ def retry_attempts_for_job(job)
20
+ Resque.redis.get(retry_key_for_job(job))
21
+ end
22
+
23
+ # gets the failure details hash for a job.
24
+ def retry_failure_details(retry_key)
25
+ Resque.decode(Resque.redis["failure_#{retry_key}"])
26
+ end
27
+
28
+ # reads a 'local' template file.
29
+ def local_template(path)
30
+ # Is there a better way to specify alternate template locations with sinatra?
31
+ File.read(File.join(File.dirname(__FILE__), "server/views/#{path}"))
32
+ end
33
+ end
34
+
35
+ get '/retry' do
36
+ erb local_template('retry.erb')
37
+ end
38
+
39
+ get '/retry/:timestamp' do
40
+ erb local_template('retry_timestamp.erb')
41
+ end
42
+ }
43
+ end
44
+
45
+ end
46
+ end
47
+
48
+ Resque::Server.tabs << 'Retry'
49
+ Resque::Server.class_eval do
50
+ include ResqueRetry::Server
51
+ end
@@ -0,0 +1,48 @@
1
+ <h1>Delayed Jobs with Retry Information</h1>
2
+
3
+ <p class="intro">
4
+ This list below contains the timestamps for scheduled delayed jobs with
5
+ retry information.
6
+ </p>
7
+
8
+ <p class="sub">
9
+ Showing <%= start = params[:start].to_i %> to <%= start + 20 %> of
10
+ <b><%= size = resque.delayed_queue_schedule_size %></b> timestamps
11
+ </p>
12
+
13
+ <table>
14
+ <tr>
15
+ <th></th>
16
+ <th>Timestamp</th>
17
+ <th>Job count</th>
18
+ <th>Class</th>
19
+ <th>Args</th>
20
+ <th>Retry Attempts</th>
21
+ </tr>
22
+ <% timestamps = resque.delayed_queue_peek(start, start+20) %>
23
+ <% timestamps.each do |timestamp| %>
24
+ <% job = resque.delayed_timestamp_peek(timestamp, 0, 1).first %>
25
+ <% retry_key = retry_key_for_job(job) %>
26
+ <tr>
27
+ <td>
28
+ <form action="<%= url "/delayed/queue_now" %>" method="post">
29
+ <input type="hidden" name="timestamp" value="<%= timestamp.to_i %>">
30
+ <input type="submit" value="Queue now">
31
+ </form>
32
+ </td>
33
+ <td><a href="<%= url "retry/#{timestamp}" %>"><%= format_time(Time.at(timestamp)) %></a></td>
34
+ <td><%= delayed_timestamp_size = resque.delayed_timestamp_size(timestamp) %></td>
35
+ <% if job && delayed_timestamp_size == 1 %>
36
+ <td><%= h job['class'] %></td>
37
+ <td><%= h job['args'].inspect %></td>
38
+ <td><%= retry_attempts_for_job(job) || '<i>n/a</i>' %></td>
39
+ <% else %>
40
+ <td><a href="<%= url "retry/#{timestamp}" %>">see details</a></td>
41
+ <td></td>
42
+ <td></td>
43
+ <% end %>
44
+ </tr>
45
+ <% end %>
46
+ </table>
47
+
48
+ <%= partial :next_more, :start => start, :size => size %>
@@ -0,0 +1,59 @@
1
+ <% timestamp = params[:timestamp].to_i %>
2
+
3
+ <h1>
4
+ Delayed Jobs scheduled for <%= format_time(Time.at(timestamp)) %>
5
+ (with Retry Information)
6
+ </h1>
7
+
8
+ <p class="intro">
9
+ This list below contains the delayed jobs scheduled for the current
10
+ timestamp, with retry information.
11
+ </p>
12
+
13
+ <p class="sub">
14
+ Showing <%= start = params[:start].to_i %> to <%= start + 20 %> of
15
+ <b><%= size = resque.delayed_timestamp_size(timestamp) %></b> jobs
16
+ </p>
17
+
18
+ <table class="jobs">
19
+ <tr>
20
+ <th>Class</th>
21
+ <th>Args</th>
22
+ <th>Retry Attempts</th>
23
+ <th>Exception</th>
24
+ <th>Backtrace</th>
25
+ </tr>
26
+ <% jobs = resque.delayed_timestamp_peek(timestamp, start, 20) %>
27
+ <% jobs.each do |job| %>
28
+ <% retry_key = retry_key_for_job(job) %>
29
+ <% retry_attempts = retry_attempts_for_job(job) %>
30
+ <tr>
31
+ <td class="class"><%= h job['class'] %></td>
32
+ <td class="args"><%= h job['args'].inspect %></td>
33
+ <% if retry_attempts.nil? %>
34
+ <td colspan="3"><i>n/a - normal delayed job</i></td>
35
+ <% else %>
36
+ <td><%= retry_attempts %></td>
37
+ <% failure = retry_failure_details(retry_key) %>
38
+ <td><code><%= failure['exception'] %></code></td>
39
+ <td class="error">
40
+ <% if failure['backtrace'] %>
41
+ <a href="#" class="backtrace"><%= h(failure['error']) %></a>
42
+ <pre style="display:none"><%= h failure['backtrace'].join("\n") %></pre>
43
+ <% else %>
44
+ <%= h failure['error'] %>
45
+ <% end %>
46
+ </td>
47
+ <% end %>
48
+ </tr>
49
+ <% end %>
50
+ <% if jobs.empty? %>
51
+ <tr>
52
+ <td class="no-data" colspan="5">
53
+ There are no pending jobs scheduled for this time.
54
+ </td>
55
+ </tr>
56
+ <% end %>
57
+ </table>
58
+
59
+ <%= partial :next_more, :start => start, :size => size %>
@@ -0,0 +1,93 @@
1
+ require 'resque/failure/multiple'
2
+
3
+ module Resque
4
+ module Failure
5
+
6
+ # A multiple failure backend, with retry suppression.
7
+ #
8
+ # For example: if you had a job that could retry 5 times, your failure
9
+ # backends are not notified unless the _final_ retry attempt also fails.
10
+ #
11
+ # Example:
12
+ #
13
+ # require 'resque-retry'
14
+ # require 'resque/failure/redis'
15
+ #
16
+ # Resque::Failure::MultipleWithRetrySuppression.classes = [Resque::Failure::Redis]
17
+ # Resque::Failure.backend = Resque::Failure::MultipleWithRetrySuppression
18
+ #
19
+ class MultipleWithRetrySuppression < Multiple
20
+ include Resque::Helpers
21
+
22
+ module CleanupHooks
23
+ # Resque after_perform hook.
24
+ #
25
+ # Deletes retry failure information from Redis.
26
+ def after_perform_retry_failure_cleanup(*args)
27
+ retry_key = redis_retry_key(*args)
28
+ failure_key = Resque::Failure::MultipleWithRetrySuppression.failure_key(retry_key)
29
+ Resque.redis.del(failure_key)
30
+ end
31
+ end
32
+
33
+ # Called when the job fails.
34
+ #
35
+ # If the job will retry, suppress the failure from the other backends.
36
+ # Store the lastest failure information in redis, used by the web
37
+ # interface.
38
+ def save
39
+ unless retryable? && retrying?
40
+ cleanup_retry_failure_log!
41
+ super
42
+ else
43
+ data = {
44
+ :failed_at => Time.now.strftime("%Y/%m/%d %H:%M:%S"),
45
+ :payload => payload,
46
+ :exception => exception.class.to_s,
47
+ :error => exception.to_s,
48
+ :backtrace => Array(exception.backtrace),
49
+ :worker => worker.to_s,
50
+ :queue => queue
51
+ }
52
+
53
+ # Register cleanup hooks.
54
+ unless klass.respond_to?(:after_perform_retry_failure_cleanup)
55
+ klass.send(:extend, CleanupHooks)
56
+ end
57
+
58
+ redis[failure_key] = Resque.encode(data)
59
+ end
60
+ end
61
+
62
+ # Expose this for the hook's use.
63
+ def self.failure_key(retry_key)
64
+ 'failure_' + retry_key
65
+ end
66
+
67
+ protected
68
+ def klass
69
+ constantize(payload['class'])
70
+ end
71
+
72
+ def retry_key
73
+ klass.redis_retry_key(payload['args'])
74
+ end
75
+
76
+ def failure_key
77
+ self.class.failure_key(retry_key)
78
+ end
79
+
80
+ def retryable?
81
+ klass.respond_to?(:redis_retry_key)
82
+ end
83
+
84
+ def retrying?
85
+ redis.exists(retry_key)
86
+ end
87
+
88
+ def cleanup_retry_failure_log!
89
+ redis.del(failure_key) if retryable?
90
+ end
91
+ end
92
+ end
93
+ end
@@ -19,7 +19,7 @@ module Resque
19
19
  # extend Resque::Plugins::Retry
20
20
  # @queue = :web_hooks
21
21
  #
22
- # @retry_limit = 8 # default: 1
22
+ # @retry_limit = 8 # default: 1
23
23
  # @retry_delay = 60 # default: 0
24
24
  #
25
25
  # # used to build redis key, for counting job attempts.
@@ -16,13 +16,16 @@ class ExponentialBackoffTest < Test::Unit::TestCase
16
16
  def test_default_backoff_strategy
17
17
  now = Time.now
18
18
  Resque.enqueue(ExponentialBackoffJob)
19
- 2.times do
20
- perform_next_job @worker
21
- end
22
19
 
23
- assert_equal 2, Resque.info[:processed], 'processed jobs'
24
- assert_equal 2, Resque.info[:failed], 'failed jobs'
25
- assert_equal 0, Resque.info[:pending], 'pending jobs'
20
+ perform_next_job @worker
21
+ assert_equal 1, Resque.info[:processed], '1 processed job'
22
+ assert_equal 1, Resque.info[:failed], 'first ever run, and it should of failed, but never retried'
23
+ assert_equal 1, Resque.info[:pending], '1 pending job, because it never hits the scheduler'
24
+
25
+ perform_next_job @worker
26
+ assert_equal 2, Resque.info[:processed], '2nd run, but first retry'
27
+ assert_equal 2, Resque.info[:failed], 'should of failed again, this is the first retry attempt'
28
+ assert_equal 0, Resque.info[:pending], '0 pending jobs, it should be in the delayed queue'
26
29
 
27
30
  delayed = Resque.delayed_queue_peek(0, 1)
28
31
  assert_equal now.to_i + 60, delayed[0], '2nd delay' # the first had a zero delay.
@@ -0,0 +1,86 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class MultipleFailureTest < Test::Unit::TestCase
4
+ def setup
5
+ Resque.redis.flushall
6
+ @worker = Resque::Worker.new(:testing)
7
+ @worker.register_worker
8
+
9
+ @old_failure_backend = Resque::Failure.backend
10
+ MockFailureBackend.errors = []
11
+ Resque::Failure::MultipleWithRetrySuppression.classes = [MockFailureBackend]
12
+ Resque::Failure.backend = Resque::Failure::MultipleWithRetrySuppression
13
+ end
14
+
15
+ def failure_key_for(klass)
16
+ args = []
17
+ key = "failure_" + klass.redis_retry_key(args)
18
+ end
19
+
20
+ def test_last_failure_is_saved_in_redis
21
+ Resque.enqueue(LimitThreeJob)
22
+ perform_next_job(@worker)
23
+
24
+ # I don't like this, but...
25
+ key = failure_key_for(LimitThreeJob)
26
+ assert Resque.redis.exists(key)
27
+ end
28
+
29
+ def test_last_failure_removed_from_redis_after_error_limit
30
+ Resque.enqueue(LimitThreeJob)
31
+ 3.times do
32
+ perform_next_job(@worker)
33
+ end
34
+
35
+ key = failure_key_for(LimitThreeJob)
36
+ assert Resque.redis.exists(key)
37
+
38
+ perform_next_job(@worker)
39
+ assert !Resque.redis.exists(key)
40
+ end
41
+
42
+ def test_on_success_failure_log_removed_from_redis
43
+ SwitchToSuccessJob.successful_after = 1
44
+ Resque.enqueue(SwitchToSuccessJob)
45
+ perform_next_job(@worker)
46
+
47
+ key = failure_key_for(SwitchToSuccessJob)
48
+ assert Resque.redis.exists(key)
49
+
50
+ perform_next_job(@worker)
51
+ assert !Resque.redis.exists(key), 'key removed on success'
52
+ ensure
53
+ SwitchToSuccessJob.reset_defaults
54
+ end
55
+
56
+ def test_errors_are_suppressed_up_to_retry_limit
57
+ Resque.enqueue(LimitThreeJob)
58
+ 3.times do
59
+ perform_next_job(@worker)
60
+ end
61
+
62
+ assert_equal 0, MockFailureBackend.errors.size
63
+ end
64
+
65
+ def test_errors_are_logged_after_retry_limit
66
+ Resque.enqueue(LimitThreeJob)
67
+ 4.times do
68
+ perform_next_job(@worker)
69
+ end
70
+
71
+ assert_equal 1, MockFailureBackend.errors.size
72
+ end
73
+
74
+ def test_jobs_without_retry_log_errors
75
+ 5.times do
76
+ Resque.enqueue(NoRetryJob)
77
+ perform_next_job(@worker)
78
+ end
79
+
80
+ assert_equal 5, MockFailureBackend.errors.size
81
+ end
82
+
83
+ def teardown
84
+ Resque::Failure.backend = @old_failure_backend
85
+ end
86
+ end
data/test/test_helper.rb CHANGED
@@ -5,6 +5,11 @@ $TESTING = true
5
5
  require 'test/unit'
6
6
  require 'rubygems'
7
7
  require 'turn'
8
+ require 'simplecov-html'
9
+
10
+ SimpleCov.start do
11
+ add_filter "/test/"
12
+ end
8
13
 
9
14
  require 'resque-retry'
10
15
  require dir + '/test_jobs'
@@ -40,6 +45,19 @@ puts "Starting redis for testing at localhost:9736..."
40
45
  `redis-server #{dir}/redis-test.conf`
41
46
  Resque.redis = '127.0.0.1:9736'
42
47
 
48
+ # Mock failure backend for testing MultipleWithRetrySuppression
49
+ class MockFailureBackend < Resque::Failure::Base
50
+ class << self
51
+ attr_accessor :errors
52
+ end
53
+
54
+ def save
55
+ self.class.errors << exception.to_s
56
+ end
57
+
58
+ self.errors = []
59
+ end
60
+
43
61
  # Test helpers
44
62
  class Test::Unit::TestCase
45
63
  def perform_next_job(worker, &block)
@@ -57,4 +75,4 @@ class Test::Unit::TestCase
57
75
  worker.perform(job)
58
76
  worker.done_working
59
77
  end
60
- end
78
+ end
data/test/test_jobs.rb CHANGED
@@ -2,6 +2,14 @@ CustomException = Class.new(StandardError)
2
2
  HierarchyCustomException = Class.new(CustomException)
3
3
  AnotherCustomException = Class.new(StandardError)
4
4
 
5
+ class NoRetryJob
6
+ @queue = :testing
7
+
8
+ def self.perform(*args)
9
+ raise "error"
10
+ end
11
+ end
12
+
5
13
  class GoodJob
6
14
  extend Resque::Plugins::Retry
7
15
  @queue = :testing
@@ -50,6 +58,15 @@ class NeverGiveUpJob < RetryDefaultsJob
50
58
  @retry_limit = 0
51
59
  end
52
60
 
61
+ class LimitThreeJob < RetryDefaultsJob
62
+ @queue = :testing
63
+ @retry_limit = 3
64
+
65
+ def self.perform(*args)
66
+ raise ArgumentError, "custom message"
67
+ end
68
+ end
69
+
53
70
  class FailFiveTimesJob < RetryDefaultsJob
54
71
  @queue = :testing
55
72
  @retry_limit = 6
@@ -187,6 +204,32 @@ class InheritOrderingJobExtendFirst
187
204
  end
188
205
  end
189
206
 
207
+ # This job switches to successful after
208
+ # n +tries+.
209
+ class SwitchToSuccessJob < GoodJob
210
+ @queue = :testing
211
+ @max_retries = 3
212
+
213
+ class << self
214
+ attr_accessor :successful_after
215
+ attr_accessor :tries
216
+
217
+ def reset_defaults
218
+ self.tries = 0
219
+ self.successful_after = 2
220
+ end
221
+ end
222
+
223
+ reset_defaults
224
+
225
+ def self.perform(*args)
226
+ if self.tries < self.successful_after
227
+ self.tries += 1
228
+ raise "error"
229
+ end
230
+ end
231
+ end
232
+
190
233
  class InheritOrderingJobExtendLast
191
234
  class << self
192
235
  attr_accessor :test_value
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resque-retry
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 27
4
5
  prerelease: false
5
6
  segments:
6
7
  - 0
8
+ - 1
7
9
  - 0
8
- - 6
9
- version: 0.0.6
10
+ version: 0.1.0
10
11
  platform: ruby
11
12
  authors:
12
13
  - Luke Antins
@@ -15,16 +16,18 @@ autorequire:
15
16
  bindir: bin
16
17
  cert_chain: []
17
18
 
18
- date: 2010-07-12 00:00:00 +01:00
19
+ date: 2010-08-29 00:00:00 +01:00
19
20
  default_executable:
20
21
  dependencies:
21
22
  - !ruby/object:Gem::Dependency
22
23
  name: resque
23
24
  prerelease: false
24
25
  requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
25
27
  requirements:
26
28
  - - ">="
27
29
  - !ruby/object:Gem::Version
30
+ hash: 55
28
31
  segments:
29
32
  - 1
30
33
  - 8
@@ -36,9 +39,11 @@ dependencies:
36
39
  name: resque-scheduler
37
40
  prerelease: false
38
41
  requirement: &id002 !ruby/object:Gem::Requirement
42
+ none: false
39
43
  requirements:
40
44
  - - ">="
41
45
  - !ruby/object:Gem::Version
46
+ hash: 55
42
47
  segments:
43
48
  - 1
44
49
  - 8
@@ -47,30 +52,64 @@ dependencies:
47
52
  type: :runtime
48
53
  version_requirements: *id002
49
54
  - !ruby/object:Gem::Dependency
50
- name: turn
55
+ name: test-unit
51
56
  prerelease: false
52
57
  requirement: &id003 !ruby/object:Gem::Requirement
58
+ none: false
53
59
  requirements:
54
60
  - - ">="
55
61
  - !ruby/object:Gem::Version
62
+ hash: 3
56
63
  segments:
57
64
  - 0
58
65
  version: "0"
59
66
  type: :development
60
67
  version_requirements: *id003
61
68
  - !ruby/object:Gem::Dependency
62
- name: yard
69
+ name: turn
63
70
  prerelease: false
64
71
  requirement: &id004 !ruby/object:Gem::Requirement
72
+ none: false
65
73
  requirements:
66
74
  - - ">="
67
75
  - !ruby/object:Gem::Version
76
+ hash: 3
68
77
  segments:
69
78
  - 0
70
79
  version: "0"
71
80
  type: :development
72
81
  version_requirements: *id004
73
- description: " resque-retry provides retry, delay and exponential backoff support for\n resque jobs.\n\n Features:\n\n * Redis backed retry count/limit.\n * Retry on all or specific exceptions.\n * Exponential backoff (varying the delay between retrys).\n * Small & Extendable - plenty of places to override retry logic/settings.\n"
82
+ - !ruby/object:Gem::Dependency
83
+ name: yard
84
+ prerelease: false
85
+ requirement: &id005 !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ hash: 3
91
+ segments:
92
+ - 0
93
+ version: "0"
94
+ type: :development
95
+ version_requirements: *id005
96
+ - !ruby/object:Gem::Dependency
97
+ name: simplecov-html
98
+ prerelease: false
99
+ requirement: &id006 !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ hash: 19
105
+ segments:
106
+ - 0
107
+ - 3
108
+ - 0
109
+ version: 0.3.0
110
+ type: :development
111
+ version_requirements: *id006
112
+ description: " resque-retry provides retry, delay and exponential backoff support for\n resque jobs.\n\n Features:\n\n * Redis backed retry count/limit.\n * Retry on all or specific exceptions.\n * Exponential backoff (varying the delay between retrys).\n * Multiple failure backend with retry suppression & resque-web tab.\n * Small & Extendable - plenty of places to override retry logic/settings.\n"
74
113
  email: luke@lividpenguin.com
75
114
  executables: []
76
115
 
@@ -84,6 +123,7 @@ files:
84
123
  - README.md
85
124
  - HISTORY.md
86
125
  - test/exponential_backoff_test.rb
126
+ - test/multiple_failure_test.rb
87
127
  - test/redis-test.conf
88
128
  - test/resque_test.rb
89
129
  - test/retry_criteria_test.rb
@@ -91,8 +131,12 @@ files:
91
131
  - test/retry_test.rb
92
132
  - test/test_helper.rb
93
133
  - test/test_jobs.rb
134
+ - lib/resque/failure/multiple_with_retry_suppression.rb
94
135
  - lib/resque/plugins/exponential_backoff.rb
95
136
  - lib/resque/plugins/retry.rb
137
+ - lib/resque-retry/server/views/retry.erb
138
+ - lib/resque-retry/server/views/retry_timestamp.erb
139
+ - lib/resque-retry/server.rb
96
140
  - lib/resque-retry.rb
97
141
  has_rdoc: false
98
142
  homepage: http://github.com/lantins/resque-retry
@@ -104,23 +148,27 @@ rdoc_options: []
104
148
  require_paths:
105
149
  - lib
106
150
  required_ruby_version: !ruby/object:Gem::Requirement
151
+ none: false
107
152
  requirements:
108
153
  - - ">="
109
154
  - !ruby/object:Gem::Version
155
+ hash: 3
110
156
  segments:
111
157
  - 0
112
158
  version: "0"
113
159
  required_rubygems_version: !ruby/object:Gem::Requirement
160
+ none: false
114
161
  requirements:
115
162
  - - ">="
116
163
  - !ruby/object:Gem::Version
164
+ hash: 3
117
165
  segments:
118
166
  - 0
119
167
  version: "0"
120
168
  requirements: []
121
169
 
122
170
  rubyforge_project:
123
- rubygems_version: 1.3.6
171
+ rubygems_version: 1.3.7
124
172
  signing_key:
125
173
  specification_version: 3
126
174
  summary: A resque plugin; provides retry, delay and exponential backoff support for resque jobs.