fix recursion in retry (#49)

This commit is contained in:
Conrad Ludgate 2022-07-09 23:34:23 +01:00 committed by GitHub
parent ad18935d1f
commit b3f7ce40d3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -58,33 +58,22 @@ impl<T: RetryPolicy + Send + Sync> Middleware for RetryTransientMiddleware<T> {
// downstream. This will guard against previous retries poluting `Extensions`. // downstream. This will guard against previous retries poluting `Extensions`.
// That is, we only return what's populated in the typemap for the last retry attempt // That is, we only return what's populated in the typemap for the last retry attempt
// and copy those into the the `global` Extensions map. // and copy those into the the `global` Extensions map.
self.execute_with_retry_recursive(req, next, extensions, 0) self.execute_with_retry(req, next, extensions).await
.await
} }
} }
impl<T: RetryPolicy + Send + Sync> RetryTransientMiddleware<T> { impl<T: RetryPolicy + Send + Sync> RetryTransientMiddleware<T> {
/// **RECURSIVE**.
///
/// SAFETY: The condition for termination is the number of retries
/// set on the `RetryOption` object which is capped to 10 therefore
/// we can know that this will not cause a overflow of the stack.
///
/// This function will try to execute the request, if it fails /// This function will try to execute the request, if it fails
/// with an error classified as transient it will call itself /// with an error classified as transient it will call itself
/// to retry the request. /// to retry the request.
/// async fn execute_with_retry<'a>(
/// NOTE: This function is not async because calling an async function
/// recursively is not allowed.
///
fn execute_with_retry_recursive<'a>(
&'a self, &'a self,
req: Request, req: Request,
next: Next<'a>, next: Next<'a>,
ext: &'a mut Extensions, ext: &'a mut Extensions,
n_past_retries: u32, ) -> Result<Response> {
) -> futures::future::BoxFuture<'a, Result<Response>> { let mut n_past_retries = 0;
Box::pin(async move { loop {
// Cloning the request object before-the-fact is not ideal.. // Cloning the request object before-the-fact is not ideal..
// However, if the body of the request is not static, e.g of type `Bytes`, // However, if the body of the request is not static, e.g of type `Bytes`,
// the Clone operation should be of constant complexity and not O(N) // the Clone operation should be of constant complexity and not O(N)
@ -95,13 +84,11 @@ impl<T: RetryPolicy + Send + Sync> RetryTransientMiddleware<T> {
)) ))
})?; })?;
let cloned_next = next.clone(); let result = next.clone().run(duplicate_request, ext).await;
let result = next.run(req, ext).await;
// We classify the response which will return None if not // We classify the response which will return None if not
// errors were returned. // errors were returned.
match Retryable::from_reqwest_response(&result) { break match Retryable::from_reqwest_response(&result) {
Some(retryable) Some(retryable)
if retryable == Retryable::Transient if retryable == Retryable::Transient
&& n_past_retries < MAXIMUM_NUMBER_OF_RETRIES => && n_past_retries < MAXIMUM_NUMBER_OF_RETRIES =>
@ -121,19 +108,14 @@ impl<T: RetryPolicy + Send + Sync> RetryTransientMiddleware<T> {
); );
tokio::time::sleep(duration).await; tokio::time::sleep(duration).await;
self.execute_with_retry_recursive( n_past_retries += 1;
duplicate_request, continue;
cloned_next,
ext,
n_past_retries + 1,
)
.await
} else { } else {
result result
} }
} }
Some(_) | None => result, Some(_) | None => result,
} };
}) }
} }
} }