Skip to content

feat: Add adjust support for Retry#192

Merged
Xuanwo merged 1 commit intomainfrom
adjust
Apr 9, 2025
Merged

feat: Add adjust support for Retry#192
Xuanwo merged 1 commit intomainfrom
adjust

Conversation

@Xuanwo
Copy link
Copy Markdown
Owner

@Xuanwo Xuanwo commented Apr 9, 2025

Demo:

use core::time::Duration;
use std::error::Error;
use std::fmt::Display;
use std::fmt::Formatter;
use anyhow::Result;
use backon::ExponentialBuilder;
use backon::Retryable;
use reqwest::header::HeaderMap;
use reqwest::StatusCode;

#[derive(Debug)]
struct HttpError {
    headers: HeaderMap,
}
impl Display for HttpError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "http error")
    }
}
impl Error for HttpError {}

async fn fetch() -> Result<String> {
    let resp = reqwest::get("https://www.rust-lang.org").await?;
    if resp.status() != StatusCode::OK {
        let source = HttpError {
            headers: resp.headers().clone(),
        };
        return Err(anyhow::Error::new(source));
    }
    Ok(resp.text().await?)
}


#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> {
    let content = fetch
        .retry(ExponentialBuilder::default())
        .adjust(|err, dur| {
            match err.downcast_ref::<HttpError>() {
                Some(v) => {
                    if let Some(retry_after) = v.headers.get("Retry-After") {
                        // Parse the Retry-After header and adjust the backoff duration
                        let retry_after = retry_after.to_str().unwrap_or("0");
                        let retry_after = retry_after.parse::<u64>().unwrap_or(0);
                        Some(Duration::from_secs(retry_after))
                    } else {
                        dur
                    }
                }
                None => dur,
            }
        })
        .await?;
    println!("fetch succeeded: {}", content);
    Ok(())
}

Signed-off-by: Xuanwo <github@xuanwo.io>
@Xuanwo Xuanwo merged commit 741c363 into main Apr 9, 2025
7 checks passed
@Xuanwo Xuanwo deleted the adjust branch April 9, 2025 08:44
///
/// When a retry occurs, the provided function will be called with the error and the proposed backoff duration, allowing you to modify the final duration used.
///
/// If the function returns `None`, it indicates that no further retries should be made, and the error will be returned regardless of the backoff duration provided by the input.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn't anything wrong with this decision, but this makes the when() method somewhat redundant, no?

I can decide with the adjust() method the following:

  1. If I should retry or not
  2. If I should use the default timeout from my backoff strategy or if I should override it.

While the when() method can only decide option 1.

This makes me lean towards merging this functionality into the when() method, but that introduces API breakage.

So I guess everything is fine, despite when() becoming slightly less useful.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Both of notify and when can be seen as a simple case of adjust. 😆

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support to allow fetching external statuses, such as the Retry-After header in HTTP responses. Potential for dynamic backoffs

2 participants