use std::time::Duration;
pub trait RetryPolicy {
fn should_retry(err: &tonic::Status, tries: u8) -> RetryPolicyResult;
}
#[derive(Debug, PartialEq, Clone, Copy, Eq, PartialOrd, Ord, Hash)]
pub enum ServerStatus {
Alive,
Dead,
}
#[derive(Debug, PartialEq, Clone, Copy, Eq, PartialOrd, Ord, Hash)]
pub enum RetryTime {
DoNotRetry,
Immediately,
After(Duration),
}
pub type RetryPolicyResult = (ServerStatus, RetryTime);
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct DefaultRetryPolicy;
impl RetryPolicy for DefaultRetryPolicy {
fn should_retry(err: &tonic::Status, _tries: u8) -> RetryPolicyResult {
if std::error::Error::source(err).is_some() {
(ServerStatus::Dead, RetryTime::Immediately)
} else {
(ServerStatus::Alive, RetryTime::DoNotRetry)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use tonic::Status;
#[test]
fn test_default_retry_policy_alive() {
let err = Status::new(tonic::Code::Unknown, "test error");
let result = DefaultRetryPolicy::should_retry(&err, 1);
assert_eq!(result, (ServerStatus::Alive, RetryTime::DoNotRetry));
}
#[test]
fn test_default_retry_policy_dead() {
#[derive(Debug)]
struct TestError;
impl std::fmt::Display for TestError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Test error")
}
}
impl std::error::Error for TestError {}
let err = Status::from_error(Box::new(TestError));
let result = DefaultRetryPolicy::should_retry(&err, 1);
assert_eq!(result, (ServerStatus::Dead, RetryTime::Immediately));
}
}