-
Notifications
You must be signed in to change notification settings - Fork 48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement retryable calls #98
Implement retryable calls #98
Conversation
Pull Request Test Coverage Report for Build 10962612990Details
💛 - Coveralls |
- apply some standard on `Cargo.toml` deps. - minor docstring improvements, and fix missing docstrings.
- remove duplicated HTTP client code for handling GET and POST requests. - adds a few new methods to `AsyncClient` implementation, the new methods are responsible to handle common HTTP requests and parsing. It was previously duplicated throughout the Esplora API implementation, but now it follows the same approach already implemented for blocking client (`BlockingClient`).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concept ACK
1f67ece
to
e50ce73
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's looking good! Thanks for working on this, I just left a few comments and nits.
I wonder if we should try to add a test for the retry behavior, but unsure if it's worth it.
src/async.rs
Outdated
if attempts < self.max_retries | ||
&& RETRYABLE_ERROR_CODES.contains(&resp.status().as_u16()) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: You can apply the same approach here and extract it to is_retryable_status
too.
src/async.rs
Outdated
let mut attempts = 0; | ||
|
||
loop { | ||
match self.client.get(url).send().await { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: You can also just propagate the error here, right? (edit: unsure if an if would be a better style)
P.S.: The same applies to the blocking function.
Cargo.toml
Outdated
@@ -17,6 +17,7 @@ name = "esplora_client" | |||
path = "src/lib.rs" | |||
|
|||
[dependencies] | |||
async-std = "1.13.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be an optional dependency, and only available when using the async
feature.
Also, I don't think we need all the features in its default features, and only the std
feature, is that right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried with only std
feature and it seems that the task
module is only available with "default"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, looks like it's behind the default feature here using their cfg_default!
macro.
@oleonardolima I haven't come up with a test for the retry behavior, although we can at least be confident that the methods that call I'm testing against https://blockstream.info/api and now seeing some "500 - Internal Server Error", so I'm considering adding this to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 693af39
nit: I'd just update the last commit message to something like: refactor(async,blocking): `get_with_retry` fn
to follow the standard.
This change also adds a new field `max_retries` to each of `Builder`, `AsyncClient`, and `BlockingClient` that defines the maximum number of times we may retry a request.
693af39
to
b96270e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK b96270e
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tACK b96270e
I tested with example_esplora
on mainnet (with gap-limit 100) and testnet. In both cases tests failed with the master branch version of this code and works with the fixes in this PR.
|
||
/// Sends a GET request to the given `url`, retrying failed attempts | ||
/// for retryable error codes until max retries hit. | ||
pub fn get_with_retry(&self, url: &str) -> Result<Response, Error> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, I don't think it was intended for this to be pub
.
bfaa9ae fix(blocking): `get_with_retry` is private method (valued mammal) Pull request description: Clean up after #98 which accidentally exposed `get_with_retry` publicly for the blocking client. ACKs for top commit: oleonardolima: ACK bfaa9ae Tree-SHA512: 33abd34e919e27069107947c34915b9de3547ab1d78291841f2052552abd23fd59621ee6ae3ed9bd8e3eda608c2e5b9285529df1dcba8a4a1705ca1f1491c136
Based on #93 the PR implements retryable calls for request failure due to too many requests (429) or service unavailable (503).
Inspired by #71
h/t @e1a0a0ea
Notes
I've added the field
max_retries
to theBuilder
.max_retries
is also added to each ofAsyncClient
,BlockingClient
. I added the dependencyasync-std
in order to have asyncsleep
.Instead of implementing a trait on the
Request
type as in #71, the approach is to add a method on the client that sends a get request to a url and returns the response after allowing for retries. I tested on the bdkwallet_esplora_*
example crates against https://blockstream.info/testnet/api and it seemed to resolve the 429 issue.