use crate::error_codes::{BACKEND_NETWORK, BACKEND_SERVER, BACKEND_TIMEOUT};
pub const MAX_STREAM_ATTEMPTS: u32 = 3;
pub const STREAM_RETRY_BACKOFF_MS: u32 = 300;
pub fn is_transient(code: u16) -> bool {
matches!(code, BACKEND_NETWORK | BACKEND_SERVER | BACKEND_TIMEOUT)
}
pub fn should_retry(code: u16, attempt: u32) -> bool {
is_transient(code) && attempt < MAX_STREAM_ATTEMPTS
}
pub fn backoff_ms(attempt: u32) -> u32 {
STREAM_RETRY_BACKOFF_MS * attempt
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error_codes::{BACKEND_AUTH, BACKEND_CREDITS, BACKEND_RATE_LIMIT};
#[test]
fn only_transient_classes_retry() {
for c in [BACKEND_NETWORK, BACKEND_SERVER, BACKEND_TIMEOUT] {
assert!(is_transient(c), "code {c} should be transient");
}
for c in [BACKEND_AUTH, BACKEND_CREDITS, BACKEND_RATE_LIMIT, 0] {
assert!(!is_transient(c), "code {c} must NOT retry");
}
}
#[test]
fn should_retry_stops_at_the_attempt_cap() {
assert!(should_retry(BACKEND_SERVER, 1));
assert!(should_retry(BACKEND_SERVER, MAX_STREAM_ATTEMPTS - 1));
assert!(!should_retry(BACKEND_SERVER, MAX_STREAM_ATTEMPTS)); assert!(!should_retry(BACKEND_RATE_LIMIT, 1)); assert_eq!(backoff_ms(2), STREAM_RETRY_BACKOFF_MS * 2);
}
}