use gax::error::Error;
use gax::{
retry_policy::{RetryPolicy, RetryPolicyExt},
retry_result::RetryResult,
};
use std::time::Duration;
pub(crate) fn default() -> impl RetryPolicy {
RecommendedPolicy.with_time_limit(Duration::from_secs(10))
}
#[derive(Clone, Debug)]
pub struct RecommendedPolicy;
impl RetryPolicy for RecommendedPolicy {
fn on_error(
&self,
_loop_start: std::time::Instant,
_attempt_count: u32,
idempotent: bool,
error: Error,
) -> RetryResult {
if error.is_transient_and_before_rpc() {
return RetryResult::Continue(error);
}
if !idempotent {
return RetryResult::Permanent(error);
}
if error.is_io() {
return RetryResult::Continue(error);
}
if let Some(code) = error.http_status_code() {
return match code {
408 | 429 | 500..600 => RetryResult::Continue(error),
_ => RetryResult::Permanent(error),
};
}
RetryResult::Permanent(error)
}
}
#[cfg(test)]
mod tests {
use super::*;
use gax::throttle_result::ThrottleResult;
use http::HeaderMap;
use test_case::test_case;
#[test_case(408)]
#[test_case(429)]
#[test_case(500)]
#[test_case(502)]
#[test_case(503)]
#[test_case(504)]
fn retryable(code: u16) {
let p = RecommendedPolicy;
let now = std::time::Instant::now();
assert!(p.on_error(now, 0, true, http_error(code)).is_continue());
assert!(p.on_error(now, 0, false, http_error(code)).is_permanent());
let t = p.on_throttle(now, 0, http_error(code));
assert!(matches!(t, ThrottleResult::Continue(_)), "{t:?}");
}
#[test_case(401)]
#[test_case(403)]
fn not_recommended(code: u16) {
let p = RecommendedPolicy;
let now = std::time::Instant::now();
assert!(p.on_error(now, 0, true, http_error(code)).is_permanent());
assert!(p.on_error(now, 0, false, http_error(code)).is_permanent());
let t = p.on_throttle(now, 0, http_error(code));
assert!(matches!(t, ThrottleResult::Continue(_)), "{t:?}");
}
fn http_error(code: u16) -> Error {
Error::http(code, HeaderMap::new(), bytes::Bytes::new())
}
}