use crate::error::types::TaskFailureKind;
use crate::policy::backoff::BackoffPolicy;
use serde::{Deserialize, Serialize};
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RestartPolicy {
Permanent,
Transient,
Temporary,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PolicyFailureKind {
Recoverable,
FatalConfig,
FatalBug,
ExternalDependency,
Timeout,
Panic,
Cancelled,
Unhealthy,
}
impl From<TaskFailureKind> for PolicyFailureKind {
fn from(value: TaskFailureKind) -> Self {
match value {
TaskFailureKind::Error => Self::Recoverable,
TaskFailureKind::Panic => Self::Panic,
TaskFailureKind::Timeout => Self::Timeout,
TaskFailureKind::Unhealthy => Self::Unhealthy,
TaskFailureKind::Cancelled => Self::Cancelled,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum TaskExit {
Succeeded,
Failed {
kind: PolicyFailureKind,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RestartDecision {
DoNotRestart,
RestartAfter {
delay: Duration,
},
Quarantine,
EscalateToParent,
ShutdownTree,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct PolicyEngine;
impl PolicyEngine {
pub fn new() -> Self {
Self
}
pub fn decide(
&self,
policy: RestartPolicy,
exit: TaskExit,
attempt: u64,
backoff: &BackoffPolicy,
) -> RestartDecision {
match exit {
TaskExit::Succeeded => self.decide_success(policy, attempt, backoff),
TaskExit::Failed { kind } => self.decide_failure(policy, kind, attempt, backoff),
}
}
fn decide_success(
&self,
policy: RestartPolicy,
attempt: u64,
backoff: &BackoffPolicy,
) -> RestartDecision {
match policy {
RestartPolicy::Permanent => RestartDecision::RestartAfter {
delay: backoff.delay_for_attempt(attempt),
},
RestartPolicy::Transient | RestartPolicy::Temporary => RestartDecision::DoNotRestart,
}
}
fn decide_failure(
&self,
policy: RestartPolicy,
kind: PolicyFailureKind,
attempt: u64,
backoff: &BackoffPolicy,
) -> RestartDecision {
match kind {
PolicyFailureKind::FatalConfig => RestartDecision::ShutdownTree,
PolicyFailureKind::FatalBug => RestartDecision::EscalateToParent,
PolicyFailureKind::Cancelled => RestartDecision::DoNotRestart,
_ => self.restartable_failure(policy, attempt, backoff),
}
}
fn restartable_failure(
&self,
policy: RestartPolicy,
attempt: u64,
backoff: &BackoffPolicy,
) -> RestartDecision {
match policy {
RestartPolicy::Permanent | RestartPolicy::Transient => RestartDecision::RestartAfter {
delay: backoff.delay_for_attempt(attempt),
},
RestartPolicy::Temporary => RestartDecision::DoNotRestart,
}
}
}