retriable 3.1.2 → 3.2.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/.github/dependabot.yml +8 -0
- data/.github/workflows/main.yml +49 -0
- data/.rspec +2 -1
- data/.rubocop.yml +9 -3
- data/CHANGELOG.md +85 -66
- data/CODE_OF_CONDUCT.md +10 -0
- data/Gemfile +4 -2
- data/README.md +50 -48
- data/Rakefile +12 -0
- data/bin/console +11 -0
- data/bin/setup +8 -0
- data/lib/retriable/config.rb +5 -2
- data/lib/retriable/core_ext/kernel.rb +2 -0
- data/lib/retriable/exponential_backoff.rb +2 -0
- data/lib/retriable/version.rb +3 -1
- data/lib/retriable.rb +8 -3
- data/retriable.gemspec +4 -9
- data/sig/retriable.rbs +4 -0
- data/spec/config_spec.rb +2 -0
- data/spec/exponential_backoff_spec.rb +2 -0
- data/spec/retriable_spec.rb +30 -0
- data/spec/spec_helper.rb +3 -7
- data/spec/support/exceptions.rb +2 -0
- metadata +15 -11
- data/.travis.yml +0 -30
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 744d79bd8be89939c6d24deaf7430841d167f62a6eeb98259ea30c8af24c8018
|
|
4
|
+
data.tar.gz: 68a1900cef5b7d8455786a681660bbe3ab779c1ef9cafb274634c48b234131dc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 931cbd73e3d88e47384c1a34faf3deb71a2a87f4ab9dd21a967fb73f1a0a364e7c1b8cd0794dd71821d84a7968d739195561d71bf020bbc27af9af2e26599c1d
|
|
7
|
+
data.tar.gz: 55e596d2efb08d3cfc4aa2eb88f0db4b51589ae48b3d2ed9af6cc41e2f06ef91085b646f185818dd22474cd60c5ac91ea651fbfb9d08cf63a9e43a21867a3f91
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [master]
|
|
8
|
+
types: [opened, synchronize, reopened]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
ci:
|
|
12
|
+
# The type of runner that the job will run on
|
|
13
|
+
runs-on: ${{ matrix.os }}
|
|
14
|
+
strategy:
|
|
15
|
+
matrix:
|
|
16
|
+
os: [ubuntu-24.04]
|
|
17
|
+
ruby:
|
|
18
|
+
[
|
|
19
|
+
"2.3",
|
|
20
|
+
"2.4",
|
|
21
|
+
"2.5",
|
|
22
|
+
"2.6",
|
|
23
|
+
"2.7",
|
|
24
|
+
"3.0",
|
|
25
|
+
"3.1",
|
|
26
|
+
"3.2",
|
|
27
|
+
"3.3",
|
|
28
|
+
"3.4",
|
|
29
|
+
"4.0",
|
|
30
|
+
jruby,
|
|
31
|
+
]
|
|
32
|
+
env:
|
|
33
|
+
CC_TEST_REPORTER_ID: 20a1139ef1830b4f813a10a03d90e8aa179b5226f75e75c5a949b25756ebf558
|
|
34
|
+
|
|
35
|
+
steps:
|
|
36
|
+
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
|
37
|
+
- uses: actions/checkout@v6
|
|
38
|
+
|
|
39
|
+
- name: Setup ruby
|
|
40
|
+
uses: ruby/setup-ruby@v1
|
|
41
|
+
with:
|
|
42
|
+
ruby-version: ${{ matrix.ruby }}
|
|
43
|
+
bundler-cache: true
|
|
44
|
+
|
|
45
|
+
- name: ruby version
|
|
46
|
+
run: ruby -v
|
|
47
|
+
|
|
48
|
+
- name: Run rspec
|
|
49
|
+
run: bundle exec rspec
|
data/.rspec
CHANGED
data/.rubocop.yml
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
Style/StringLiterals:
|
|
2
2
|
EnforcedStyle: double_quotes
|
|
3
3
|
|
|
4
|
+
Style/StringLiteralsInInterpolation:
|
|
5
|
+
EnforcedStyle: double_quotes
|
|
6
|
+
|
|
4
7
|
Style/Documentation:
|
|
5
8
|
Enabled: false
|
|
6
9
|
|
|
@@ -10,10 +13,10 @@ Style/TrailingCommaInArguments:
|
|
|
10
13
|
Lint/InheritException:
|
|
11
14
|
Enabled: false
|
|
12
15
|
|
|
13
|
-
Layout/
|
|
16
|
+
Layout/FirstArrayElementIndentation:
|
|
14
17
|
Enabled: false
|
|
15
18
|
|
|
16
|
-
Layout/
|
|
19
|
+
Layout/FirstHashElementIndentation:
|
|
17
20
|
Enabled: false
|
|
18
21
|
|
|
19
22
|
Style/NegatedIf:
|
|
@@ -25,7 +28,7 @@ Metrics/ClassLength:
|
|
|
25
28
|
Metrics/ModuleLength:
|
|
26
29
|
Enabled: false
|
|
27
30
|
|
|
28
|
-
|
|
31
|
+
Layout/LineLength:
|
|
29
32
|
Max: 120
|
|
30
33
|
|
|
31
34
|
Metrics/MethodLength:
|
|
@@ -36,3 +39,6 @@ Metrics/BlockLength:
|
|
|
36
39
|
|
|
37
40
|
Metrics/AbcSize:
|
|
38
41
|
Enabled: false
|
|
42
|
+
|
|
43
|
+
Style/TrailingCommaInArrayLiteral:
|
|
44
|
+
Enabled: false
|
data/CHANGELOG.md
CHANGED
|
@@ -1,122 +1,141 @@
|
|
|
1
|
-
|
|
1
|
+
# HEAD
|
|
2
|
+
|
|
3
|
+
## 3.2.0
|
|
4
|
+
|
|
5
|
+
- Require ruby 2.3+.
|
|
6
|
+
- Fix: Ensure `tries` value is overridden by `intervals` parameter if both are provided and add a test for this. This is always what the README stated but the code didn't actually do it.
|
|
7
|
+
- Fix: Some rubocop offenses.
|
|
2
8
|
|
|
3
9
|
## 3.1.2
|
|
4
10
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
11
|
+
- Replace `minitest` gem with `rspec`
|
|
12
|
+
- Fancier README
|
|
13
|
+
- Remove unnecessary short circuit in `randomize` method
|
|
8
14
|
|
|
9
15
|
## 3.1.1
|
|
10
16
|
|
|
11
|
-
|
|
12
|
-
|
|
17
|
+
- Fix typo in contexts exception message.
|
|
18
|
+
- Fix updating the version in the library.
|
|
13
19
|
|
|
14
20
|
## 3.1.0
|
|
15
21
|
|
|
16
|
-
|
|
22
|
+
- Added [contexts feature](https://github.com/kamui/retriable#contexts). Thanks to @apurvis.
|
|
17
23
|
|
|
18
24
|
## 3.0.2
|
|
19
25
|
|
|
20
|
-
|
|
26
|
+
- Add configuration and options validation.
|
|
21
27
|
|
|
22
28
|
## 3.0.1
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
|
|
30
|
+
- Add `rubocop` linter to enforce coding styles for this library. Also, fix rule violations.
|
|
31
|
+
- Removed `attr_reader :config` that caused a warning. @bruno-
|
|
32
|
+
- Clean up Rakefile testing cruft. @bruno-
|
|
33
|
+
- Use `.any?` in the `:on` hash processing. @apurvis
|
|
27
34
|
|
|
28
35
|
## 3.0.0
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
|
|
37
|
+
- Require ruby 2.0+.
|
|
38
|
+
- Breaking Change: `on` with a `Hash` value now matches subclassed exceptions. Thanks @apurvis!
|
|
39
|
+
- Remove `awesome_print` from development environment.
|
|
32
40
|
|
|
33
41
|
## 2.1.0
|
|
34
42
|
|
|
35
|
-
|
|
36
|
-
|
|
43
|
+
- Fix bug #17 due to confusing the initial try as a retry.
|
|
44
|
+
- Switch to `Minitest` 5.6 expect syntax.
|
|
37
45
|
|
|
38
46
|
## 2.0.2
|
|
39
47
|
|
|
40
|
-
|
|
48
|
+
- Change required_ruby_version in gemspec to >= 1.9.3.
|
|
41
49
|
|
|
42
50
|
## 2.0.1
|
|
43
51
|
|
|
44
|
-
|
|
52
|
+
- Add support for ruby 1.9.3.
|
|
45
53
|
|
|
46
54
|
## 2.0.0
|
|
47
55
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
- Require ruby 2.0+.
|
|
57
|
+
- Time intervals default to randomized exponential backoff instead of fixed time intervals. The delay between retries grows with every attempt and there's a randomization factor added to each attempt.
|
|
58
|
+
- `base_interval`, `max_interval`, `rand_factor`, and `multiplier` are new arguments that are used to generate randomized exponential back off time intervals.
|
|
59
|
+
- `interval` argument removed.
|
|
60
|
+
- Accept `intervals` array argument to provide your own custom intervals.
|
|
61
|
+
- Allow configurable defaults via `Retriable#configure` block.
|
|
62
|
+
- Add ability for `:on` argument to accept a `Hash` where the keys are exception types and the values are a single or array of `Regexp` pattern(s) to match against exception messages for retrial.
|
|
63
|
+
- Raise, not return, on max elapsed time.
|
|
64
|
+
- Check for elapsed time after next interval is calculated and it goes over the max elapsed time.
|
|
65
|
+
- Support early termination via `max_elapsed_time` argument.
|
|
58
66
|
|
|
59
67
|
## 2.0.0.beta5
|
|
60
|
-
|
|
68
|
+
|
|
69
|
+
- Change `:max_tries` back to `:tries`.
|
|
61
70
|
|
|
62
71
|
## 2.0.0.beta4
|
|
63
|
-
|
|
64
|
-
|
|
72
|
+
|
|
73
|
+
- Change #retry back to #retriable. Didn't like the idea of defining a method that is also a reserved word.
|
|
74
|
+
- Add ability for `:on` argument to accept a `Hash` where the keys are exception types and the values are a single or array of `Regexp` pattern(s) to match against exception messages for retrial.
|
|
65
75
|
|
|
66
76
|
## 2.0.0.beta3
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
|
|
78
|
+
- Accept `intervals` array argument to provide your own custom intervals.
|
|
79
|
+
- Refactor the exponential backoff code into it's own class.
|
|
80
|
+
- Add specs for exponential backoff, randomization, and config.
|
|
70
81
|
|
|
71
82
|
## 2.0.0.beta2
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
83
|
+
|
|
84
|
+
- Raise, not return, on max elapsed time.
|
|
85
|
+
- Check for elapsed time after next interval is calculated and it goes over the max elapsed time.
|
|
86
|
+
- Add specs for `max_elapsed_time` and `max_interval`.
|
|
75
87
|
|
|
76
88
|
## 2.0.0.beta1
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
89
|
+
|
|
90
|
+
- Require ruby 2.0+.
|
|
91
|
+
- Default to random exponential backoff, removes the `interval` option. Exponential backoff is configurable via arguments.
|
|
92
|
+
- Allow configurable defaults via `Retriable#configure` block.
|
|
93
|
+
- Change `Retriable.retriable` to `Retriable.retry`.
|
|
94
|
+
- Support early termination via `max_elapsed_time` argument.
|
|
82
95
|
|
|
83
96
|
## 1.4.1
|
|
84
|
-
|
|
97
|
+
|
|
98
|
+
- Fixes non kernel mode bug. Remove DSL class, move `#retriable` into Retriable module. Thanks @mkrogemann.
|
|
85
99
|
|
|
86
100
|
## 1.4.0
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
101
|
+
|
|
102
|
+
- By default, retriable doesn't monkey patch `Kernel`. If you want this functionality,
|
|
103
|
+
you can `require 'retriable/core_ext/kernel'.
|
|
104
|
+
- Upgrade minitest to 5.x.
|
|
105
|
+
- Refactor the DSL into it's own class.
|
|
91
106
|
|
|
92
107
|
## 1.3.3.1
|
|
93
|
-
|
|
108
|
+
|
|
109
|
+
- Allow sleep parameter to be a proc/lambda to allow for exponential backoff.
|
|
94
110
|
|
|
95
111
|
## 1.3.3
|
|
96
|
-
|
|
112
|
+
|
|
113
|
+
- sleep after executing the retry block, so there's no wait on the first call (molfar)
|
|
97
114
|
|
|
98
115
|
## 1.3.2
|
|
99
|
-
|
|
100
|
-
|
|
116
|
+
|
|
117
|
+
- Clean up option defaults.
|
|
118
|
+
- By default, rescue StandardError and Timeout::Error instead of [Exception](http://www.mikeperham.com/2012/03/03/the-perils-of-rescue-exception).
|
|
101
119
|
|
|
102
120
|
## 1.3.1
|
|
103
|
-
|
|
104
|
-
|
|
121
|
+
|
|
122
|
+
- Add `rake` dependency for travis-ci.
|
|
123
|
+
- Update gemspec summary and description.
|
|
105
124
|
|
|
106
125
|
## 1.3.0
|
|
107
126
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
127
|
+
- Rewrote a lot of the code with inspiration from [attempt](https://rubygems.org/gems/attempt).
|
|
128
|
+
- Add timeout option to the code block.
|
|
129
|
+
- Include in Kernel by default, but allow require 'retriable/no_kernel' to load a non kernel version.
|
|
130
|
+
- Renamed `:times` option to `:tries`.
|
|
131
|
+
- Renamed `:sleep` option to `:interval`.
|
|
132
|
+
- Renamed `:then` option to `:on_retry`.
|
|
133
|
+
- Removed other callbacks, you can wrap retriable in a begin/rescue/else/ensure block if you need that functionality. It avoids the need to define multiple Procs and makes the code more readable.
|
|
134
|
+
- Rewrote most of the README
|
|
116
135
|
|
|
117
136
|
## 1.2.0
|
|
118
137
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
138
|
+
- Forked the retryable-rb repo.
|
|
139
|
+
- Extend the Kernel module with the retriable method so you can use it anywhere without having to include it in every class.
|
|
140
|
+
- Update gemspec, Gemfile, and Raketask.
|
|
141
|
+
- Remove echoe dependency.
|
data/CODE_OF_CONDUCT.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Code of Conduct
|
|
2
|
+
|
|
3
|
+
"retriable" follows [The Ruby Community Conduct Guideline](https://www.ruby-lang.org/en/conduct) in all "collaborative space", which is defined as community communications channels (such as mailing lists, submitted patches, commit comments, etc.):
|
|
4
|
+
|
|
5
|
+
* Participants will be tolerant of opposing views.
|
|
6
|
+
* Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
|
|
7
|
+
* When interpreting the words and actions of others, participants should always assume good intentions.
|
|
8
|
+
* Behaviour which can be reasonably considered harassment will not be tolerated.
|
|
9
|
+
|
|
10
|
+
If you have any concerns about behaviour within this project, please contact us at ["jack@jackchu.com"](mailto:"jack@jackchu.com").
|
data/Gemfile
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
source "https://rubygems.org"
|
|
2
4
|
|
|
3
5
|
gemspec
|
|
4
6
|
|
|
5
7
|
group :test do
|
|
6
|
-
gem "
|
|
7
|
-
gem "rspec"
|
|
8
|
+
gem "rspec", "~> 3.0"
|
|
8
9
|
gem "simplecov", require: false
|
|
9
10
|
end
|
|
10
11
|
|
|
@@ -14,4 +15,5 @@ end
|
|
|
14
15
|
|
|
15
16
|
group :development, :test do
|
|
16
17
|
gem "pry"
|
|
18
|
+
gem "rake", "~> 13.0"
|
|
17
19
|
end
|
data/README.md
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
# Retriable
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[](https://codeclimate.com/github/kamui/retriable)
|
|
3
|
+

|
|
4
|
+
[](https://houndci.com)
|
|
6
5
|
|
|
7
6
|
Retriable is a simple DSL to retry failed code blocks with randomized [exponential backoff](http://en.wikipedia.org/wiki/Exponential_backoff) time intervals. This is especially useful when interacting external APIs, remote services, or file system calls.
|
|
8
7
|
|
|
9
8
|
## Requirements
|
|
10
9
|
|
|
11
|
-
Ruby 2.
|
|
10
|
+
Ruby 2.3.0+
|
|
11
|
+
|
|
12
|
+
If you need ruby 2.0.0-2.2.x support, use the [3.1 branch](https://github.com/kamui/retriable/tree/3.1.x) by specifying `~3.1` in your Gemfile.
|
|
12
13
|
|
|
13
14
|
If you need ruby 1.9.3 support, use the [2.x branch](https://github.com/kamui/retriable/tree/2.x) by specifying `~2.1` in your Gemfile.
|
|
14
15
|
|
|
@@ -35,6 +36,7 @@ gem 'retriable', '~> 3.1'
|
|
|
35
36
|
```
|
|
36
37
|
|
|
37
38
|
## Usage
|
|
39
|
+
|
|
38
40
|
Code in a `Retriable.retriable` block will be retried if an exception is raised.
|
|
39
41
|
|
|
40
42
|
```ruby
|
|
@@ -51,45 +53,48 @@ end
|
|
|
51
53
|
```
|
|
52
54
|
|
|
53
55
|
### Defaults
|
|
56
|
+
|
|
54
57
|
By default, `Retriable` will:
|
|
55
|
-
* rescue any exception inherited from `StandardError`
|
|
56
|
-
* make 3 tries (including the initial attempt) before raising the last exception
|
|
57
|
-
* use randomized exponential backoff to calculate each succeeding try interval.
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
- rescue any exception inherited from `StandardError`
|
|
60
|
+
- make 3 tries (including the initial attempt) before raising the last exception
|
|
61
|
+
- use randomized exponential backoff to calculate each succeeding try interval.
|
|
60
62
|
|
|
61
|
-
|
|
62
|
-
| -------- | -------- | -------- | -------- |
|
|
63
|
-
| 1 | `0.25` | `0.5` | `0.75` |
|
|
64
|
-
| 2 | `0.375` | `0.75` | `1.125` |
|
|
65
|
-
| 3 | `0.563` | `1.125` | `1.688` |
|
|
66
|
-
| 4 | `0.844` | `1.688` | `2.531` |
|
|
67
|
-
| 5 | `1.266` | `2.531` | `3.797` |
|
|
68
|
-
| 6 | `1.898` | `3.797` | `5.695` |
|
|
69
|
-
| 7 | `2.848` | `5.695` | `8.543` |
|
|
70
|
-
| 8 | `4.271` | `8.543` | `12.814` |
|
|
71
|
-
| 9 | `6.407` | `12.814` | `19.222` |
|
|
72
|
-
| 10 | **stop** | **stop** | **stop** |
|
|
63
|
+
The default interval table with 10 tries looks like this (in seconds, rounded to the nearest millisecond):
|
|
73
64
|
|
|
65
|
+
| Retry # | Min | Average | Max |
|
|
66
|
+
| ------- | -------- | -------- | -------- |
|
|
67
|
+
| 1 | `0.25` | `0.5` | `0.75` |
|
|
68
|
+
| 2 | `0.375` | `0.75` | `1.125` |
|
|
69
|
+
| 3 | `0.563` | `1.125` | `1.688` |
|
|
70
|
+
| 4 | `0.844` | `1.688` | `2.531` |
|
|
71
|
+
| 5 | `1.266` | `2.531` | `3.797` |
|
|
72
|
+
| 6 | `1.898` | `3.797` | `5.695` |
|
|
73
|
+
| 7 | `2.848` | `5.695` | `8.543` |
|
|
74
|
+
| 8 | `4.271` | `8.543` | `12.814` |
|
|
75
|
+
| 9 | `6.407` | `12.814` | `19.222` |
|
|
76
|
+
| 10 | **stop** | **stop** | **stop** |
|
|
74
77
|
|
|
75
78
|
### Options
|
|
76
79
|
|
|
77
80
|
Here are the available options, in some vague order of relevance to most common use patterns:
|
|
78
81
|
|
|
79
|
-
| Option
|
|
80
|
-
|
|
|
81
|
-
| **`tries`**
|
|
82
|
-
| **`on`**
|
|
83
|
-
| **`on_retry`**
|
|
84
|
-
| **`
|
|
85
|
-
| **`
|
|
86
|
-
| **`
|
|
87
|
-
| **`
|
|
88
|
-
| **`
|
|
89
|
-
| **`rand_factor`**
|
|
90
|
-
| **`intervals`**
|
|
82
|
+
| Option | Default | Definition |
|
|
83
|
+
| ---------------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
84
|
+
| **`tries`** | `3` | Number of attempts to make at running your code block (includes initial attempt). |
|
|
85
|
+
| **`on`** | `[StandardError]` | Type of exceptions to retry. [Read more](#configuring-which-options-to-retry-with-on). |
|
|
86
|
+
| **`on_retry`** | `nil` | `Proc` to call after each try is rescued. [Read more](#callbacks). |
|
|
87
|
+
| **`sleep_disabled`** | `false` | When true, disable exponential backoff and attempt retries immediately. |
|
|
88
|
+
| **`base_interval`** | `0.5` | The initial interval in seconds between tries. |
|
|
89
|
+
| **`max_elapsed_time`** | `900` (15 min) | The maximum amount of total time in seconds that code is allowed to keep being retried. |
|
|
90
|
+
| **`max_interval`** | `60` | The maximum interval in seconds that any individual retry can reach. |
|
|
91
|
+
| **`multiplier`** | `1.5` | Each successive interval grows by this factor. A multipler of 1.5 means the next interval will be 1.5x the current interval. |
|
|
92
|
+
| **`rand_factor`** | `0.5` | The percentage to randomize the next retry interval time. The next interval calculation is `randomized_interval = retry_interval * (random value in range [1 - randomization_factor, 1 + randomization_factor])` |
|
|
93
|
+
| **`intervals`** | `nil` | Skip generated intervals and provide your own array of intervals in seconds. [Read more](#custom-interval-array). |
|
|
94
|
+
| **`timeout`** | `nil` | Number of seconds to allow the code block to run before raising a `Timeout::Error` inside each try. `nil` means the code block can run forever without raising error. The implementation uses `Timeout::timeout`, which may be [unsafe](https://jvns.ca/blog/2015/11/27/why-rubys-timeout-is-dangerous-and-thread-dot-raise-is-terrifying/) [and](http://blog.headius.com/2008/02/ruby-threadraise-threadkill-timeoutrb.html) [even](https://adamhooper.medium.com/in-ruby-dont-use-timeout-77d9d4e5a001) [dangerous](https://www.mikeperham.com/2015/05/08/timeout-rubys-most-dangerous-api/). Proceed with caution. |
|
|
91
95
|
|
|
92
96
|
#### Configuring Which Options to Retry With :on
|
|
97
|
+
|
|
93
98
|
**`:on`** Can take the form:
|
|
94
99
|
|
|
95
100
|
- An `Exception` class (retry every exception of this type, including subclasses)
|
|
@@ -99,7 +104,6 @@ Here are the available options, in some vague order of relevance to most common
|
|
|
99
104
|
- A single `Regexp` pattern (retries exceptions ONLY if their `message` matches the pattern)
|
|
100
105
|
- An array of patterns (retries exceptions ONLY if their `message` matches at least one of the patterns)
|
|
101
106
|
|
|
102
|
-
|
|
103
107
|
### Configuration
|
|
104
108
|
|
|
105
109
|
You can change the global defaults with a `#configure` block:
|
|
@@ -129,7 +133,7 @@ Retriable.retriable(on: [Timeout::Error, Errno::ECONNRESET]) do
|
|
|
129
133
|
end
|
|
130
134
|
```
|
|
131
135
|
|
|
132
|
-
You can also use a hash to specify that you only want to retry exceptions with certain messages (see [the documentation above](#configuring-which-options-to-retry-with-on)).
|
|
136
|
+
You can also use a hash to specify that you only want to retry exceptions with certain messages (see [the documentation above](#configuring-which-options-to-retry-with-on)). This example will retry all `ActiveRecord::RecordNotUnique` exceptions, `ActiveRecord::RecordInvalid` exceptions where the message matches either `/Parent must exist/` or `/Username has already been taken/`, or `Mysql2::Error` exceptions where the message matches `/Duplicate entry/`.
|
|
133
137
|
|
|
134
138
|
```ruby
|
|
135
139
|
Retriable.retriable(on: {
|
|
@@ -141,7 +145,9 @@ Retriable.retriable(on: {
|
|
|
141
145
|
end
|
|
142
146
|
```
|
|
143
147
|
|
|
144
|
-
You can also specify a timeout if you want the code block to only try for X amount of seconds. This timeout is per try.
|
|
148
|
+
You can also specify a timeout if you want the code block to only try for X amount of seconds. This timeout is per try.
|
|
149
|
+
|
|
150
|
+
The implementation uses `Timeout::timeout`, which may be [unsafe](https://jvns.ca/blog/2015/11/27/why-rubys-timeout-is-dangerous-and-thread-dot-raise-is-terrifying/) [and](http://blog.headius.com/2008/02/ruby-threadraise-threadkill-timeoutrb.html) [even](https://adamhooper.medium.com/in-ruby-dont-use-timeout-77d9d4e5a001) [dangerous](https://www.mikeperham.com/2015/05/08/timeout-rubys-most-dangerous-api/). You can use this option, but you need to be very careful because the code in the block, including libraries or other code it calls, could be interrupted by the timeout at any line. You must ensure you have the right rescue logic and guards in place ([Thread.handle_interrupt](https://www.rubydoc.info/stdlib/core/Thread.handle_interrupt)) to handle that possible behavior. If that's not possible, the recommendation is that you're better off impelenting your own timeout methods depending on what your code is doing than use this feature.
|
|
145
151
|
|
|
146
152
|
```ruby
|
|
147
153
|
Retriable.retriable(timeout: 60) do
|
|
@@ -171,7 +177,7 @@ This example makes 5 total attempts. If the first attempt fails, the 2nd attempt
|
|
|
171
177
|
|
|
172
178
|
### Turn off Exponential Backoff
|
|
173
179
|
|
|
174
|
-
Exponential backoff is enabled by default.
|
|
180
|
+
Exponential backoff is enabled by default. If you want to simply retry code every second, 5 times maximum, you can do this:
|
|
175
181
|
|
|
176
182
|
```ruby
|
|
177
183
|
Retriable.retriable(tries: 5, base_interval: 1.0, multiplier: 1.0, rand_factor: 0.0) do
|
|
@@ -179,7 +185,7 @@ Retriable.retriable(tries: 5, base_interval: 1.0, multiplier: 1.0, rand_factor:
|
|
|
179
185
|
end
|
|
180
186
|
```
|
|
181
187
|
|
|
182
|
-
This works by starting at a 1 second `base_interval`.
|
|
188
|
+
This works by starting at a 1 second `base_interval`. Setting the `multipler` to 1.0 means each subsequent try will increase 1x, which is still `1.0` seconds, and then a `rand_factor` of 0.0 means that there's no randomization of that interval. (By default, it would randomize 0.5 seconds, which would mean normally the intervals would randomize between 0.5 and 1.5 seconds, but in this case `rand_factor` is basically being disabled.)
|
|
183
189
|
|
|
184
190
|
Another way to accomplish this would be to create an array with a fixed interval. In this example, `Array.new(5, 1)` creates an array with 5 elements, all with the value 1. The code block will retry up to 5 times, and wait 1 second between each attempt.
|
|
185
191
|
|
|
@@ -223,7 +229,7 @@ begin
|
|
|
223
229
|
# some code
|
|
224
230
|
end
|
|
225
231
|
rescue => e
|
|
226
|
-
# run this if retriable ends up re-
|
|
232
|
+
# run this if retriable ends up re-raising the exception
|
|
227
233
|
else
|
|
228
234
|
# run this if retriable doesn't raise any exceptions
|
|
229
235
|
ensure
|
|
@@ -233,7 +239,7 @@ end
|
|
|
233
239
|
|
|
234
240
|
## Contexts
|
|
235
241
|
|
|
236
|
-
Contexts allow you to coordinate sets of Retriable options across an application.
|
|
242
|
+
Contexts allow you to coordinate sets of Retriable options across an application. Each context is basically an argument hash for `Retriable.retriable` that is stored in the `Retriable.config` as a simple `Hash` and is accessible by name. For example:
|
|
237
243
|
|
|
238
244
|
```ruby
|
|
239
245
|
Retriable.configure do |c|
|
|
@@ -322,6 +328,8 @@ Retriable.configure do |c|
|
|
|
322
328
|
end
|
|
323
329
|
```
|
|
324
330
|
|
|
331
|
+
Note: In this and the following examples, `Retriable.configure` sets a default config, it doesn't override the configuration for the `retriable` method calls. Calling `Retriable.retriable` with options will override the default configuration for that call. So if you have `tries` set to 5 in `Retriable.configure`, but then you call `Retriable.retriable(tries: 3)`, that call will use 3 tries instead of 5. The configuration is basically a default set of options that can be overridden by passing options to the `retriable` method or by using contexts.
|
|
332
|
+
|
|
325
333
|
Alternately, if you are using RSpec, you could override the Retriable confguration in your `spec_helper`.
|
|
326
334
|
|
|
327
335
|
```ruby
|
|
@@ -334,6 +342,7 @@ end
|
|
|
334
342
|
If you have defined contexts for your configuration, you'll need to change values for each context, because those values take precedence over the default configured value.
|
|
335
343
|
|
|
336
344
|
For example assuming you have configured a `google_api` context:
|
|
345
|
+
|
|
337
346
|
```ruby
|
|
338
347
|
# config/initializers/retriable.rb
|
|
339
348
|
Retriable.configure do |c|
|
|
@@ -366,21 +375,14 @@ Retriable.configure do |c|
|
|
|
366
375
|
end
|
|
367
376
|
```
|
|
368
377
|
|
|
369
|
-
## Proxy Wrapper Object
|
|
370
|
-
|
|
371
|
-
[@julik](https://github.com/julik) has created a gem called [retriable_proxy](https://github.com/julik/retriable_proxy) that extends `retriable` with the ability to wrap objects and specify which methods you want to be retriable, like so:
|
|
372
|
-
|
|
373
|
-
```ruby
|
|
374
|
-
# api_endpoint is an instance of some kind of class that connects to an API
|
|
375
|
-
RetriableProxy.for_object(api_endpoint, on: Net::TimeoutError)
|
|
376
|
-
```
|
|
377
|
-
|
|
378
378
|
## Credits
|
|
379
379
|
|
|
380
380
|
The randomized exponential backoff implementation was inspired by the one used in Google's [google-http-java-client](https://code.google.com/p/google-http-java-client/wiki/ExponentialBackoff) project.
|
|
381
381
|
|
|
382
382
|
## Development
|
|
383
|
+
|
|
383
384
|
### Running Specs
|
|
385
|
+
|
|
384
386
|
```bash
|
|
385
387
|
bundle exec rspec
|
|
386
388
|
```
|
data/Rakefile
ADDED
data/bin/console
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "bundler/setup"
|
|
5
|
+
require "retriable"
|
|
6
|
+
|
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
9
|
+
|
|
10
|
+
require "irb"
|
|
11
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/lib/retriable/config.rb
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative "exponential_backoff"
|
|
2
4
|
|
|
3
5
|
module Retriable
|
|
4
6
|
class Config
|
|
5
|
-
ATTRIBUTES = ExponentialBackoff::ATTRIBUTES + [
|
|
7
|
+
ATTRIBUTES = (ExponentialBackoff::ATTRIBUTES + [
|
|
6
8
|
:sleep_disabled,
|
|
7
9
|
:max_elapsed_time,
|
|
8
10
|
:intervals,
|
|
@@ -10,7 +12,7 @@ module Retriable
|
|
|
10
12
|
:on,
|
|
11
13
|
:on_retry,
|
|
12
14
|
:contexts,
|
|
13
|
-
].freeze
|
|
15
|
+
]).freeze
|
|
14
16
|
|
|
15
17
|
attr_accessor(*ATTRIBUTES)
|
|
16
18
|
|
|
@@ -32,6 +34,7 @@ module Retriable
|
|
|
32
34
|
|
|
33
35
|
opts.each do |k, v|
|
|
34
36
|
raise ArgumentError, "#{k} is not a valid option" if !ATTRIBUTES.include?(k)
|
|
37
|
+
|
|
35
38
|
instance_variable_set(:"@#{k}", v)
|
|
36
39
|
end
|
|
37
40
|
end
|
data/lib/retriable/version.rb
CHANGED
data/lib/retriable.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "timeout"
|
|
2
4
|
require_relative "retriable/config"
|
|
3
5
|
require_relative "retriable/exponential_backoff"
|
|
@@ -41,9 +43,7 @@ module Retriable
|
|
|
41
43
|
start_time = Time.now
|
|
42
44
|
elapsed_time = -> { Time.now - start_time }
|
|
43
45
|
|
|
44
|
-
if intervals
|
|
45
|
-
tries = intervals.size + 1
|
|
46
|
-
else
|
|
46
|
+
if !intervals
|
|
47
47
|
intervals = ExponentialBackoff.new(
|
|
48
48
|
tries: tries - 1,
|
|
49
49
|
base_interval: base_interval,
|
|
@@ -53,11 +53,14 @@ module Retriable
|
|
|
53
53
|
).intervals
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
+
tries = intervals.size + 1
|
|
57
|
+
|
|
56
58
|
tries.times do |index|
|
|
57
59
|
try = index + 1
|
|
58
60
|
|
|
59
61
|
begin
|
|
60
62
|
return Timeout.timeout(timeout) { return yield(try) } if timeout
|
|
63
|
+
|
|
61
64
|
return yield(try)
|
|
62
65
|
rescue *[*exception_list] => exception
|
|
63
66
|
if on.is_a?(Hash)
|
|
@@ -68,7 +71,9 @@ module Retriable
|
|
|
68
71
|
|
|
69
72
|
interval = intervals[index]
|
|
70
73
|
on_retry.call(exception, try, elapsed_time.call, interval) if on_retry
|
|
74
|
+
|
|
71
75
|
raise if try >= tries || (elapsed_time.call + interval) > max_elapsed_time
|
|
76
|
+
|
|
72
77
|
sleep interval if sleep_disabled != true
|
|
73
78
|
end
|
|
74
79
|
end
|
data/retriable.gemspec
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
lib = File.expand_path("../lib", __FILE__)
|
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require "retriable/version"
|
|
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
|
10
10
|
spec.email = ["jack@jackchu.com"]
|
|
11
11
|
spec.summary = "Retriable is a simple DSL to retry failed code blocks with randomized exponential backoff"
|
|
12
12
|
spec.description = "Retriable is a simple DSL to retry failed code blocks with randomized exponential backoff. This is especially useful when interacting external api/services or file system calls."
|
|
13
|
-
spec.homepage = "
|
|
13
|
+
spec.homepage = "https://github.com/kamui/retriable"
|
|
14
14
|
spec.license = "MIT"
|
|
15
15
|
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
|
@@ -18,15 +18,10 @@ Gem::Specification.new do |spec|
|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
19
|
spec.require_paths = ["lib"]
|
|
20
20
|
|
|
21
|
-
spec.required_ruby_version = ">= 2.
|
|
21
|
+
spec.required_ruby_version = ">= 2.3.0"
|
|
22
22
|
|
|
23
23
|
spec.add_development_dependency "bundler"
|
|
24
24
|
spec.add_development_dependency "rspec", "~> 3"
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
spec.add_development_dependency "ruby_dep", "~> 1.3.1"
|
|
28
|
-
spec.add_development_dependency "listen", "~> 3.0.8"
|
|
29
|
-
else
|
|
30
|
-
spec.add_development_dependency "listen", "~> 3.1"
|
|
31
|
-
end
|
|
26
|
+
spec.add_development_dependency "listen", "~> 3.1"
|
|
32
27
|
end
|
data/sig/retriable.rbs
ADDED
data/spec/config_spec.rb
CHANGED
data/spec/retriable_spec.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
describe Retriable do
|
|
2
4
|
let(:time_table_handler) do
|
|
3
5
|
->(_exception, try, _elapsed_time, next_interval) { @next_interval_table[try] = next_interval }
|
|
@@ -130,6 +132,34 @@ describe Retriable do
|
|
|
130
132
|
expect(@next_interval_table).to eq(interval_hash)
|
|
131
133
|
expect(@tries).to eq(intervals.size + 1)
|
|
132
134
|
end
|
|
135
|
+
|
|
136
|
+
it "intervals option overrides tries, base_interval, max_interval, rand_factor, and multiplier" do
|
|
137
|
+
# Even though we specify tries: 10, base_interval: 1.0, max_interval: 100.0,
|
|
138
|
+
# rand_factor: 0.8, and multiplier: 2.0, the explicit intervals should take precedence
|
|
139
|
+
custom_intervals = [0.1, 0.2, 0.3]
|
|
140
|
+
|
|
141
|
+
expect do
|
|
142
|
+
described_class.retriable(
|
|
143
|
+
intervals: custom_intervals,
|
|
144
|
+
tries: 10,
|
|
145
|
+
base_interval: 1.0,
|
|
146
|
+
max_interval: 100.0,
|
|
147
|
+
rand_factor: 0.8,
|
|
148
|
+
multiplier: 2.0,
|
|
149
|
+
on_retry: time_table_handler,
|
|
150
|
+
) do
|
|
151
|
+
increment_tries_with_exception
|
|
152
|
+
end
|
|
153
|
+
end.to raise_error(StandardError)
|
|
154
|
+
|
|
155
|
+
# Should have 4 tries (3 intervals + 1), not 10
|
|
156
|
+
expect(@tries).to eq(4)
|
|
157
|
+
# Should use the exact intervals provided, not generate them
|
|
158
|
+
expect(@next_interval_table[1]).to eq(0.1)
|
|
159
|
+
expect(@next_interval_table[2]).to eq(0.2)
|
|
160
|
+
expect(@next_interval_table[3]).to eq(0.3)
|
|
161
|
+
expect(@next_interval_table[4]).to be_nil
|
|
162
|
+
end
|
|
133
163
|
end
|
|
134
164
|
|
|
135
165
|
context "with an array :on parameter" do
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
require "simplecov"
|
|
3
|
-
|
|
4
|
-
CodeClimate::TestReporter.configure do |config|
|
|
5
|
-
config.logger.level = Logger::WARN
|
|
6
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
7
2
|
|
|
3
|
+
require "simplecov"
|
|
8
4
|
SimpleCov.start
|
|
9
5
|
|
|
10
6
|
require "pry"
|
|
11
7
|
require_relative "../lib/retriable"
|
|
12
|
-
require_relative "support/exceptions
|
|
8
|
+
require_relative "support/exceptions"
|
|
13
9
|
|
|
14
10
|
RSpec.configure do |config|
|
|
15
11
|
config.before(:each) do
|
data/spec/support/exceptions.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: retriable
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jack Chu
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: bundler
|
|
@@ -57,35 +56,42 @@ description: Retriable is a simple DSL to retry failed code blocks with randomiz
|
|
|
57
56
|
or file system calls.
|
|
58
57
|
email:
|
|
59
58
|
- jack@jackchu.com
|
|
60
|
-
executables:
|
|
59
|
+
executables:
|
|
60
|
+
- console
|
|
61
|
+
- setup
|
|
61
62
|
extensions: []
|
|
62
63
|
extra_rdoc_files: []
|
|
63
64
|
files:
|
|
65
|
+
- ".github/dependabot.yml"
|
|
66
|
+
- ".github/workflows/main.yml"
|
|
64
67
|
- ".gitignore"
|
|
65
68
|
- ".hound.yml"
|
|
66
69
|
- ".rspec"
|
|
67
70
|
- ".rubocop.yml"
|
|
68
|
-
- ".travis.yml"
|
|
69
71
|
- CHANGELOG.md
|
|
72
|
+
- CODE_OF_CONDUCT.md
|
|
70
73
|
- Gemfile
|
|
71
74
|
- LICENSE
|
|
72
75
|
- README.md
|
|
76
|
+
- Rakefile
|
|
77
|
+
- bin/console
|
|
78
|
+
- bin/setup
|
|
73
79
|
- lib/retriable.rb
|
|
74
80
|
- lib/retriable/config.rb
|
|
75
81
|
- lib/retriable/core_ext/kernel.rb
|
|
76
82
|
- lib/retriable/exponential_backoff.rb
|
|
77
83
|
- lib/retriable/version.rb
|
|
78
84
|
- retriable.gemspec
|
|
85
|
+
- sig/retriable.rbs
|
|
79
86
|
- spec/config_spec.rb
|
|
80
87
|
- spec/exponential_backoff_spec.rb
|
|
81
88
|
- spec/retriable_spec.rb
|
|
82
89
|
- spec/spec_helper.rb
|
|
83
90
|
- spec/support/exceptions.rb
|
|
84
|
-
homepage:
|
|
91
|
+
homepage: https://github.com/kamui/retriable
|
|
85
92
|
licenses:
|
|
86
93
|
- MIT
|
|
87
94
|
metadata: {}
|
|
88
|
-
post_install_message:
|
|
89
95
|
rdoc_options: []
|
|
90
96
|
require_paths:
|
|
91
97
|
- lib
|
|
@@ -93,16 +99,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
93
99
|
requirements:
|
|
94
100
|
- - ">="
|
|
95
101
|
- !ruby/object:Gem::Version
|
|
96
|
-
version: 2.
|
|
102
|
+
version: 2.3.0
|
|
97
103
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
98
104
|
requirements:
|
|
99
105
|
- - ">="
|
|
100
106
|
- !ruby/object:Gem::Version
|
|
101
107
|
version: '0'
|
|
102
108
|
requirements: []
|
|
103
|
-
|
|
104
|
-
rubygems_version: 2.7.6
|
|
105
|
-
signing_key:
|
|
109
|
+
rubygems_version: 4.0.3
|
|
106
110
|
specification_version: 4
|
|
107
111
|
summary: Retriable is a simple DSL to retry failed code blocks with randomized exponential
|
|
108
112
|
backoff
|
data/.travis.yml
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# Send builds to container-based infrastructure
|
|
2
|
-
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/
|
|
3
|
-
dist: trusty
|
|
4
|
-
sudo: false
|
|
5
|
-
language: ruby
|
|
6
|
-
rvm:
|
|
7
|
-
- 2.0.0
|
|
8
|
-
- 2.1.10
|
|
9
|
-
- 2.2.10
|
|
10
|
-
- 2.3.7
|
|
11
|
-
- 2.4.4
|
|
12
|
-
- 2.5.1
|
|
13
|
-
- ruby-head
|
|
14
|
-
- jruby-9.2.0.0
|
|
15
|
-
- jruby-head
|
|
16
|
-
- rbx-3.99
|
|
17
|
-
script:
|
|
18
|
-
- bundle exec rspec
|
|
19
|
-
|
|
20
|
-
matrix:
|
|
21
|
-
allow_failures:
|
|
22
|
-
- rvm: rbx-3.99
|
|
23
|
-
|
|
24
|
-
before_install:
|
|
25
|
-
- gem update --system
|
|
26
|
-
- gem install bundler
|
|
27
|
-
|
|
28
|
-
addons:
|
|
29
|
-
code_climate:
|
|
30
|
-
repo_token: 20a1139ef1830b4f813a10a03d90e8aa179b5226f75e75c5a949b25756ebf558
|