activejob-retry 0.4.2 → 0.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/Gemfile.lock +4 -1
- data/README.md +16 -5
- data/lib/active_job/retry.rb +5 -0
- data/lib/active_job/retry/exponential_backoff_strategy.rb +18 -0
- data/lib/active_job/retry/exponential_options_validator.rb +79 -0
- data/lib/active_job/retry/version.rb +1 -1
- data/spec/retry/exponential_backoff_strategy_spec.rb +143 -0
- data/spec/retry/exponential_options_validator_spec.rb +76 -0
- data/spec/retry_spec.rb +22 -0
- metadata +24 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 60dfb14cc0c6a91446c784b4e8411e9aae47c7ee
|
4
|
+
data.tar.gz: a718488ecd619792443718c66ecea001eeeb5266
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 697ffb8e3dba15591d96f1f9c65d8bd75692266c1c90e64eb527eb6bccceb0bca21838b1f348a9afcfbdb3b9634743f544bbbc98e570c12b790776fe6b5e0c3e
|
7
|
+
data.tar.gz: d894718b172f33037f94089b54b6cb39d62e23c2b1df82a9ff8a70a48d6665c1bdc38a62d6b5ba813243516856fc9edbe4036df28d3aaacd81b148248651d377
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
## 0.5.0 - July 23, 2015
|
2
|
+
|
3
|
+
- Add exponential backoff strategy (by [@DavydenkovM](https://github.com/DavydenkovM))
|
4
|
+
|
5
|
+
## 0.4.2 - March 22, 2015
|
6
|
+
|
7
|
+
- Remove Sidekiq from the problematic adapter blacklist (patch by [@troter](https://github.com/troter))
|
8
|
+
|
9
|
+
## 0.4.1 - March 18, 2015
|
10
|
+
|
11
|
+
- Remove the need for an explicit require (patch by [@isaacseymour](https://github.com/isaacseymour))
|
12
|
+
|
1
13
|
## 0.4.0 - January 16, 2015
|
2
14
|
|
3
15
|
- Blacklist problematic adapters rather than whitelisting known good ones (patch by [@isaacseymour](https://github.com/isaacseymour))
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -4,7 +4,7 @@ ActiveJob::Retry [`, and `retry_delay(attempt, exception)`
|
@@ -48,15 +51,23 @@ if the exception is not going to be retried, or has failed the final retry.
|
|
48
51
|
|:---------------------- |:------- |:-------------- |
|
49
52
|
| `limit` | `1` | Maximum number of times to attempt the job (default: 1).
|
50
53
|
| `unlimited_retries` | `false` | If set to `true`, this job will be repeated indefinitely until in succeeds. Use with extreme caution.
|
51
|
-
| `delay` | `0` | Time between attempts (default: 0).
|
54
|
+
| `delay` | `0` | Time between attempts in seconds (default: 0).
|
52
55
|
| `retryable_exceptions` | `nil` | A whitelist of exceptions to retry. When `nil`, all exceptions will result in a retry.
|
53
56
|
| `fatal_exceptions` | `[]` | A blacklist of exceptions to not retry (default: []).
|
54
57
|
|
58
|
+
#### exponential_retry options
|
59
|
+
| Option | Default | Description |
|
60
|
+
|:---------------------- |:------- |:-------------- |
|
61
|
+
| `limit` | `1` | Maximum number of times to attempt the job (default: 1).
|
62
|
+
| `unlimited_retries` | `false` | If set to `true`, this job will be repeated indefinitely until in succeeds. Use with extreme caution.
|
63
|
+
| `retryable_exceptions` | `nil` | Same as for [constant_retry](#constant_retry-options).
|
64
|
+
| `fatal_exceptions` | `[]` | Same as for [constant_retry](#constant_retry-options).
|
65
|
+
|
55
66
|
#### variable_retry options
|
56
67
|
|
57
68
|
| Option | Default | Description |
|
58
69
|
|:---------------------- |:------- |:------------- |
|
59
|
-
| `delays` | | __required__ An array of delays between attempts. The first attempt will occur whenever you originally enqueued the job to happen.
|
70
|
+
| `delays` | | __required__ An array of delays between attempts in seconds. The first attempt will occur whenever you originally enqueued the job to happen.
|
60
71
|
| `min_delay_multiplier` | | If supplied, each delay will be multiplied by a random number between this and `max_delay_multiplier`.
|
61
72
|
| `max_delay_multiplier` | | The other end of the range for `min_delay_multiplier`. If one is supplied, both must be.
|
62
73
|
| `retryable_exceptions` | `nil` | Same as for [constant_retry](#constant_retry-options).
|
data/lib/active_job/retry.rb
CHANGED
@@ -5,6 +5,7 @@ require 'active_job/retry/version'
|
|
5
5
|
require 'active_job/retry/errors'
|
6
6
|
require 'active_job/retry/constant_backoff_strategy'
|
7
7
|
require 'active_job/retry/variable_backoff_strategy'
|
8
|
+
require 'active_job/retry/exponential_backoff_strategy'
|
8
9
|
|
9
10
|
unless ActiveJob::Base.method_defined?(:deserialize)
|
10
11
|
require 'active_job/retry/deserialize_monkey_patch'
|
@@ -45,6 +46,10 @@ module ActiveJob
|
|
45
46
|
retry_with(VariableBackoffStrategy.new(options))
|
46
47
|
end
|
47
48
|
|
49
|
+
def exponential_retry(options)
|
50
|
+
retry_with(ExponentialBackoffStrategy.new(options))
|
51
|
+
end
|
52
|
+
|
48
53
|
def retry_with(backoff_strategy)
|
49
54
|
unless backoff_strategy_valid?(backoff_strategy)
|
50
55
|
raise InvalidConfigurationError,
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'active_job/retry/exponential_options_validator'
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module Retry
|
5
|
+
class ExponentialBackoffStrategy < ConstantBackoffStrategy
|
6
|
+
def initialize(options)
|
7
|
+
ExponentialOptionsValidator.new(options).validate!
|
8
|
+
@retry_limit = options.fetch(:limit, 1)
|
9
|
+
@fatal_exceptions = options.fetch(:fatal_exceptions, [])
|
10
|
+
@retryable_exceptions = options.fetch(:retryable_exceptions, nil)
|
11
|
+
end
|
12
|
+
|
13
|
+
def retry_delay(attempt, _exception)
|
14
|
+
(attempt**4 + 15 + (rand(30) * (attempt + 1))).seconds
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'active_job/retry/errors'
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module Retry
|
5
|
+
class ExponentialOptionsValidator
|
6
|
+
def initialize(options)
|
7
|
+
@options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
def validate!
|
11
|
+
validate_limit_numericality!
|
12
|
+
validate_infinite_limit!
|
13
|
+
validate_delay_not_specified!
|
14
|
+
validate_not_both_exceptions!
|
15
|
+
# Fatal exceptions must be an array (cannot be nil, since then all
|
16
|
+
# exceptions would be fatal - for that just set `limit: 0`)
|
17
|
+
validate_array_of_exceptions!(:fatal_exceptions)
|
18
|
+
# Retryable exceptions must be an array of exceptions or `nil` to retry
|
19
|
+
# any exception
|
20
|
+
if options[:retryable_exceptions]
|
21
|
+
validate_array_of_exceptions!(:retryable_exceptions)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :options
|
28
|
+
|
29
|
+
# Limit must be an integer >= 0, or nil
|
30
|
+
def validate_limit_numericality!
|
31
|
+
return unless options[:limit]
|
32
|
+
return if options[:limit].is_a?(Fixnum) && options[:limit] >= 0
|
33
|
+
|
34
|
+
raise InvalidConfigurationError,
|
35
|
+
'Limit must be an integer >= 0, or nil for unlimited retries'
|
36
|
+
end
|
37
|
+
|
38
|
+
# If no limit is supplied, you *must* set `unlimited_retries: true` and
|
39
|
+
# understand that your ops team might hurt you.
|
40
|
+
def validate_infinite_limit!
|
41
|
+
limit = options.fetch(:limit, 1)
|
42
|
+
return unless limit.nil? ^ options[:unlimited_retries] == true
|
43
|
+
|
44
|
+
if limit.nil? && options[:unlimited_retries] != true
|
45
|
+
raise InvalidConfigurationError,
|
46
|
+
'You must set `unlimited_retries: true` to use `limit: nil`'
|
47
|
+
else
|
48
|
+
raise InvalidConfigurationError,
|
49
|
+
'You must set `limit: nil` to have unlimited retries'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Delay must not be set
|
54
|
+
def validate_delay_not_specified!
|
55
|
+
return unless options[:delay]
|
56
|
+
|
57
|
+
raise InvalidConfigurationError,
|
58
|
+
'You can`t set delay for ExponentialBackoffStrategy'
|
59
|
+
end
|
60
|
+
|
61
|
+
def validate_not_both_exceptions!
|
62
|
+
return unless options[:fatal_exceptions] && options[:retryable_exceptions]
|
63
|
+
|
64
|
+
raise InvalidConfigurationError,
|
65
|
+
'fatal_exceptions and retryable_exceptions cannot be used together'
|
66
|
+
end
|
67
|
+
|
68
|
+
def validate_array_of_exceptions!(key)
|
69
|
+
return unless options[key]
|
70
|
+
if options[key].is_a?(Array) &&
|
71
|
+
options[key].all? { |ex| ex.is_a?(Class) && ex <= Exception }
|
72
|
+
return
|
73
|
+
end
|
74
|
+
|
75
|
+
raise InvalidConfigurationError, "#{key} must be an array of exceptions!"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe ActiveJob::Retry::ExponentialBackoffStrategy do
|
4
|
+
let(:strategy) { described_class.new(options) }
|
5
|
+
|
6
|
+
describe '#should_retry?' do
|
7
|
+
subject { strategy.should_retry?(attempt, exception) }
|
8
|
+
let(:attempt) { 1 }
|
9
|
+
let(:exception) { RuntimeError.new }
|
10
|
+
|
11
|
+
context 'when the limit is infinite' do
|
12
|
+
let(:options) { { limit: nil, unlimited_retries: true } }
|
13
|
+
|
14
|
+
context '1st attempt' do
|
15
|
+
let(:attempt) { 1 }
|
16
|
+
it { is_expected.to be(true) }
|
17
|
+
end
|
18
|
+
|
19
|
+
context '99999th attempt' do
|
20
|
+
let(:attempt) { 99_999 }
|
21
|
+
it { is_expected.to be(true) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'when the limit is 0' do
|
26
|
+
let(:options) { { limit: 0 } }
|
27
|
+
|
28
|
+
context '1st attempt' do
|
29
|
+
let(:attempt) { 1 }
|
30
|
+
it { is_expected.to be(false) }
|
31
|
+
end
|
32
|
+
|
33
|
+
context '99999th attempt' do
|
34
|
+
let(:attempt) { 99_999 }
|
35
|
+
it { is_expected.to be(false) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'when the limit is 5' do
|
40
|
+
let(:options) { { limit: 5 } }
|
41
|
+
|
42
|
+
context '1st attempt' do
|
43
|
+
let(:attempt) { 1 }
|
44
|
+
it { is_expected.to be(true) }
|
45
|
+
end
|
46
|
+
|
47
|
+
context '4th attempt' do
|
48
|
+
let(:attempt) { 4 }
|
49
|
+
it { is_expected.to be(true) }
|
50
|
+
end
|
51
|
+
|
52
|
+
context '5th attempt' do
|
53
|
+
let(:attempt) { 5 }
|
54
|
+
it { is_expected.to be(false) }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'defaults (retry everything)' do
|
59
|
+
let(:options) { { limit: 10 } }
|
60
|
+
|
61
|
+
context 'Exception' do
|
62
|
+
let(:exception) { Exception.new }
|
63
|
+
it { is_expected.to be(true) }
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'RuntimeError' do
|
67
|
+
let(:exception) { RuntimeError.new }
|
68
|
+
it { is_expected.to be(true) }
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'subclass of RuntimeError' do
|
72
|
+
let(:exception) { Class.new(RuntimeError).new }
|
73
|
+
it { is_expected.to be(true) }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'with whitelist' do
|
78
|
+
let(:options) { { limit: 10, retryable_exceptions: [RuntimeError] } }
|
79
|
+
|
80
|
+
context 'Exception' do
|
81
|
+
let(:exception) { Exception.new }
|
82
|
+
it { is_expected.to be(false) }
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'RuntimeError' do
|
86
|
+
let(:exception) { RuntimeError.new }
|
87
|
+
it { is_expected.to be(true) }
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'subclass of RuntimeError' do
|
91
|
+
let(:exception) { Class.new(RuntimeError).new }
|
92
|
+
it { is_expected.to be(true) }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'with blacklist' do
|
97
|
+
let(:options) { { limit: 10, fatal_exceptions: [RuntimeError] } }
|
98
|
+
|
99
|
+
context 'Exception' do
|
100
|
+
let(:exception) { Exception.new }
|
101
|
+
it { is_expected.to be(true) }
|
102
|
+
end
|
103
|
+
|
104
|
+
context 'RuntimeError' do
|
105
|
+
let(:exception) { RuntimeError.new }
|
106
|
+
it { is_expected.to be(false) }
|
107
|
+
end
|
108
|
+
|
109
|
+
context 'subclass of RuntimeError' do
|
110
|
+
let(:exception) { Class.new(RuntimeError).new }
|
111
|
+
it { is_expected.to be(false) }
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe '#retry_delay' do
|
117
|
+
subject { strategy.retry_delay(attempt, exception) }
|
118
|
+
let(:exception) { RuntimeError.new }
|
119
|
+
|
120
|
+
context 'limited retries' do
|
121
|
+
let(:options) { { limit: 5 } }
|
122
|
+
let(:attempt) { 1 }
|
123
|
+
|
124
|
+
let(:attempt_3_delay) { strategy.retry_delay(attempt_3, exception) }
|
125
|
+
let(:attempt_5_delay) { strategy.retry_delay(attempt_5, exception) }
|
126
|
+
|
127
|
+
let(:attempt_3) { 3 }
|
128
|
+
let(:attempt_5) { 5 }
|
129
|
+
|
130
|
+
it 'returns value greater than previous for each of the following attempts' do
|
131
|
+
expect(subject).to be < attempt_3_delay
|
132
|
+
expect(attempt_3_delay).to be < attempt_5_delay
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context 'unlimited retries' do
|
137
|
+
let(:options) { { limit: nil, unlimited_retries: true } }
|
138
|
+
let(:attempt) { 1000 }
|
139
|
+
|
140
|
+
specify { expect { subject }.to_not raise_error }
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe ActiveJob::Retry::ExponentialOptionsValidator do
|
4
|
+
let(:validator) { described_class.new(options) }
|
5
|
+
subject(:validate!) { -> { validator.validate! } }
|
6
|
+
|
7
|
+
context 'valid options' do
|
8
|
+
context 'unlimited retries' do
|
9
|
+
let(:options) { { limit: nil, unlimited_retries: true } }
|
10
|
+
it { is_expected.to_not raise_error }
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'no retries' do
|
14
|
+
let(:options) { { limit: 0 } }
|
15
|
+
it { is_expected.to_not raise_error }
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'some retries' do
|
19
|
+
let(:options) { { limit: 3 } }
|
20
|
+
it { is_expected.to_not raise_error }
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'fatal_exceptions' do
|
24
|
+
let(:options) { { fatal_exceptions: [RuntimeError] } }
|
25
|
+
it { is_expected.to_not raise_error }
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'retryable_exceptions' do
|
29
|
+
let(:options) { { retryable_exceptions: [StandardError, NoMethodError] } }
|
30
|
+
it { is_expected.to_not raise_error }
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'multiple options' do
|
34
|
+
let(:options) { { limit: 3, retryable_exceptions: [RuntimeError] } }
|
35
|
+
|
36
|
+
it { is_expected.to_not raise_error }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'invalid options' do
|
41
|
+
context 'bad limit' do
|
42
|
+
let(:options) { { limit: -1 } }
|
43
|
+
it { is_expected.to raise_error(ActiveJob::Retry::InvalidConfigurationError) }
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'accidental infinite limit' do
|
47
|
+
let(:options) { { limit: nil } }
|
48
|
+
it { is_expected.to raise_error(ActiveJob::Retry::InvalidConfigurationError) }
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'accidental finite limit' do
|
52
|
+
let(:options) { { unlimited_retries: true } }
|
53
|
+
it { is_expected.to raise_error(ActiveJob::Retry::InvalidConfigurationError) }
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'delay provided' do
|
57
|
+
let(:options) { { delay: 1 } }
|
58
|
+
it { is_expected.to raise_error(ActiveJob::Retry::InvalidConfigurationError) }
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'bad fatal_exceptions' do
|
62
|
+
let(:options) { { fatal_exceptions: ['StandardError'] } }
|
63
|
+
it { is_expected.to raise_error(ActiveJob::Retry::InvalidConfigurationError) }
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'bad retryable_exceptions' do
|
67
|
+
let(:options) { { retryable_exceptions: [:runtime] } }
|
68
|
+
it { is_expected.to raise_error(ActiveJob::Retry::InvalidConfigurationError) }
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'retry and fatal exceptions together' do
|
72
|
+
let(:options) { { fatal_exceptions: [StandardError], retryable_exceptions: [] } }
|
73
|
+
it { is_expected.to raise_error(ActiveJob::Retry::InvalidConfigurationError) }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/spec/retry_spec.rb
CHANGED
@@ -43,6 +43,28 @@ RSpec.describe ActiveJob::Retry do
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
+
describe '.exponential_retry' do
|
47
|
+
it 'sets an ExponentialBackoffStrategy' do
|
48
|
+
job.exponential_retry(limit: 10)
|
49
|
+
expect(job.backoff_strategy).to be_a(ActiveJob::Retry::ExponentialBackoffStrategy)
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'invalid options' do
|
53
|
+
let(:options) { { limit: -2 } }
|
54
|
+
let(:options_with_delay) { { limit: 2, delay: 3 } }
|
55
|
+
|
56
|
+
specify do
|
57
|
+
expect { job.exponential_retry(options) }.
|
58
|
+
to raise_error(ActiveJob::Retry::InvalidConfigurationError)
|
59
|
+
end
|
60
|
+
|
61
|
+
specify do
|
62
|
+
expect { job.exponential_retry(options_with_delay) }.
|
63
|
+
to raise_error(ActiveJob::Retry::InvalidConfigurationError)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
46
68
|
describe '.retry_with' do
|
47
69
|
it 'rejects invalid backoff strategies' do
|
48
70
|
expect { job.retry_with(Object.new) }.
|
metadata
CHANGED
@@ -1,97 +1,97 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activejob-retry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Isaac Seymour
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-07-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activejob
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '4.2'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '4.2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activesupport
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '4.2'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '4.2'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '10.3'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '10.3'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rspec
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rspec-its
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- -
|
73
|
+
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: '0'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- -
|
80
|
+
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: rubocop
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- -
|
87
|
+
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: '0'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- -
|
94
|
+
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
description: |2
|
@@ -110,10 +110,10 @@ executables: []
|
|
110
110
|
extensions: []
|
111
111
|
extra_rdoc_files: []
|
112
112
|
files:
|
113
|
-
- .gitignore
|
114
|
-
- .rspec
|
115
|
-
- .rubocop.yml
|
116
|
-
- .travis.yml
|
113
|
+
- ".gitignore"
|
114
|
+
- ".rspec"
|
115
|
+
- ".rubocop.yml"
|
116
|
+
- ".travis.yml"
|
117
117
|
- CHANGELOG.md
|
118
118
|
- Gemfile
|
119
119
|
- Gemfile.lock
|
@@ -126,12 +126,16 @@ files:
|
|
126
126
|
- lib/active_job/retry/constant_options_validator.rb
|
127
127
|
- lib/active_job/retry/deserialize_monkey_patch.rb
|
128
128
|
- lib/active_job/retry/errors.rb
|
129
|
+
- lib/active_job/retry/exponential_backoff_strategy.rb
|
130
|
+
- lib/active_job/retry/exponential_options_validator.rb
|
129
131
|
- lib/active_job/retry/variable_backoff_strategy.rb
|
130
132
|
- lib/active_job/retry/variable_options_validator.rb
|
131
133
|
- lib/active_job/retry/version.rb
|
132
134
|
- lib/activejob/retry.rb
|
133
135
|
- spec/retry/constant_backoff_strategy_spec.rb
|
134
136
|
- spec/retry/constant_options_validator_spec.rb
|
137
|
+
- spec/retry/exponential_backoff_strategy_spec.rb
|
138
|
+
- spec/retry/exponential_options_validator_spec.rb
|
135
139
|
- spec/retry/variable_backoff_strategy_spec.rb
|
136
140
|
- spec/retry/variable_options_validator_spec.rb
|
137
141
|
- spec/retry_spec.rb
|
@@ -189,12 +193,12 @@ require_paths:
|
|
189
193
|
- lib
|
190
194
|
required_ruby_version: !ruby/object:Gem::Requirement
|
191
195
|
requirements:
|
192
|
-
- -
|
196
|
+
- - ">="
|
193
197
|
- !ruby/object:Gem::Version
|
194
198
|
version: '0'
|
195
199
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
196
200
|
requirements:
|
197
|
-
- -
|
201
|
+
- - ">="
|
198
202
|
- !ruby/object:Gem::Version
|
199
203
|
version: '0'
|
200
204
|
requirements: []
|