use jiff::{Span, ToSpan};
#[derive(Debug, Clone, Copy, PartialEq, sqlx::Type)]
#[sqlx(type_name = "underway.task_retry_policy")]
pub struct RetryPolicy {
pub(crate) max_attempts: i32,
pub(crate) initial_interval_ms: i32,
pub(crate) max_interval_ms: i32,
pub(crate) backoff_coefficient: f64,
}
pub(crate) type RetryCount = i32;
impl RetryPolicy {
pub fn builder() -> Builder {
Builder::default()
}
pub(crate) fn calculate_delay(&self, retry_count: RetryCount) -> Span {
let base_delay = self.initial_interval_ms as f64;
let backoff_delay = base_delay * self.backoff_coefficient.powi(retry_count - 1);
let delay = backoff_delay.min(self.max_interval_ms as f64) as i64;
delay.milliseconds()
}
}
const DEFAULT_RETRY_POLICY: RetryPolicy = RetryPolicy {
max_attempts: 5,
initial_interval_ms: 1_000,
max_interval_ms: 60_000,
backoff_coefficient: 2.0,
};
impl Default for RetryPolicy {
fn default() -> Self {
DEFAULT_RETRY_POLICY
}
}
#[derive(Debug, Default)]
pub struct Builder {
inner: RetryPolicy,
}
impl Builder {
pub const fn new() -> Self {
Self {
inner: DEFAULT_RETRY_POLICY,
}
}
pub const fn max_attempts(mut self, max_attempts: i32) -> Self {
self.inner.max_attempts = max_attempts;
self
}
pub const fn initial_interval_ms(mut self, initial_interval_ms: i32) -> Self {
self.inner.initial_interval_ms = initial_interval_ms;
self
}
pub const fn max_interval_ms(mut self, max_interval_ms: i32) -> Self {
self.inner.max_interval_ms = max_interval_ms;
self
}
pub const fn backoff_coefficient(mut self, backoff_coefficient: f64) -> Self {
self.inner.backoff_coefficient = backoff_coefficient;
self
}
pub const fn build(self) -> RetryPolicy {
self.inner
}
}