Skip to main content

solti_model/domain/policy/
jitter.rs

1//! # Jitter policy
2//!
3//! [`JitterPolicy`] selects how retry delay randomness is applied.
4
5use serde::{Deserialize, Serialize};
6use std::str::FromStr;
7
8use crate::error::{ModelError, ModelResult};
9
10/// Jitter applied to a backoff delay.
11///
12/// | Variant        | Delay range                              |
13/// |----------------|------------------------------------------|
14/// | `None`         | `base`                                   |
15/// | `Full`         | `[0, base]`                              |
16/// | `Equal`        | `[base / 2, base]`                       |
17/// | `Decorrelated` | `[first, min(base * 3, max)]`            |
18///
19/// The execution layer applies the selected policy.
20///
21/// ## Example
22///
23/// ```
24/// use solti_model::JitterPolicy;
25///
26/// assert_eq!("equal".parse::<JitterPolicy>().unwrap(), JitterPolicy::Equal);
27/// assert_eq!("".parse::<JitterPolicy>().unwrap(), JitterPolicy::Full);
28/// ```
29#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
30#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
31#[serde(rename_all = "camelCase")]
32#[non_exhaustive]
33pub enum JitterPolicy {
34    /// Full jitter: delay is uniformly sampled from `[0, base]`.
35    #[default]
36    Full,
37    /// No randomness applied. Backoff durations remain fixed.
38    None,
39    /// Equal jitter samples from `[base / 2, base]`.
40    Equal,
41    /// Memoryless randomized band: delay is sampled uniformly from
42    /// `[first, min(base * 3, max)]`.
43    Decorrelated,
44}
45
46impl FromStr for JitterPolicy {
47    type Err = ModelError;
48    fn from_str(s: &str) -> ModelResult<Self> {
49        let s = s.trim();
50        if s.is_empty() || s.eq_ignore_ascii_case("full") || s.eq_ignore_ascii_case("default") {
51            Ok(JitterPolicy::Full)
52        } else if s.eq_ignore_ascii_case("none") {
53            Ok(JitterPolicy::None)
54        } else if s.eq_ignore_ascii_case("equal") {
55            Ok(JitterPolicy::Equal)
56        } else if s.eq_ignore_ascii_case("decorrelated") {
57            Ok(JitterPolicy::Decorrelated)
58        } else {
59            Err(ModelError::UnknownJitter(s.to_string()))
60        }
61    }
62}