activejob-retry 0.6.2 → 0.6.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +9 -3
- data/.travis.yml +10 -5
- data/CHANGELOG.md +13 -0
- data/Gemfile.activejob42 +3 -0
- data/Gemfile.activejob50 +3 -0
- data/README.md +38 -4
- data/lib/active_job/retry.rb +27 -7
- data/lib/active_job/retry/constant_options_validator.rb +1 -1
- data/lib/active_job/retry/exponential_options_validator.rb +1 -1
- data/lib/active_job/retry/version.rb +1 -1
- data/spec/retry_spec.rb +52 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4fbf3f8e2eee97ef30a65fdc9f627acb6fd9c271
|
4
|
+
data.tar.gz: 24c084bf1714043588bcd7643a39eabfdfe71d38
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c62d03830fe93de1de6f04ae1e712c9098dd2e0ed029898faafad283406b8766f00a7f6b69c2de323176038149f48ca45a6759486f6f3109cfd9efca083b5ae3
|
7
|
+
data.tar.gz: '094302d4b6466df850e38a94af520c64d8141284bcde467ba66e71149a8113bd3527a0d9334b9dd95562b48895989e958a0a0a032afc8ca3129b9bf83c51d72d'
|
data/.rubocop.yml
CHANGED
@@ -12,9 +12,6 @@ LineLength:
|
|
12
12
|
Style/SignalException:
|
13
13
|
EnforcedStyle: 'only_raise'
|
14
14
|
|
15
|
-
Metrics/MethodLength:
|
16
|
-
CountComments: false
|
17
|
-
|
18
15
|
Style/Documentation:
|
19
16
|
Enabled: false
|
20
17
|
|
@@ -23,3 +20,12 @@ Style/DotPosition:
|
|
23
20
|
|
24
21
|
Style/GuardClause:
|
25
22
|
Enabled: false
|
23
|
+
|
24
|
+
Metrics/MethodLength:
|
25
|
+
CountComments: false
|
26
|
+
|
27
|
+
Metrics/BlockLength:
|
28
|
+
CountComments: false
|
29
|
+
Exclude:
|
30
|
+
- Rakefile
|
31
|
+
- spec/**/*
|
data/.travis.yml
CHANGED
@@ -6,21 +6,26 @@ language: ruby
|
|
6
6
|
matrix:
|
7
7
|
exclude:
|
8
8
|
# Rails 5.0 dropped support for Ruby <2.2
|
9
|
-
- rvm: 2.0
|
10
|
-
gemfile: Gemfile.activejob50
|
11
9
|
- rvm: 2.1
|
12
10
|
gemfile: Gemfile.activejob50
|
11
|
+
# Rails 4-2-stable supports Ruby 2.4, but the released version doesn't. Ignore for now.
|
12
|
+
- rvm: 2.4.0
|
13
|
+
gemfile: Gemfile.activejob42
|
13
14
|
|
14
15
|
rvm:
|
15
|
-
- 2.0
|
16
16
|
- 2.1
|
17
17
|
- 2.2.2
|
18
|
-
- 2.3.
|
18
|
+
- 2.3.3
|
19
|
+
- 2.4.0
|
19
20
|
|
20
21
|
gemfile:
|
21
22
|
- Gemfile.activejob42
|
22
23
|
- Gemfile.activejob50
|
23
24
|
|
25
|
+
before_install:
|
26
|
+
- gem update --system
|
27
|
+
- gem install bundler
|
28
|
+
|
24
29
|
script:
|
25
30
|
- bundle exec rubocop
|
26
31
|
- bundle exec rspec spec
|
@@ -31,4 +36,4 @@ services:
|
|
31
36
|
- rabbitmq
|
32
37
|
|
33
38
|
addons:
|
34
|
-
postgresql: "9.
|
39
|
+
postgresql: "9.6"
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
## 0.6.3 - Feb 9, 2017
|
2
|
+
|
3
|
+
- Add `callback` option, to be run before each retry, and potentially cancel the retry (by
|
4
|
+
[@doits](https://github.com/doits))
|
5
|
+
|
6
|
+
## 0.6.2 - Aug 10, 2016
|
7
|
+
|
8
|
+
- Rails 5 support (by [@isaacseymour](https://github.com/isaacseymour))
|
9
|
+
|
10
|
+
## 0.6.1 - Jun 1, 2016
|
11
|
+
|
12
|
+
- Apply retry settings to subclasses (by [@isaacseymour](https://github.com/isaacseymour))
|
13
|
+
|
1
14
|
## 0.6.0 - May 18, 2016
|
2
15
|
|
3
16
|
- Change API usage to make improper use harder (by [@isaacseymour](https://github.com/isaacseymour))
|
data/Gemfile.activejob42
CHANGED
data/Gemfile.activejob50
CHANGED
data/README.md
CHANGED
@@ -65,7 +65,6 @@ if the exception is not going to be retried, or has failed the final retry.
|
|
65
65
|
| `fatal_exceptions` | `[]` | Same as for [constant](#constant-options).
|
66
66
|
|
67
67
|
#### variable options
|
68
|
-
|
69
68
|
| Option | Default | Description |
|
70
69
|
|:---------------------- |:------- |:------------- |
|
71
70
|
| `delays` | | __required__ An array of delays between attempts in seconds. The first attempt will occur whenever you originally enqueued the job to happen.
|
@@ -74,6 +73,42 @@ if the exception is not going to be retried, or has failed the final retry.
|
|
74
73
|
| `retryable_exceptions` | `nil` | Same as for [constant](#constant-options).
|
75
74
|
| `fatal_exceptions` | `[]` | Same as for [constant](#constant-options).
|
76
75
|
|
76
|
+
## Callback
|
77
|
+
|
78
|
+
All strategies support a `callback` option:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
class ProcessWebhook < ActiveJob::Base
|
82
|
+
include ActiveJob::Retry.new(
|
83
|
+
strategy: :exponential, limit: 25,
|
84
|
+
callback: proc do |exception, delay|
|
85
|
+
# will be run before each retry
|
86
|
+
end
|
87
|
+
)
|
88
|
+
end
|
89
|
+
```
|
90
|
+
|
91
|
+
`callback` must be a `proc` and is run before each retry. It receives the
|
92
|
+
exception and delay before the next retry as arguments. It is evaluated on
|
93
|
+
instance level, so you have access to all instance variables and methods (for
|
94
|
+
example `retry_attempt`) of your job.
|
95
|
+
|
96
|
+
If the callback returns `:halt`, retry chain is halted and no further retries
|
97
|
+
will be made:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
class ProcessWebhook < ActiveJob::Base
|
101
|
+
include ActiveJob::Retry.new(
|
102
|
+
strategy: :exponential, limit: 25,
|
103
|
+
callback: proc do |exception, delay|
|
104
|
+
if some_condition
|
105
|
+
:halt # this will halt the retry chain
|
106
|
+
end
|
107
|
+
end
|
108
|
+
)
|
109
|
+
end
|
110
|
+
```
|
111
|
+
|
77
112
|
## Supported backends
|
78
113
|
|
79
114
|
Any queue adapter which supports delayed enqueuing (i.e. the `enqueue_at`
|
@@ -121,9 +156,8 @@ ActiveJob jobs.
|
|
121
156
|
Supported Versions
|
122
157
|
------------------
|
123
158
|
|
124
|
-
Rails 4.2 and 5.0 are supported,
|
125
|
-
|
126
|
-
tested in Travis CI.
|
159
|
+
Rails 4.2 and 5.0 are supported, Ruby 2.1+. Other Ruby runtimes (e.g. JRuby,
|
160
|
+
Rubinius) probably work, but are not tested in Travis CI.
|
127
161
|
|
128
162
|
Contributing
|
129
163
|
------------
|
data/lib/active_job/retry.rb
CHANGED
@@ -32,15 +32,12 @@ module ActiveJob
|
|
32
32
|
#################
|
33
33
|
# Configuration #
|
34
34
|
#################
|
35
|
-
def initialize(strategy: nil, **options)
|
35
|
+
def initialize(strategy: nil, callback: nil, **options)
|
36
36
|
check_adapter!
|
37
37
|
@backoff_strategy = choose_strategy(strategy, options)
|
38
|
+
@retry_callback = callback
|
38
39
|
|
39
|
-
|
40
|
-
raise InvalidConfigurationError,
|
41
|
-
'Backoff strategies must define `should_retry?(attempt, exception)`, ' \
|
42
|
-
'and `retry_delay(attempt, exception)`.'
|
43
|
-
end
|
40
|
+
validate_params
|
44
41
|
end
|
45
42
|
|
46
43
|
def included(base)
|
@@ -52,11 +49,12 @@ module ActiveJob
|
|
52
49
|
define_retry_attempt_tracking(base)
|
53
50
|
define_retry_method(base)
|
54
51
|
define_retry_logic(base)
|
52
|
+
define_retry_callback(base)
|
55
53
|
end
|
56
54
|
|
57
55
|
private
|
58
56
|
|
59
|
-
attr_reader :backoff_strategy
|
57
|
+
attr_reader :backoff_strategy, :retry_callback
|
60
58
|
|
61
59
|
def define_backoff_strategy(klass)
|
62
60
|
klass.instance_variable_set(:@backoff_strategy, @backoff_strategy)
|
@@ -80,6 +78,11 @@ module ActiveJob
|
|
80
78
|
klass.instance_eval do
|
81
79
|
define_method :internal_retry do |exception|
|
82
80
|
this_delay = self.class.backoff_strategy.retry_delay(retry_attempt, exception)
|
81
|
+
|
82
|
+
cb = self.class.retry_callback &&
|
83
|
+
instance_exec(exception, this_delay, &self.class.retry_callback)
|
84
|
+
return if cb == :halt
|
85
|
+
|
83
86
|
# TODO: This breaks DelayedJob and Resque for some weird ActiveSupport reason.
|
84
87
|
# logger.info("Retrying (attempt #{retry_attempt + 1}, waiting #{this_delay}s)")
|
85
88
|
@retry_attempt += 1
|
@@ -103,6 +106,11 @@ module ActiveJob
|
|
103
106
|
end
|
104
107
|
end
|
105
108
|
|
109
|
+
def define_retry_callback(klass)
|
110
|
+
klass.instance_variable_set(:@retry_callback, @retry_callback)
|
111
|
+
klass.define_singleton_method(:retry_callback) { @retry_callback }
|
112
|
+
end
|
113
|
+
|
106
114
|
def check_adapter!
|
107
115
|
adapter = ActiveJob::Base.queue_adapter
|
108
116
|
adapter_name =
|
@@ -117,6 +125,18 @@ module ActiveJob
|
|
117
125
|
end
|
118
126
|
end
|
119
127
|
|
128
|
+
def validate_params
|
129
|
+
if retry_callback && !retry_callback.is_a?(Proc)
|
130
|
+
raise InvalidConfigurationError, 'Callback must be a `Proc`'
|
131
|
+
end
|
132
|
+
|
133
|
+
unless backoff_strategy_valid?
|
134
|
+
raise InvalidConfigurationError,
|
135
|
+
'Backoff strategies must define `should_retry?(attempt, exception)`, ' \
|
136
|
+
'and `retry_delay(attempt, exception)`.'
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
120
140
|
def backoff_strategy_valid?
|
121
141
|
backoff_strategy.respond_to?(:should_retry?) &&
|
122
142
|
backoff_strategy.respond_to?(:retry_delay) &&
|
@@ -29,7 +29,7 @@ module ActiveJob
|
|
29
29
|
# Limit must be an integer >= 0, or nil
|
30
30
|
def validate_limit_numericality!
|
31
31
|
return unless options[:limit]
|
32
|
-
return if options[:limit].is_a?(
|
32
|
+
return if options[:limit].is_a?(Integer) && options[:limit] >= 0
|
33
33
|
|
34
34
|
raise InvalidConfigurationError,
|
35
35
|
'Limit must be an integer >= 0, or nil for unlimited retries'
|
@@ -29,7 +29,7 @@ module ActiveJob
|
|
29
29
|
# Limit must be an integer >= 0, or nil
|
30
30
|
def validate_limit_numericality!
|
31
31
|
return unless options[:limit]
|
32
|
-
return if options[:limit].is_a?(
|
32
|
+
return if options[:limit].is_a?(Integer) && options[:limit] >= 0
|
33
33
|
|
34
34
|
raise InvalidConfigurationError,
|
35
35
|
'Limit must be an integer >= 0, or nil for unlimited retries'
|
data/spec/retry_spec.rb
CHANGED
@@ -295,4 +295,56 @@ RSpec.describe ActiveJob::Retry do
|
|
295
295
|
end
|
296
296
|
end
|
297
297
|
end
|
298
|
+
|
299
|
+
describe 'retry callback' do
|
300
|
+
let(:retry_instance) do
|
301
|
+
described_class.new(strategy: :constant, callback: callback, **options)
|
302
|
+
end
|
303
|
+
let(:callback_double) { double(call: nil) }
|
304
|
+
let(:callback) { callback_double.method(:call).to_proc }
|
305
|
+
let(:instance) { job.new }
|
306
|
+
subject(:perform) { instance.perform_now }
|
307
|
+
|
308
|
+
context 'invalid options' do
|
309
|
+
let(:callback) { 'not a proc' }
|
310
|
+
|
311
|
+
specify do
|
312
|
+
expect { retry_instance }.
|
313
|
+
to raise_error(ActiveJob::Retry::InvalidConfigurationError)
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
context 'when the job should be retried' do
|
318
|
+
before do
|
319
|
+
expect(job.backoff_strategy).to receive(:should_retry?).
|
320
|
+
with(1, instance_of(RuntimeError)).
|
321
|
+
and_return(true)
|
322
|
+
end
|
323
|
+
|
324
|
+
it 'executes callback proc on retry' do
|
325
|
+
expect(callback_double).to receive(:call)
|
326
|
+
perform
|
327
|
+
end
|
328
|
+
|
329
|
+
context 'with callback returning :halt' do
|
330
|
+
let(:callback) { proc { :halt } }
|
331
|
+
|
332
|
+
it 'it does not retry the job' do
|
333
|
+
expect(instance).not_to receive(:retry_job)
|
334
|
+
|
335
|
+
perform
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
context 'with callback not returning :halt' do
|
340
|
+
let(:callback) { proc { 'not halt' } }
|
341
|
+
|
342
|
+
it 'it retries the job' do
|
343
|
+
expect(instance).to receive(:retry_job)
|
344
|
+
|
345
|
+
perform
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
298
350
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activejob-retry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Isaac Seymour
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-02-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activejob
|
@@ -200,7 +200,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
200
200
|
version: '0'
|
201
201
|
requirements: []
|
202
202
|
rubyforge_project:
|
203
|
-
rubygems_version: 2.
|
203
|
+
rubygems_version: 2.6.10
|
204
204
|
signing_key:
|
205
205
|
specification_version: 4
|
206
206
|
summary: Automatic retry functionality for ActiveJob.
|