rocketmq_protocol/protocol/subscription/
exponential_retry_policy.rs1use std::time::Duration;
16
17use serde::Deserialize;
18use serde::Serialize;
19
20use crate::protocol::subscription::retry_policy::RetryPolicy;
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct ExponentialRetryPolicy {
24 initial: u64,
25 max: u64,
26 multiplier: u64,
27}
28
29impl Default for ExponentialRetryPolicy {
30 #[allow(clippy::incompatible_msrv)]
31 fn default() -> Self {
32 ExponentialRetryPolicy {
33 initial: Duration::from_secs(5).as_millis() as u64,
34 max: Duration::from_hours(2).as_millis() as u64,
35 multiplier: 2,
36 }
37 }
38}
39
40impl ExponentialRetryPolicy {
41 pub fn new(initial: u64, max: u64, multiplier: u64) -> Self {
42 ExponentialRetryPolicy {
43 initial,
44 max,
45 multiplier,
46 }
47 }
48
49 pub fn initial(&self) -> u64 {
50 self.initial
51 }
52
53 pub fn max(&self) -> u64 {
54 self.max
55 }
56
57 pub fn multiplier(&self) -> u64 {
58 self.multiplier
59 }
60
61 pub fn set_initial(&mut self, initial: u64) {
62 self.initial = initial;
63 }
64
65 pub fn set_max(&mut self, max: u64) {
66 self.max = max;
67 }
68
69 pub fn set_multiplier(&mut self, multiplier: u64) {
70 self.multiplier = multiplier;
71 }
72}
73
74impl RetryPolicy for ExponentialRetryPolicy {
75 fn next_delay_duration(&self, reconsume_times: i32) -> i64 {
76 let reconsume_times = reconsume_times.clamp(0, 32) as u32;
77 let delay = self
81 .initial
82 .saturating_mul(self.multiplier.saturating_pow(reconsume_times));
83 delay.min(self.max).min(i64::MAX as u64) as i64
84 }
85}
86
87#[cfg(test)]
88mod exponential_retry_policy_tests {
89 use std::time::Duration;
90
91 use super::*;
92
93 #[test]
94 fn configuration_methods_and_serde_preserve_values() {
95 let default = ExponentialRetryPolicy::default();
96 assert_eq!(
97 (default.initial(), default.max(), default.multiplier()),
98 (
99 Duration::from_secs(5).as_millis() as u64,
100 Duration::from_hours(2).as_millis() as u64,
101 2,
102 )
103 );
104
105 let mut policy = ExponentialRetryPolicy::new(1_000, 3_600_000, 3);
106 policy.set_initial(2_000);
107 policy.set_max(7_200_000);
108 policy.set_multiplier(4);
109
110 let json = serde_json::to_string(&policy).unwrap();
111 assert_eq!(json, r#"{"initial":2000,"max":7200000,"multiplier":4}"#);
112
113 let decoded: ExponentialRetryPolicy = serde_json::from_str(&json).unwrap();
114 assert_eq!(
115 (decoded.initial(), decoded.max(), decoded.multiplier()),
116 (2_000, 7_200_000, 4)
117 );
118 }
119
120 #[test]
121 fn next_delay_duration_handles_boundaries() {
122 let policy = ExponentialRetryPolicy::new(1_000, 5_000, 2);
123 for (reconsume_times, expected_delay) in [(-1, 1_000), (0, 1_000), (1, 2_000), (2, 4_000), (3, 5_000)] {
124 assert_eq!(policy.next_delay_duration(reconsume_times), expected_delay);
125 }
126
127 let uncapped_max = ExponentialRetryPolicy::new(1, u64::MAX, 2);
128 assert_eq!(uncapped_max.next_delay_duration(100), 1_i64 << 32);
129 }
130
131 #[test]
132 fn next_delay_duration_saturates_instead_of_overflowing() {
133 let exponentiation_overflow = ExponentialRetryPolicy::new(1_000, 5_000, 10);
135 assert_eq!(exponentiation_overflow.next_delay_duration(32), 5_000);
136
137 let multiplication_overflow = ExponentialRetryPolicy::new(u64::MAX, 7_200_000, 2);
139 assert_eq!(multiplication_overflow.next_delay_duration(1), 7_200_000);
140
141 let zero_initial = ExponentialRetryPolicy::new(0, 5_000, 10);
143 assert_eq!(zero_initial.next_delay_duration(32), 0);
144
145 let above_i64_max = ExponentialRetryPolicy::new(u64::MAX, u64::MAX, u64::MAX);
147 assert_eq!(above_i64_max.next_delay_duration(32), i64::MAX);
148 }
149}