faraday-retry 2.1.0 → 2.3.1
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 +19 -5
- data/README.md +36 -8
- data/lib/faraday/retry/middleware.rb +38 -32
- data/lib/faraday/retry/retryable.rb +46 -0
- data/lib/faraday/retry/version.rb +1 -1
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 76aff4f986315a337600432e6d2458f4080004755f37583ac413bf454713602c
|
4
|
+
data.tar.gz: fe612cfa054e9f293d533404759734672284f92258f43a17fb9febbb455f5b61
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 11f9e7f0a270962bcc25ab31690fa8c14dc0fb461a74b8a7db94d7d719e48679590f12b554e37da579ea57824dc35fc13fc6820e4772b4de08b269e88924202f
|
7
|
+
data.tar.gz: 305559acfa19db8b994f2dc8e2e734f31fd1fdbe255b82eb3612ce5630d0c9b325ad46621892675a4e7fa6b85f99329fa0af635923619d650b69ce5a837689e5
|
data/CHANGELOG.md
CHANGED
@@ -1,14 +1,24 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
##
|
3
|
+
## Unreleased
|
4
4
|
|
5
|
-
|
5
|
+
_nothing yet_
|
6
6
|
|
7
|
-
|
7
|
+
## v2.2.1 (2024-04-15)
|
8
|
+
|
9
|
+
* Avoid deprecation warning about ::UploadIO constant when used without faraday-multipart gem [PR #37](https://github.com/lostisland/faraday-retry/pull/37) [@iMacTia]
|
10
|
+
* Documentation update [PR #30](https://github.com/lostisland/faraday-retry/pull/30) [@olleolleolle]
|
11
|
+
* Documentation update [PR #32](https://github.com/lostisland/faraday-retry/pull/32) Thanks, [@Drowze]!
|
12
|
+
|
13
|
+
## v2.2.0 (2023-06-01)
|
8
14
|
|
9
|
-
|
15
|
+
* Support new `header_parser_block` option. [PR #28](https://github.com/lostisland/faraday-retry/pull/28). Thanks, [@zavan]!
|
16
|
+
|
17
|
+
## v2.1.0 (2023-03-03)
|
18
|
+
|
19
|
+
* Support for custom RateLimit headers. [PR #13](https://github.com/lostisland/faraday-retry/pull/13). Thanks, [@brookemckim]!
|
10
20
|
|
11
|
-
|
21
|
+
v2.1.1 (2023-02-17) is a spurious not-released version that you may have seen mentioned in this CHANGELOG.
|
12
22
|
|
13
23
|
## v2.0.0 (2022-06-08)
|
14
24
|
|
@@ -33,3 +43,7 @@ This release consists of the same middleware that was previously bundled with Fa
|
|
33
43
|
|
34
44
|
[@maxprokopiev]: https://github.com/maxprokopiev
|
35
45
|
[@brookemckim]: https://github.com/brookemckim
|
46
|
+
[@zavan]: https://github.com/zavan
|
47
|
+
[@Drowze]: https://github.com/Drowze
|
48
|
+
[@olleolleolle]: https://github.com/olleolleolle
|
49
|
+
[@iMacTia]: https://github.com/iMacTia
|
data/README.md
CHANGED
@@ -75,7 +75,7 @@ retry_options = {
|
|
75
75
|
#### Specify which exceptions should trigger a retry
|
76
76
|
|
77
77
|
You can provide an `exceptions` option with a list of exceptions that will replace
|
78
|
-
the default
|
78
|
+
the default exceptions: `Errno::ETIMEDOUT`, `Timeout::Error`, `Faraday::TimeoutError`, `Faraday::Error::RetriableResponse`.
|
79
79
|
This can be particularly useful when combined with the [RaiseError][raise_error] middleware.
|
80
80
|
|
81
81
|
```ruby
|
@@ -84,6 +84,14 @@ retry_options = {
|
|
84
84
|
}
|
85
85
|
```
|
86
86
|
|
87
|
+
If you want to inherit default exceptions, do it this way.
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
retry_options = {
|
91
|
+
exceptions: Faraday::Retry::Middleware::DEFAULT_EXCEPTIONS + [Faraday::ResourceNotFound, Faraday::UnauthorizedError]
|
92
|
+
}
|
93
|
+
```
|
94
|
+
|
87
95
|
#### Specify on which response statuses to retry
|
88
96
|
|
89
97
|
By default the `Retry` middleware will only retry the request if one of the expected exceptions arise.
|
@@ -111,13 +119,14 @@ retry_options = {
|
|
111
119
|
}
|
112
120
|
```
|
113
121
|
|
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
|
122
|
+
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, as well as a block to parse the headers:
|
115
123
|
|
116
124
|
```ruby
|
117
125
|
retry_options = {
|
118
126
|
retry_statuses: [429],
|
119
127
|
rate_limit_retry_header: 'x-rate-limit-retry-after',
|
120
|
-
rate_limit_reset_header: 'x-rate-limit-reset'
|
128
|
+
rate_limit_reset_header: 'x-rate-limit-reset',
|
129
|
+
header_parser_block: ->(value) { Time.at(value.to_i).utc - Time.now.utc }
|
121
130
|
}
|
122
131
|
```
|
123
132
|
|
@@ -140,13 +149,11 @@ retry_options = {
|
|
140
149
|
|
141
150
|
### Call a block on every retry
|
142
151
|
|
143
|
-
You can specify a proc object through the `retry_block` option that will be called before every
|
144
|
-
|
145
|
-
|
152
|
+
You can specify a proc object through the `retry_block` option that will be called before every retry.
|
153
|
+
There are many different applications for this feature, ranging from instrumentation to monitoring.
|
146
154
|
|
147
155
|
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
156
|
|
149
|
-
|
150
157
|
For example, you might want to keep track of the response statuses:
|
151
158
|
|
152
159
|
```ruby
|
@@ -156,6 +163,27 @@ retry_options = {
|
|
156
163
|
}
|
157
164
|
```
|
158
165
|
|
166
|
+
### Call a block after retries have been exhausted
|
167
|
+
|
168
|
+
You can specify a lambda object through the `exhausted_retries_block` option that will be called after all retries are exhausted.
|
169
|
+
This block will be called once.
|
170
|
+
|
171
|
+
The block is passed keyword arguments with contextual information and passed your data:
|
172
|
+
* Request environment,
|
173
|
+
* exception,
|
174
|
+
* middleware options
|
175
|
+
* and your data.
|
176
|
+
|
177
|
+
In a lambda you can pass any logic for further work.
|
178
|
+
|
179
|
+
For example, you might want to update user by request query.
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
retry_options = {
|
183
|
+
exhausted_retries_block: -> (user_id:, env:, exception:, options:) { User.find_by!(id: user_id).do_admin! }
|
184
|
+
}
|
185
|
+
```
|
186
|
+
|
159
187
|
## Development
|
160
188
|
|
161
189
|
After checking out the repo, run `bin/setup` to install dependencies.
|
@@ -176,4 +204,4 @@ Bug reports and pull requests are welcome on [GitHub](https://github.com/lostisl
|
|
176
204
|
|
177
205
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
178
206
|
|
179
|
-
[raise_error]: https://lostisland.github.io/faraday
|
207
|
+
[raise_error]: https://lostisland.github.io/faraday/#/middleware/included/raising-errors
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'retryable'
|
4
|
+
|
3
5
|
module Faraday
|
4
6
|
module Retry
|
5
7
|
# This class provides the main implementation for your middleware.
|
@@ -15,6 +17,8 @@ module Faraday
|
|
15
17
|
# (see "retry" middleware: https://github.com/lostisland/faraday/blob/main/lib/faraday/request/retry.rb#L142).
|
16
18
|
# IMPORTANT: Remember to call `@app.call(env)` or `super` to not interrupt the middleware chain!
|
17
19
|
class Middleware < Faraday::Middleware
|
20
|
+
include Retryable
|
21
|
+
|
18
22
|
DEFAULT_EXCEPTIONS = [
|
19
23
|
Errno::ETIMEDOUT, 'Timeout::Error',
|
20
24
|
Faraday::TimeoutError, Faraday::RetriableResponse
|
@@ -27,7 +31,8 @@ module Faraday
|
|
27
31
|
:backoff_factor, :exceptions,
|
28
32
|
:methods, :retry_if, :retry_block,
|
29
33
|
:retry_statuses, :rate_limit_retry_header,
|
30
|
-
:rate_limit_reset_header
|
34
|
+
:rate_limit_reset_header, :header_parser_block,
|
35
|
+
:exhausted_retries_block)
|
31
36
|
|
32
37
|
DEFAULT_CHECK = ->(_env, _exception) { false }
|
33
38
|
|
@@ -78,6 +83,10 @@ module Faraday
|
|
78
83
|
def retry_statuses
|
79
84
|
Array(self[:retry_statuses] ||= [])
|
80
85
|
end
|
86
|
+
|
87
|
+
def exhausted_retries_block
|
88
|
+
self[:exhausted_retries_block] ||= proc {}
|
89
|
+
end
|
81
90
|
end
|
82
91
|
|
83
92
|
# @param app [#call]
|
@@ -95,8 +104,8 @@ module Faraday
|
|
95
104
|
# 'Timeout::Error', Faraday::TimeoutError, Faraday::RetriableResponse])
|
96
105
|
# The list of exceptions to handle. Exceptions can be given as
|
97
106
|
# Class, Module, or String.
|
98
|
-
# @option options [Array] :methods (the idempotent HTTP methods
|
99
|
-
# in IDEMPOTENT_METHODS) A list of HTTP methods to retry without
|
107
|
+
# @option options [Array<Symbol>] :methods (the idempotent HTTP methods
|
108
|
+
# in IDEMPOTENT_METHODS) A list of HTTP methods, as symbols, to retry without
|
100
109
|
# calling retry_if. Pass an empty Array to call retry_if
|
101
110
|
# for all exceptions.
|
102
111
|
# @option options [Block] :retry_if (false) block that will receive
|
@@ -120,6 +129,17 @@ module Faraday
|
|
120
129
|
# codes or a single Integer value that determines whether to raise
|
121
130
|
# a Faraday::RetriableResponse exception based on the HTTP status code
|
122
131
|
# of an HTTP response.
|
132
|
+
# @option options [Block] :header_parser_block block that will receive
|
133
|
+
# the the value of the retry header and should return the number of
|
134
|
+
# seconds to wait before retrying the request. This is useful if the
|
135
|
+
# value of the header is not a number of seconds or a RFC 2822 formatted date.
|
136
|
+
# @option options [Block] :exhausted_retries_block block will receive
|
137
|
+
# when all attempts are exhausted. The block will be yielded keyword arguments:
|
138
|
+
# * env [Faraday::Env]: Request environment
|
139
|
+
# * exception [Exception]: exception that triggered the retry,
|
140
|
+
# will be the synthetic `Faraday::RetriableResponse` if the
|
141
|
+
# retry was triggered by something other than an exception.
|
142
|
+
# * options [Faraday::Options]: middleware options
|
123
143
|
def initialize(app, options = nil)
|
124
144
|
super(app)
|
125
145
|
@options = Options.from(options)
|
@@ -143,32 +163,14 @@ module Faraday
|
|
143
163
|
def call(env)
|
144
164
|
retries = @options.max
|
145
165
|
request_body = env[:body]
|
146
|
-
|
166
|
+
|
167
|
+
with_retries(env: env, options: @options, retries: retries, body: request_body, errmatch: @errmatch) do
|
147
168
|
# after failure env[:body] is set to the response body
|
148
169
|
env[:body] = request_body
|
170
|
+
|
149
171
|
@app.call(env).tap do |resp|
|
150
172
|
raise Faraday::RetriableResponse.new(nil, resp) if @options.retry_statuses.include?(resp.status)
|
151
173
|
end
|
152
|
-
rescue @errmatch => e
|
153
|
-
if retries.positive? && retry_request?(env, e)
|
154
|
-
retries -= 1
|
155
|
-
rewind_files(request_body)
|
156
|
-
if (sleep_amount = calculate_sleep_amount(retries + 1, env))
|
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
|
-
)
|
164
|
-
sleep sleep_amount
|
165
|
-
retry
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
raise unless e.is_a?(Faraday::RetriableResponse)
|
170
|
-
|
171
|
-
e.response
|
172
174
|
end
|
173
175
|
end
|
174
176
|
|
@@ -205,11 +207,11 @@ module Faraday
|
|
205
207
|
end
|
206
208
|
|
207
209
|
def rewind_files(body)
|
208
|
-
return unless defined?(UploadIO)
|
210
|
+
return unless defined?(Faraday::UploadIO)
|
209
211
|
return unless body.is_a?(Hash)
|
210
212
|
|
211
213
|
body.each do |_, value|
|
212
|
-
value.rewind if value.is_a?(UploadIO)
|
214
|
+
value.rewind if value.is_a?(Faraday::UploadIO)
|
213
215
|
end
|
214
216
|
end
|
215
217
|
|
@@ -244,12 +246,16 @@ module Faraday
|
|
244
246
|
|
245
247
|
retry_after_value = env[:response_headers][header]
|
246
248
|
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
249
|
+
if @options.header_parser_block
|
250
|
+
@options.header_parser_block.call(retry_after_value)
|
251
|
+
else
|
252
|
+
# Try to parse date from the header value
|
253
|
+
begin
|
254
|
+
datetime = DateTime.rfc2822(retry_after_value)
|
255
|
+
datetime.to_time - Time.now.utc
|
256
|
+
rescue ArgumentError
|
257
|
+
retry_after_value.to_f
|
258
|
+
end
|
253
259
|
end
|
254
260
|
end
|
255
261
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Faraday
|
4
|
+
# Adds the ability to retry a request based on settings and errors that have occurred.
|
5
|
+
module Retryable
|
6
|
+
def with_retries(env:, options:, retries:, body:, errmatch:)
|
7
|
+
yield
|
8
|
+
rescue errmatch => e
|
9
|
+
exhausted_retries(options, env, e) if retries_zero?(retries, env, e)
|
10
|
+
|
11
|
+
if retries.positive? && retry_request?(env, e)
|
12
|
+
retries -= 1
|
13
|
+
rewind_files(body)
|
14
|
+
if (sleep_amount = calculate_sleep_amount(retries + 1, env))
|
15
|
+
options.retry_block.call(
|
16
|
+
env: env,
|
17
|
+
options: options,
|
18
|
+
retry_count: options.max - (retries + 1),
|
19
|
+
exception: e,
|
20
|
+
will_retry_in: sleep_amount
|
21
|
+
)
|
22
|
+
sleep sleep_amount
|
23
|
+
retry
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
raise unless e.is_a?(Faraday::RetriableResponse)
|
28
|
+
|
29
|
+
e.response
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def retries_zero?(retries, env, exception)
|
35
|
+
retries.zero? && retry_request?(env, exception)
|
36
|
+
end
|
37
|
+
|
38
|
+
def exhausted_retries(options, env, exception)
|
39
|
+
options.exhausted_retries_block.call(
|
40
|
+
env: env,
|
41
|
+
exception: exception,
|
42
|
+
options: options
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: faraday-retry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1
|
4
|
+
version: 2.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mattia Giuffrida
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-04-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -151,17 +151,18 @@ files:
|
|
151
151
|
- lib/faraday/retriable_response.rb
|
152
152
|
- lib/faraday/retry.rb
|
153
153
|
- lib/faraday/retry/middleware.rb
|
154
|
+
- lib/faraday/retry/retryable.rb
|
154
155
|
- lib/faraday/retry/version.rb
|
155
156
|
homepage: https://github.com/lostisland/faraday-retry
|
156
157
|
licenses:
|
157
158
|
- MIT
|
158
159
|
metadata:
|
159
160
|
bug_tracker_uri: https://github.com/lostisland/faraday-retry/issues
|
160
|
-
changelog_uri: https://github.com/lostisland/faraday-retry/blob/v2.1
|
161
|
-
documentation_uri: http://www.rubydoc.info/gems/faraday-retry/2.1
|
161
|
+
changelog_uri: https://github.com/lostisland/faraday-retry/blob/v2.3.1/CHANGELOG.md
|
162
|
+
documentation_uri: http://www.rubydoc.info/gems/faraday-retry/2.3.1
|
162
163
|
homepage_uri: https://github.com/lostisland/faraday-retry
|
163
164
|
source_code_uri: https://github.com/lostisland/faraday-retry
|
164
|
-
post_install_message:
|
165
|
+
post_install_message:
|
165
166
|
rdoc_options: []
|
166
167
|
require_paths:
|
167
168
|
- lib
|
@@ -180,7 +181,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
180
181
|
version: '0'
|
181
182
|
requirements: []
|
182
183
|
rubygems_version: 3.1.6
|
183
|
-
signing_key:
|
184
|
+
signing_key:
|
184
185
|
specification_version: 4
|
185
186
|
summary: Catches exceptions and retries each request a limited number of times
|
186
187
|
test_files: []
|