Skip to content
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

Change semantics to avoid duplicate code in expanded output #2

Closed
wants to merge 1 commit into from

Conversation

Brendan-Blanchard
Copy link
Owner

This uses an Option<?> to store the result instead of first computing the value, then double-expanding the wrapped function's block of code inside the loop.

The only side effect of this approach is the possibility of a panic, and that max_retries in the configuration is now more like max_attempts or max_tries, since the loop encompasses the first attempt. If that change is fine, I'll rename it more appropriately. Functionally it's gone from describing "retry at most X times", to "attempt at most X times". I'm indifferent to either way, but it just needs to be named correctly and documented.

Otherwise this adds a few tests for cases that came up with real use-cases of mine that the test suite wasn't covering, and a test to show the panic for a backoff that has <= 0 for the max_retries value.

Without dumping the entire expanded output, the following code expands like this:

#[tokio::main]
async fn main() {
    let val = retryable().await;
    assert_eq!(Ok(3), val);
}

fn retry_if(_: &Result<i32, ParseIntError>) -> bool {
    false
}

#[retry(BACKOFF_CONFIG, retry_if)]
async fn retryable() -> Result<i32, ParseIntError> {
    tokio::time::sleep(Duration::from_secs(0)).await;
    i32::from_str("3")
}
async fn retryable() -> Result<i32, ParseIntError> {
    let start = tokio::time::Instant::now();
    let backoff_max = BACKOFF_CONFIG.backoff_max.unwrap_or(std::time::Duration::MAX);
    let max_tries = BACKOFF_CONFIG.max_retries;
    let mut result = None;
    for attempt in 0..max_tries {
        result = Some({  // < ------------------------------ single code expansion here
            tokio::time::sleep(Duration::from_secs(0)).await;
            i32::from_str("3")
        });
        if !retry_if(result.as_ref().unwrap()) {
            break;
        }
        let retry_wait = BACKOFF_CONFIG
            .t_wait
            .mul_f64(BACKOFF_CONFIG.backoff.powi(attempt))
            .min(backoff_max);
        if let Some(max_wait) = BACKOFF_CONFIG.t_wait_max {
            let now = tokio::time::Instant::now();
            let since_start = now - start;
            let will_exceed_time = since_start + retry_wait > max_wait;
            if will_exceed_time {
                break;
            }
        }

        // REMOVED - much tracing-related expanded output

        tokio::time::sleep(retry_wait).await;
    }
    result.take().expect("retries failed to produce a value; max_retries must be >= 1")
}

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.

2 participants