|
18 | 18 | use std::fmt::Debug; |
19 | 19 | use std::sync::Arc; |
20 | 20 |
|
| 21 | +use backon::ExponentialBuilder; |
| 22 | +use backon::Retryable; |
21 | 23 | use bytes::Buf; |
22 | 24 | use bytes::Bytes; |
23 | 25 | use http::Request; |
@@ -237,43 +239,30 @@ impl HfCore { |
237 | 239 | self.repo.uri(&self.root, path) |
238 | 240 | } |
239 | 241 |
|
240 | | - /// Exponential backoff: 200ms, 400ms, 800ms, … capped at ~6s. |
241 | | - async fn backoff(attempt: usize) { |
242 | | - let millis = 200u64 * (1u64 << attempt.min(5)); |
243 | | - tokio::time::sleep(std::time::Duration::from_millis(millis)).await; |
244 | | - } |
245 | | - |
246 | 242 | /// Send a request with retries, returning the successful response. |
247 | 243 | /// |
248 | 244 | /// Retries on commit conflicts (HTTP 412) and transient server errors |
249 | 245 | /// (HTTP 5xx) up to `self.max_retries` attempts with exponential backoff. |
250 | 246 | pub(super) async fn send(&self, req: Request<Buffer>) -> Result<Response<Buffer>> { |
| 247 | + let backoff = ExponentialBuilder::default() |
| 248 | + .with_min_delay(std::time::Duration::from_millis(200)) |
| 249 | + .with_max_delay(std::time::Duration::from_millis(6400)) |
| 250 | + .with_max_times(self.max_retries.saturating_sub(1)); |
251 | 251 | let client = self.info.http_client(); |
252 | | - let mut attempt = 0; |
253 | | - loop { |
254 | | - match client.send(req.clone()).await { |
255 | | - Ok(resp) if resp.status().is_success() => { |
256 | | - return Ok(resp); |
257 | | - } |
258 | | - Ok(resp) => { |
259 | | - attempt += 1; |
260 | | - let err = parse_error(resp); |
261 | | - let retryable = |
262 | | - err.kind() == ErrorKind::ConditionNotMatch || err.is_temporary(); |
263 | | - if attempt >= self.max_retries || !retryable { |
264 | | - return Err(err); |
265 | | - } |
266 | | - Self::backoff(attempt).await; |
267 | | - } |
268 | | - Err(err) => { |
269 | | - attempt += 1; |
270 | | - if attempt >= self.max_retries || !err.is_temporary() { |
271 | | - return Err(err); |
272 | | - } |
273 | | - Self::backoff(attempt).await; |
274 | | - } |
| 252 | + |
| 253 | + let send_once = || async { |
| 254 | + let resp = client.send(req.clone()).await?; |
| 255 | + if resp.status().is_success() { |
| 256 | + Ok(resp) |
| 257 | + } else { |
| 258 | + Err(parse_error(resp)) |
275 | 259 | } |
276 | | - } |
| 260 | + }; |
| 261 | + |
| 262 | + send_once |
| 263 | + .retry(backoff) |
| 264 | + .when(|e: &Error| e.kind() == ErrorKind::ConditionNotMatch || e.is_temporary()) |
| 265 | + .await |
277 | 266 | } |
278 | 267 |
|
279 | 268 | /// Send a request, check for success, and deserialize the JSON response. |
|
0 commit comments