faraday-retry 1.0.3 → 2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/README.md +28 -10
- data/lib/faraday/retry/middleware.rb +45 -18
- data/lib/faraday/retry/version.rb +1 -1
- data/lib/faraday/retry.rb +1 -0
- metadata +132 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2b88675fec154db2d70b835b98124879b77dbcd283b898fea74fe88c1a75a4b
|
4
|
+
data.tar.gz: 57eefe94dd39d192dff652e90cfadda2ce06d244a3a20e849dd9f3df6a88ffb4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: de85932e530b33168b83399f0135fd862071c10318faaaef324935c3e51bc1914dd9361c68a5c2a6d3a4fc81e9f0193b928a4fe65c2dbc34826998573465af86
|
7
|
+
data.tar.gz: f5e12ca382154fac0fbff872f9f906a62c0a990b06226363c701fa52d05ee927d23b0358f005e6de974e7fad938944beb2621413419b48a5d43b29e4b3180af8
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,27 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## v2.1.1 (2023-02-17)
|
4
|
+
|
5
|
+
### Changed
|
6
|
+
|
7
|
+
* Support for custom RateLimit headers. [PR #13](https://github.com/lostisland/faraday-retry/pull/13). Thanks, [@brookemckim]!
|
8
|
+
|
9
|
+
## v2.1.0 (2023-02-17)
|
10
|
+
|
11
|
+
Invalid release, use v2.1.1.
|
12
|
+
|
13
|
+
## v2.0.0 (2022-06-08)
|
14
|
+
|
15
|
+
### Changed
|
16
|
+
|
17
|
+
* `retry_block` now takes keyword arguments instead of positional (backwards incompatible)
|
18
|
+
* `retry_block`'s `retry_count` argument now counts up from 0, instead of old `retries_remaining`
|
19
|
+
|
20
|
+
### Added
|
21
|
+
|
22
|
+
* Support for the `RateLimit-Reset` header. [PR #9](https://github.com/lostisland/faraday-retry/pull/9). Thanks, [@maxprokopiev]!
|
23
|
+
* `retry_block` has additional `will_retry_in` argument with upcoming delay before retry in seconds.
|
24
|
+
|
3
25
|
## v1.0
|
4
26
|
|
5
27
|
Initial release.
|
@@ -8,3 +30,6 @@ This release consists of the same middleware that was previously bundled with Fa
|
|
8
30
|
### Fixed
|
9
31
|
|
10
32
|
* Retry middleware `retry_block` is not called if retry will not happen due to `max_interval`, https://github.com/lostisland/faraday/pull/1350
|
33
|
+
|
34
|
+
[@maxprokopiev]: https://github.com/maxprokopiev
|
35
|
+
[@brookemckim]: https://github.com/brookemckim
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Faraday Retry
|
2
2
|
|
3
|
-
[](https://github.com/lostisland/faraday-retry/actions/workflows/ci.yaml)
|
4
4
|
[](https://rubygems.org/gems/faraday-retry)
|
5
5
|
[](LICENSE.md)
|
6
6
|
|
@@ -96,11 +96,14 @@ retry_options = {
|
|
96
96
|
}
|
97
97
|
```
|
98
98
|
|
99
|
-
#### Automatically handle the `Retry-After`
|
99
|
+
#### Automatically handle the `Retry-After` and `RateLimit-Reset` headers
|
100
100
|
|
101
|
-
Some APIs, like the [Slack API](https://api.slack.com/docs/rate-limits), will inform you when you reach their API limits by replying with a response status code of `429`
|
101
|
+
Some APIs, like the [Slack API](https://api.slack.com/docs/rate-limits), will inform you when you reach their API limits by replying with a response status code of `429`
|
102
|
+
and a response header of `Retry-After` containing a time in seconds. You should then only retry querying after the amount of time provided by the `Retry-After` header,
|
103
|
+
otherwise you won't get a response. Other APIs communicate their rate limits via the [RateLimit-xxx](https://www.ietf.org/archive/id/draft-ietf-httpapi-ratelimit-headers-05.html#name-providing-ratelimit-fields) headers
|
104
|
+
where `RateLimit-Reset` behaves similarly to the `Retry-After`.
|
102
105
|
|
103
|
-
You can automatically handle
|
106
|
+
You can automatically handle both headers and have Faraday pause and retry for the right amount of time by including the `429` status code in the retry statuses list:
|
104
107
|
|
105
108
|
```ruby
|
106
109
|
retry_options = {
|
@@ -108,6 +111,16 @@ retry_options = {
|
|
108
111
|
}
|
109
112
|
```
|
110
113
|
|
114
|
+
If you are working with an API which does not comply with the Rate Limit RFC you can specify custom headers to be used for retry and reset.
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
retry_options = {
|
118
|
+
retry_statuses: [429],
|
119
|
+
rate_limit_retry_header: 'x-rate-limit-retry-after',
|
120
|
+
rate_limit_reset_header: 'x-rate-limit-reset'
|
121
|
+
}
|
122
|
+
```
|
123
|
+
|
111
124
|
#### Specify a custom retry logic
|
112
125
|
|
113
126
|
You can also specify a custom retry logic with the `retry_if` option.
|
@@ -127,15 +140,19 @@ retry_options = {
|
|
127
140
|
|
128
141
|
### Call a block on every retry
|
129
142
|
|
130
|
-
You can specify a
|
131
|
-
There are many different applications for this feature, spacing from instrumentation to monitoring.
|
132
|
-
|
143
|
+
You can specify a proc object through the `retry_block` option that will be called before every
|
144
|
+
retry, before There are many different applications for this feature, spacing from instrumentation to monitoring.
|
145
|
+
|
146
|
+
|
147
|
+
The block is passed keyword arguments with contextual information: Request environment, middleware options, current number of retries, exception, and amount of time we will wait before retrying. (retry_block is called before the wait time happens)
|
148
|
+
|
149
|
+
|
133
150
|
For example, you might want to keep track of the response statuses:
|
134
151
|
|
135
152
|
```ruby
|
136
153
|
response_statuses = []
|
137
154
|
retry_options = {
|
138
|
-
retry_block: -> (env
|
155
|
+
retry_block: -> (env:, options:, retry_count:, exception:, will_retry_in:) { response_statuses << env.status }
|
139
156
|
}
|
140
157
|
```
|
141
158
|
|
@@ -147,8 +164,9 @@ Then, run `bin/test` to run the tests.
|
|
147
164
|
|
148
165
|
To install this gem onto your local machine, run `rake build`.
|
149
166
|
|
150
|
-
|
151
|
-
|
167
|
+
### Releasing a new version
|
168
|
+
|
169
|
+
To release a new version, make a commit with a message such as "Bumped to 0.0.2", and change the _Unreleased_ heading in `CHANGELOG.md` to a heading like "0.0.2 (2022-01-01)", and then use GitHub Releases to author a release. A GitHub Actions workflow then publishes a new gem to [RubyGems.org](https://rubygems.org/gems/faraday-retry).
|
152
170
|
|
153
171
|
## Contributing
|
154
172
|
|
@@ -26,7 +26,8 @@ module Faraday
|
|
26
26
|
:interval_randomness,
|
27
27
|
:backoff_factor, :exceptions,
|
28
28
|
:methods, :retry_if, :retry_block,
|
29
|
-
:retry_statuses
|
29
|
+
:retry_statuses, :rate_limit_retry_header,
|
30
|
+
:rate_limit_reset_header)
|
30
31
|
|
31
32
|
DEFAULT_CHECK = ->(_env, _exception) { false }
|
32
33
|
|
@@ -71,7 +72,7 @@ module Faraday
|
|
71
72
|
end
|
72
73
|
|
73
74
|
def retry_block
|
74
|
-
self[:retry_block] ||= proc {}
|
75
|
+
self[:retry_block] ||= proc {}
|
75
76
|
end
|
76
77
|
|
77
78
|
def retry_statuses
|
@@ -105,8 +106,16 @@ module Faraday
|
|
105
106
|
# if the exception produced is non-recoverable or if the
|
106
107
|
# the HTTP method called is not idempotent.
|
107
108
|
# @option options [Block] :retry_block block that is executed before
|
108
|
-
# every retry.
|
109
|
-
#
|
109
|
+
# every retry. The block will be yielded keyword arguments:
|
110
|
+
# * env [Faraday::Env]: Request environment
|
111
|
+
# * options [Faraday::Options]: middleware options
|
112
|
+
# * retry_count [Integer]: how many retries have already occured (starts at 0)
|
113
|
+
# * exception [Exception]: exception that triggered the retry,
|
114
|
+
# will be the synthetic `Faraday::RetriableResponse` if the
|
115
|
+
# retry was triggered by something other than an exception.
|
116
|
+
# * will_retry_in [Float]: retry_block is called *before* the retry
|
117
|
+
# delay, actual retry will happen in will_retry_in number of
|
118
|
+
# seconds.
|
110
119
|
# @option options [Array] :retry_statuses Array of Integer HTTP status
|
111
120
|
# codes or a single Integer value that determines whether to raise
|
112
121
|
# a Faraday::RetriableResponse exception based on the HTTP status code
|
@@ -118,7 +127,7 @@ module Faraday
|
|
118
127
|
end
|
119
128
|
|
120
129
|
def calculate_sleep_amount(retries, env)
|
121
|
-
retry_after = calculate_retry_after(env)
|
130
|
+
retry_after = [calculate_retry_after(env), calculate_rate_limit_reset(env)].compact.max
|
122
131
|
retry_interval = calculate_retry_interval(retries)
|
123
132
|
|
124
133
|
return if retry_after && retry_after > @options.max_interval
|
@@ -145,7 +154,13 @@ module Faraday
|
|
145
154
|
retries -= 1
|
146
155
|
rewind_files(request_body)
|
147
156
|
if (sleep_amount = calculate_sleep_amount(retries + 1, env))
|
148
|
-
@options.retry_block.call(
|
157
|
+
@options.retry_block.call(
|
158
|
+
env: env,
|
159
|
+
options: @options,
|
160
|
+
retry_count: @options.max - (retries + 1),
|
161
|
+
exception: e,
|
162
|
+
will_retry_in: sleep_amount
|
163
|
+
)
|
149
164
|
sleep sleep_amount
|
150
165
|
retry
|
151
166
|
end
|
@@ -198,21 +213,18 @@ module Faraday
|
|
198
213
|
end
|
199
214
|
end
|
200
215
|
|
216
|
+
# RFC for RateLimit Header Fields for HTTP:
|
217
|
+
# https://www.ietf.org/archive/id/draft-ietf-httpapi-ratelimit-headers-05.html#name-fields-definition
|
218
|
+
def calculate_rate_limit_reset(env)
|
219
|
+
reset_header = @options.rate_limit_reset_header || 'RateLimit-Reset'
|
220
|
+
parse_retry_header(env, reset_header)
|
221
|
+
end
|
222
|
+
|
201
223
|
# MDN spec for Retry-After header:
|
202
224
|
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
|
203
225
|
def calculate_retry_after(env)
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
retry_after_value = env[:response_headers]['Retry-After']
|
208
|
-
|
209
|
-
# Try to parse date from the header value
|
210
|
-
begin
|
211
|
-
datetime = DateTime.rfc2822(retry_after_value)
|
212
|
-
datetime.to_time - Time.now.utc
|
213
|
-
rescue ArgumentError
|
214
|
-
retry_after_value.to_f
|
215
|
-
end
|
226
|
+
retry_header = @options.rate_limit_retry_header || 'Retry-After'
|
227
|
+
parse_retry_header(env, retry_header)
|
216
228
|
end
|
217
229
|
|
218
230
|
def calculate_retry_interval(retries)
|
@@ -225,6 +237,21 @@ module Faraday
|
|
225
237
|
|
226
238
|
current_interval + random_interval
|
227
239
|
end
|
240
|
+
|
241
|
+
def parse_retry_header(env, header)
|
242
|
+
response_headers = env[:response_headers]
|
243
|
+
return unless response_headers
|
244
|
+
|
245
|
+
retry_after_value = env[:response_headers][header]
|
246
|
+
|
247
|
+
# Try to parse date from the header value
|
248
|
+
begin
|
249
|
+
datetime = DateTime.rfc2822(retry_after_value)
|
250
|
+
datetime.to_time - Time.now.utc
|
251
|
+
rescue ArgumentError
|
252
|
+
retry_after_value.to_f
|
253
|
+
end
|
254
|
+
end
|
228
255
|
end
|
229
256
|
end
|
230
257
|
end
|
data/lib/faraday/retry.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,141 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: faraday-retry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mattia Giuffrida
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
11
|
+
date: 2023-03-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faraday
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '13.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '13.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.21.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.21.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.21.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.21.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop-packaging
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.5.0
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.5.0
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop-performance
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1.0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '1.0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rubocop-rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '2.0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '2.0'
|
13
139
|
description: 'Catches exceptions and retries each request a limited number of times.
|
14
140
|
|
15
141
|
'
|
@@ -31,11 +157,10 @@ licenses:
|
|
31
157
|
- MIT
|
32
158
|
metadata:
|
33
159
|
bug_tracker_uri: https://github.com/lostisland/faraday-retry/issues
|
34
|
-
changelog_uri: https://github.com/lostisland/faraday-retry/blob/
|
35
|
-
documentation_uri: http://www.rubydoc.info/gems/faraday-retry/1.0
|
160
|
+
changelog_uri: https://github.com/lostisland/faraday-retry/blob/v2.1.0/CHANGELOG.md
|
161
|
+
documentation_uri: http://www.rubydoc.info/gems/faraday-retry/2.1.0
|
36
162
|
homepage_uri: https://github.com/lostisland/faraday-retry
|
37
163
|
source_code_uri: https://github.com/lostisland/faraday-retry
|
38
|
-
wiki_uri: https://github.com/lostisland/faraday-retry/wiki
|
39
164
|
post_install_message:
|
40
165
|
rdoc_options: []
|
41
166
|
require_paths:
|
@@ -44,7 +169,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
44
169
|
requirements:
|
45
170
|
- - ">="
|
46
171
|
- !ruby/object:Gem::Version
|
47
|
-
version: '2.
|
172
|
+
version: '2.6'
|
48
173
|
- - "<"
|
49
174
|
- !ruby/object:Gem::Version
|
50
175
|
version: '4'
|