1use crate::{Backend, Error, SecretString};
2use std::sync::Arc;
3use std::time::Duration;
4use url::Url;
5
6pub struct RetryBackend {
8 inner: Arc<dyn Backend>,
9 max_retries: u32,
10 base_delay: Duration,
11}
12
13impl RetryBackend {
14 pub fn new(inner: Arc<dyn Backend>) -> Self {
16 Self {
17 inner,
18 max_retries: 3,
19 base_delay: Duration::from_millis(100),
20 }
21 }
22
23 pub fn max_retries(mut self, n: u32) -> Self {
25 self.max_retries = n;
26 self
27 }
28
29 pub fn base_delay(mut self, d: Duration) -> Self {
31 self.base_delay = d;
32 self
33 }
34
35 fn retry(
36 &self,
37 mut op: impl FnMut() -> Result<SecretString, Error>,
38 ) -> Result<SecretString, Error> {
39 let mut last_err = None;
40 for attempt in 0..=self.max_retries {
41 match op() {
42 Ok(val) => return Ok(val),
43 Err(e) => {
44 if !e.is_transient() || attempt == self.max_retries {
45 return Err(e);
46 }
47 let delay = Self::backoff(attempt);
48 std::thread::sleep(delay);
49 last_err = Some(e);
50 }
51 }
52 }
53 Err(last_err.unwrap())
54 }
55
56 fn retry_void(&self, mut op: impl FnMut() -> Result<(), Error>) -> Result<(), Error> {
57 let mut last_err = None;
58 for attempt in 0..=self.max_retries {
59 match op() {
60 Ok(()) => return Ok(()),
61 Err(e) => {
62 if !e.is_transient() || attempt == self.max_retries {
63 return Err(e);
64 }
65 let delay = Self::backoff(attempt);
66 std::thread::sleep(delay);
67 last_err = Some(e);
68 }
69 }
70 }
71 Err(last_err.unwrap())
72 }
73
74 fn retry_bool(&self, mut op: impl FnMut() -> Result<bool, Error>) -> Result<bool, Error> {
75 let mut last_err = None;
76 for attempt in 0..=self.max_retries {
77 match op() {
78 Ok(val) => return Ok(val),
79 Err(e) => {
80 if !e.is_transient() || attempt == self.max_retries {
81 return Err(e);
82 }
83 let delay = Self::backoff(attempt);
84 std::thread::sleep(delay);
85 last_err = Some(e);
86 }
87 }
88 }
89 Err(last_err.unwrap())
90 }
91
92 fn retry_vec(
93 &self,
94 mut op: impl FnMut() -> Result<Vec<crate::Entry>, Error>,
95 ) -> Result<Vec<crate::Entry>, Error> {
96 let mut last_err = None;
97 for attempt in 0..=self.max_retries {
98 match op() {
99 Ok(val) => return Ok(val),
100 Err(e) => {
101 if !e.is_transient() || attempt == self.max_retries {
102 return Err(e);
103 }
104 let delay = Self::backoff(attempt);
105 std::thread::sleep(delay);
106 last_err = Some(e);
107 }
108 }
109 }
110 Err(last_err.unwrap())
111 }
112
113 fn backoff(attempt: u32) -> Duration {
118 let base = Duration::from_millis(100);
119 let multiplier = 2_u128.pow(attempt);
120 let exponential = base.as_millis().saturating_mul(multiplier);
121 let jitter = (attempt.wrapping_mul(7) % 50) as u128;
122 Duration::from_millis(u64::try_from(exponential.saturating_add(jitter)).unwrap_or(u64::MAX))
123 }
124}
125
126impl Backend for RetryBackend {
127 fn scheme(&self) -> &'static str {
128 self.inner.scheme()
129 }
130
131 fn get(&self, url: &Url) -> Result<SecretString, Error> {
132 self.retry(|| self.inner.get(url))
133 }
134
135 fn put(&self, url: &Url, value: &SecretString) -> Result<(), Error> {
136 self.retry_void(|| self.inner.put(url, value))
137 }
138
139 fn list(&self, url: &Url) -> Result<Vec<crate::Entry>, Error> {
140 self.retry_vec(|| self.inner.list(url))
141 }
142
143 fn delete(&self, url: &Url) -> Result<(), Error> {
144 self.retry_void(|| self.inner.delete(url))
145 }
146
147 fn exists(&self, url: &Url) -> Result<bool, Error> {
148 self.retry_bool(|| self.inner.exists(url))
149 }
150}