use std::time::Duration;
pub const DEFAULT_SPAWN_WAIT_HARD_CEILING: Duration = Duration::from_secs(60);
pub const SPAWN_WAIT_BACKOFF_SEQUENCE: [Duration; 6] = [
Duration::from_millis(50),
Duration::from_millis(100),
Duration::from_millis(200),
Duration::from_millis(500),
Duration::from_secs(1),
Duration::from_secs(2),
];
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SpawnWaitPolicy {
hard_ceiling: Duration,
}
impl SpawnWaitPolicy {
pub fn new() -> Self {
Self::with_hard_ceiling(DEFAULT_SPAWN_WAIT_HARD_CEILING)
}
pub fn with_hard_ceiling(hard_ceiling: Duration) -> Self {
Self { hard_ceiling }
}
pub fn hard_ceiling(&self) -> Duration {
self.hard_ceiling
}
pub fn backoff_for_attempt(&self, attempt: usize) -> Duration {
let capped_index = attempt.min(SPAWN_WAIT_BACKOFF_SEQUENCE.len() - 1);
SPAWN_WAIT_BACKOFF_SEQUENCE[capped_index]
}
pub fn decide(&self, probe: SpawnWaitProbe) -> SpawnWaitDecision {
if probe.endpoint_ready {
return SpawnWaitDecision::EndpointReady;
}
if !probe.daemon_alive {
return SpawnWaitDecision::DaemonExitedBeforeReady;
}
if probe.elapsed >= self.hard_ceiling {
return SpawnWaitDecision::Timeout {
hard_ceiling: self.hard_ceiling,
};
}
SpawnWaitDecision::Sleep {
duration: self
.backoff_for_attempt(probe.attempt)
.min(self.hard_ceiling - probe.elapsed),
}
}
}
impl Default for SpawnWaitPolicy {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SpawnWaitProbe {
pub elapsed: Duration,
pub daemon_alive: bool,
pub endpoint_ready: bool,
pub attempt: usize,
}
impl SpawnWaitProbe {
pub fn new(
elapsed: Duration,
daemon_alive: bool,
endpoint_ready: bool,
attempt: usize,
) -> Self {
Self {
elapsed,
daemon_alive,
endpoint_ready,
attempt,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SpawnWaitDecision {
EndpointReady,
DaemonExitedBeforeReady,
Timeout {
hard_ceiling: Duration,
},
Sleep {
duration: Duration,
},
}