- 
                Notifications
    You must be signed in to change notification settings 
- Fork 68
Implement retryable calls #98
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
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
          
        
      | 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
b96270e2993377f121fccdbf117676830f059049 feat(async,blocking)!: implement retryable calls (valued mammal) Pull request description: 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 the `Builder`. `max_retries` is also added to each of `AsyncClient`, `BlockingClient`. I added the dependency `async-std` in order to have async `sleep`. 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 bdk `wallet_esplora_*` example crates against https://blockstream.info/testnet/api and it seemed to resolve the 429 issue. ACKs for top commit: oleonardolima: ACK b96270e2993377f121fccdbf117676830f059049 notmandatory: tACK b96270e2993377f121fccdbf117676830f059049 Tree-SHA512: 78124106959ba9a5cce58e343bbf30c29b4bc7e1ac434ba71eeb2e774d73ea003d0520139781062947ed27563748925c24998b2a5be450bc511bb2c7090a6682
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_retriesto theBuilder.max_retriesis also added to each ofAsyncClient,BlockingClient. I added the dependencyasync-stdin order to have asyncsleep.Instead of implementing a trait on the
Requesttype 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.