pyth_lazer_client/
backoff.rs

1//! Exponential backoff implementation for Pyth Lazer client.
2//!
3//! This module provides a wrapper around the [`backoff`] crate's exponential backoff functionality,
4//! offering a simplified interface tailored for Pyth Lazer client operations.
5
6use std::time::Duration;
7
8use backoff::{
9    default::{INITIAL_INTERVAL_MILLIS, MAX_INTERVAL_MILLIS, MULTIPLIER, RANDOMIZATION_FACTOR},
10    ExponentialBackoff, ExponentialBackoffBuilder,
11};
12
13/// A wrapper around the backoff crate's exponential backoff configuration.
14///
15/// This struct encapsulates the parameters needed to configure exponential backoff
16/// behavior and can be converted into the backoff crate's [`ExponentialBackoff`] type.
17#[derive(Debug)]
18pub struct PythLazerExponentialBackoff {
19    /// The initial retry interval.
20    initial_interval: Duration,
21    /// The randomization factor to use for creating a range around the retry interval.
22    ///
23    /// A randomization factor of 0.5 results in a random period ranging between 50% below and 50%
24    /// above the retry interval.
25    randomization_factor: f64,
26    /// The value to multiply the current interval with for each retry attempt.
27    multiplier: f64,
28    /// The maximum value of the back off period. Once the retry interval reaches this
29    /// value it stops increasing.
30    max_interval: Duration,
31}
32
33impl From<PythLazerExponentialBackoff> for ExponentialBackoff {
34    fn from(val: PythLazerExponentialBackoff) -> Self {
35        ExponentialBackoffBuilder::default()
36            .with_initial_interval(val.initial_interval)
37            .with_randomization_factor(val.randomization_factor)
38            .with_multiplier(val.multiplier)
39            .with_max_interval(val.max_interval)
40            .with_max_elapsed_time(None)
41            .build()
42    }
43}
44
45/// Builder for [`PythLazerExponentialBackoff`].
46///
47/// Provides a fluent interface for configuring exponential backoff parameters
48/// with sensible defaults from the backoff crate.
49#[derive(Debug)]
50pub struct PythLazerExponentialBackoffBuilder {
51    initial_interval: Duration,
52    randomization_factor: f64,
53    multiplier: f64,
54    max_interval: Duration,
55}
56
57impl Default for PythLazerExponentialBackoffBuilder {
58    fn default() -> Self {
59        Self {
60            initial_interval: Duration::from_millis(INITIAL_INTERVAL_MILLIS),
61            randomization_factor: RANDOMIZATION_FACTOR,
62            multiplier: MULTIPLIER,
63            max_interval: Duration::from_millis(MAX_INTERVAL_MILLIS),
64        }
65    }
66}
67
68impl PythLazerExponentialBackoffBuilder {
69    /// Creates a new builder with default values.
70    pub fn new() -> Self {
71        Default::default()
72    }
73
74    /// Sets the initial retry interval.
75    ///
76    /// This is the starting interval for the first retry attempt.
77    pub fn with_initial_interval(&mut self, initial_interval: Duration) -> &mut Self {
78        self.initial_interval = initial_interval;
79        self
80    }
81
82    /// Sets the randomization factor to use for creating a range around the retry interval.
83    ///
84    /// A randomization factor of 0.5 results in a random period ranging between 50% below and 50%
85    /// above the retry interval. This helps avoid the "thundering herd" problem when multiple
86    /// clients retry at the same time.
87    pub fn with_randomization_factor(&mut self, randomization_factor: f64) -> &mut Self {
88        self.randomization_factor = randomization_factor;
89        self
90    }
91
92    /// Sets the value to multiply the current interval with for each retry attempt.
93    ///
94    /// A multiplier of 2.0 means each retry interval will be double the previous one.
95    pub fn with_multiplier(&mut self, multiplier: f64) -> &mut Self {
96        self.multiplier = multiplier;
97        self
98    }
99
100    /// Sets the maximum value of the back off period.
101    ///
102    /// Once the retry interval reaches this value it stops increasing, providing
103    /// an upper bound on the wait time between retries.
104    pub fn with_max_interval(&mut self, max_interval: Duration) -> &mut Self {
105        self.max_interval = max_interval;
106        self
107    }
108
109    /// Builds the [`PythLazerExponentialBackoff`] configuration.
110    pub fn build(&self) -> PythLazerExponentialBackoff {
111        PythLazerExponentialBackoff {
112            initial_interval: self.initial_interval,
113            randomization_factor: self.randomization_factor,
114            multiplier: self.multiplier,
115            max_interval: self.max_interval,
116        }
117    }
118}