Skip to content

Commit 79134a5

Browse files
committed
refactor: move next backoff delay function to retry.rs
1 parent 4a65db1 commit 79134a5

2 files changed

Lines changed: 37 additions & 35 deletions

File tree

aggregation_mode/db/src/orchestrator.rs

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::{
99

1010
use sqlx::{postgres::PgPoolOptions, Pool, Postgres};
1111

12-
use crate::retry::{RetryConfig, RetryError};
12+
use crate::retry::{next_backoff_delay, RetryConfig, RetryError};
1313

1414
#[derive(Debug, Clone, Copy)]
1515
enum Operation {
@@ -127,46 +127,13 @@ impl DbOrchestrator {
127127

128128
tracing::warn!(attempt = attempts, delay_millis = delay.as_millis(), error = ?err, "retrying after backoff");
129129
tokio::time::sleep(delay).await;
130-
delay = self.next_backoff_delay(delay);
130+
delay = next_backoff_delay(delay, self.retry_config.clone());
131131
attempts += 1;
132132
}
133133
}
134134
}
135135
}
136136

137-
// Exponential backoff with a hard cap.
138-
//
139-
// Each retry multiplies the previous delay by `retry_config.factor`,
140-
// then clamps it to `max_delay_seconds`. This yields:
141-
//
142-
// d_{n+1} = min(max, d_n * factor) => d_n = min(max, d_initial * factor^n)
143-
//
144-
// Example starting at 500ms with factor = 2.0 (no jitter):
145-
// retry 0: 0.5s
146-
// retry 1: 1.0s
147-
// retry 2: 2.0s
148-
// retry 3: 4.0s
149-
// retry 4: 8.0s
150-
// ...
151-
// until the delay reaches `max_delay_seconds`, after which it stays at that max.
152-
// see reference: https://en.wikipedia.org/wiki/Exponential_backoff
153-
// and here: https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/retry-backoff.html
154-
fn next_backoff_delay(&self, current_delay: Duration) -> Duration {
155-
let max: Duration = Duration::from_secs(self.retry_config.max_delay_seconds);
156-
// Defensive: factor should be >= 1.0 for backoff, we clamp it to avoid shrinking/NaN.
157-
let factor = f64::from(self.retry_config.factor).max(1.0);
158-
159-
let scaled_secs = current_delay.as_secs_f64() * factor;
160-
let scaled_secs = if scaled_secs.is_finite() {
161-
scaled_secs
162-
} else {
163-
max.as_secs_f64()
164-
};
165-
166-
let scaled = Duration::from_secs_f64(scaled_secs);
167-
scaled.max(max)
168-
}
169-
170137
async fn execute_once<T, Q, Fut>(
171138
&self,
172139
query_fn: &Q,

aggregation_mode/db/src/retry.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::time::Duration;
2+
13
#[derive(Debug)]
24
pub(super) enum RetryError<E> {
35
Transient(E),
@@ -26,3 +28,36 @@ pub struct RetryConfig {
2628
/// * `max_delay_seconds` - Maximum delay between retry attempts (in seconds)
2729
pub max_delay_seconds: u64,
2830
}
31+
32+
// Exponential backoff with a hard cap.
33+
//
34+
// Each retry multiplies the previous delay by `retry_config.factor`,
35+
// then clamps it to `max_delay_seconds`. This yields:
36+
//
37+
// d_{n+1} = min(max, d_n * factor) => d_n = min(max, d_initial * factor^n)
38+
//
39+
// Example starting at 500ms with factor = 2.0 (no jitter):
40+
// retry 0: 0.5s
41+
// retry 1: 1.0s
42+
// retry 2: 2.0s
43+
// retry 3: 4.0s
44+
// retry 4: 8.0s
45+
// ...
46+
// until the delay reaches `max_delay_seconds`, after which it stays at that max.
47+
// see reference: https://en.wikipedia.org/wiki/Exponential_backoff
48+
// and here: https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/retry-backoff.html
49+
pub fn next_backoff_delay(current_delay: Duration, retry_config: RetryConfig) -> Duration {
50+
let max: Duration = Duration::from_secs(retry_config.max_delay_seconds);
51+
// Defensive: factor should be >= 1.0 for backoff, we clamp it to avoid shrinking/NaN.
52+
let factor = f64::from(retry_config.factor).max(1.0);
53+
54+
let scaled_secs = current_delay.as_secs_f64() * factor;
55+
let scaled_secs = if scaled_secs.is_finite() {
56+
scaled_secs
57+
} else {
58+
max.as_secs_f64()
59+
};
60+
61+
let scaled = Duration::from_secs_f64(scaled_secs);
62+
scaled.max(max)
63+
}

0 commit comments

Comments
 (0)