-
Notifications
You must be signed in to change notification settings - Fork 79
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
Add retries to error message #159
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
use std::time::{Duration, SystemTime}; | ||
|
||
use crate::retryable_strategy::RetryableStrategy; | ||
use crate::{retryable::Retryable, retryable_strategy::DefaultRetryableStrategy}; | ||
use crate::{retryable::Retryable, retryable_strategy::DefaultRetryableStrategy, RetryError}; | ||
use anyhow::anyhow; | ||
use http::Extensions; | ||
use reqwest::{Request, Response}; | ||
|
@@ -56,14 +56,14 @@ | |
/// | ||
/// This middleware always errors when given requests with streaming bodies, before even executing | ||
/// the request. When this happens you'll get an [`Error::Middleware`] with the message | ||
/// 'Request object is not clonable. Are you passing a streaming body?'. | ||
/// | ||
/// Some workaround suggestions: | ||
/// * If you can fit the data in memory, you can instead build static request bodies e.g. with | ||
/// `Body`'s `From<String>` or `From<Bytes>` implementations. | ||
/// * You can wrap this middleware in a custom one which skips retries for streaming requests. | ||
/// * You can write a custom retry middleware that builds new streaming requests from the data | ||
/// source directly, avoiding the issue of streaming requests not being clonable. | ||
pub struct RetryTransientMiddleware< | ||
T: RetryPolicy + Send + Sync + 'static, | ||
R: RetryableStrategy + Send + Sync + 'static = DefaultRetryableStrategy, | ||
|
@@ -149,7 +149,7 @@ | |
// since the byte abstraction is a shared pointer over a buffer. | ||
let duplicate_request = req.try_clone().ok_or_else(|| { | ||
Error::Middleware(anyhow!( | ||
"Request object is not clonable. Are you passing a streaming body?".to_string() | ||
)) | ||
})?; | ||
|
||
|
@@ -157,7 +157,7 @@ | |
|
||
// We classify the response which will return None if not | ||
// errors were returned. | ||
break match self.retryable_strategy.handle(&result) { | ||
match self.retryable_strategy.handle(&result) { | ||
Some(Retryable::Transient) => { | ||
// If the response failed and the error type was transient | ||
// we can safely try to retry the request. | ||
|
@@ -183,11 +183,24 @@ | |
|
||
n_past_retries += 1; | ||
continue; | ||
} else { | ||
result | ||
} | ||
} | ||
Some(_) | None => result, | ||
Some(_) | None => {} | ||
}; | ||
|
||
// Report whether we failed with or without retries. | ||
break if n_past_retries > 0 { | ||
result.map_err(|err| { | ||
Error::Middleware( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this looks wrong, on this code path, this is not a middleware error, it's a request error. a good example of a middleware error is when the middleware cannot do something it needs to do to function, like at like 146 above. the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. to make this work you need to change There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't change There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you can change There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would require changing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah i see now, that's annoying |
||
RetryError::WithRetries { | ||
retries: n_past_retries, | ||
err, | ||
} | ||
.into(), | ||
) | ||
}) | ||
} else { | ||
result.map_err(|err| Error::Middleware(RetryError::Error(err).into())) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is also not a middleware error There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's the most common pattern in rust to have one error type per crate (or sometimes per top level module, like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the change you are making here, where normal There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it almost completely defeats the point of having There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. imo, these changelog entries are not sufficient, you should indicate a semver breaking change There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current interpretation of the error enum assumes two cases:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point, i've bumped |
||
}; | ||
} | ||
} | ||
|
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.
suggestion:
using an enum here is going to make it harder for a user to match on this error now, because they have to handle an extra branch that is still just a
reqwest_middleware::Error
it seems completely unnecessary, why don't you just make this a struct that has a retry count and an error?
the only place where
RetryError::Error
was constructed, we actually do have a retry count that we could supply.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.
for example, suppose I'm a user, and i want to do further error handling and call
is_body
on the underlyingReqwest::Error
. https://docs.rs/reqwest/latest/reqwest/struct.Error.html#method.is_bodyWith this enum that requires a lot more boilerplate code
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.
We need the enum so we can insert the error message about the number of retries when we did them, while not showing an irrelevant "Failed after 0 retries" otherwise.
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.
that doesn't seem like such a big problem. maybe you could customize
Display
so that it doesn't show when the number is 0? the enum is going to create boilerplate at any call-site that wants to further inspect the errorThere 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.
We can't skip the display on 0 because it
ToString
has to return a string even if it's an empty string, the only way i know of how to skip a step in the error trace iserror(transparent)
.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 think maybe you misunderstood me, you could do something like this
then you could implement
Display
likeor similar