Skip to content

Commit 7749b37

Browse files
refi64sjoerdsimons
authored andcommitted
Make retriable request functions Fn instead of FnMut
At the time I wrote this code I didn't realize that plain `Fn` existed and thought the only two options were `FnOnce` (which I couldn't use since the function is called more than once) and the previously used `FnMut`. This brought the problem that, because the function could mutate state (which we never used outside of the tests), it had to be wrapped in an `Arc<Mutex<_>>`, which was quite ugly. This makes the tests a tad messier since they have to use synchronized operations to keep track of the attempt count now, but it's not too bad and isolated solely to the test code. Signed-off-by: Ryan Gonzalez <[email protected]>
1 parent dc5787d commit 7749b37

File tree

1 file changed

+25
-29
lines changed

1 file changed

+25
-29
lines changed

src/retry.rs

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
use std::{sync::Arc, time::Duration};
1+
use std::time::Duration;
22

33
use backoff::ExponentialBackoff;
44
use futures_util::Future;
55

66
use color_eyre::{eyre::Result, Report};
77
use open_build_service_api as obs;
8-
use tokio::sync::Mutex;
98
use tracing::instrument;
109

1110
const INITIAL_INTERVAL: Duration = Duration::from_millis(300);
@@ -36,36 +35,33 @@ fn is_caused_by_client_error(report: &Report) -> bool {
3635
pub async fn retry_request<T, E, Fut, Func>(func: Func) -> Result<T>
3736
where
3837
Fut: Future<Output = Result<T, E>>,
39-
Func: FnMut() -> Fut,
38+
Func: Fn() -> Fut,
4039
E: Into<Report>,
4140
{
42-
let func = Arc::new(Mutex::new(func));
4341
backoff::future::retry(
4442
ExponentialBackoff {
4543
max_elapsed_time: None,
4644
initial_interval: INITIAL_INTERVAL,
4745
..Default::default()
4846
},
49-
move || {
50-
let func = func.clone();
51-
async move {
52-
let mut func = func.lock().await;
53-
func().await.map_err(|err| {
54-
let report = err.into();
55-
if is_caused_by_client_error(&report) {
56-
backoff::Error::permanent(report)
57-
} else {
58-
backoff::Error::transient(report)
59-
}
60-
})
61-
}
47+
|| async {
48+
func().await.map_err(|err| {
49+
let report = err.into();
50+
if is_caused_by_client_error(&report) {
51+
backoff::Error::permanent(report)
52+
} else {
53+
backoff::Error::transient(report)
54+
}
55+
})
6256
},
6357
)
6458
.await
6559
}
6660

6761
#[cfg(test)]
6862
mod tests {
63+
use std::sync::atomic::{AtomicI32, Ordering};
64+
6965
use claim::*;
7066
use open_build_service_api as obs;
7167
use rstest::*;
@@ -114,18 +110,18 @@ mod tests {
114110
TEST_PASS.to_owned(),
115111
);
116112

117-
let mut attempts = 0;
113+
let attempts = AtomicI32::new(0);
118114
assert_err!(
119115
tokio::time::timeout(
120116
Duration::from_millis(2000),
121117
retry_request(|| {
122-
attempts += 1;
118+
attempts.fetch_add(1, Ordering::SeqCst);
123119
async { client.project("500".to_owned()).meta().await }
124120
})
125121
)
126122
.await
127123
);
128-
assert_gt!(attempts, 1);
124+
assert_gt!(attempts.load(Ordering::SeqCst), 1);
129125
}
130126

131127
#[rstest]
@@ -138,12 +134,12 @@ mod tests {
138134
TEST_PASS.to_owned(),
139135
);
140136

141-
let mut attempts = 0;
137+
let attempts = AtomicI32::new(0);
142138
assert_err!(
143139
tokio::time::timeout(
144140
Duration::from_millis(2000),
145141
retry_request(|| {
146-
attempts += 1;
142+
attempts.fetch_add(1, Ordering::SeqCst);
147143
async {
148144
client
149145
.project("500".to_owned())
@@ -155,7 +151,7 @@ mod tests {
155151
)
156152
.await
157153
);
158-
assert_gt!(attempts, 1);
154+
assert_gt!(attempts.load(Ordering::SeqCst), 1);
159155
}
160156

161157
#[rstest]
@@ -168,15 +164,15 @@ mod tests {
168164
TEST_PASS.to_owned(),
169165
);
170166

171-
let mut attempts = 0;
167+
let attempts = AtomicI32::new(0);
172168
assert_err!(
173169
retry_request(|| {
174-
attempts += 1;
170+
attempts.fetch_add(1, Ordering::SeqCst);
175171
async { client.project("403".to_owned()).meta().await }
176172
})
177173
.await
178174
);
179-
assert_eq!(attempts, 1);
175+
assert_eq!(attempts.load(Ordering::SeqCst), 1);
180176
}
181177

182178
#[rstest]
@@ -189,10 +185,10 @@ mod tests {
189185
TEST_PASS.to_owned(),
190186
);
191187

192-
let mut attempts = 0;
188+
let attempts = AtomicI32::new(0);
193189
assert_err!(
194190
retry_request(|| {
195-
attempts += 1;
191+
attempts.fetch_add(1, Ordering::SeqCst);
196192
async {
197193
client
198194
.project("403".to_owned())
@@ -203,6 +199,6 @@ mod tests {
203199
})
204200
.await
205201
);
206-
assert_eq!(attempts, 1);
202+
assert_eq!(attempts.load(Ordering::SeqCst), 1);
207203
}
208204
}

0 commit comments

Comments
 (0)