Skip to content

Commit c1130f6

Browse files
committed
Add some more docs to the retry module.
1 parent 4ea6bcb commit c1130f6

File tree

1 file changed

+61
-1
lines changed

1 file changed

+61
-1
lines changed

Diff for: src/cargo/util/network/retry.rs

+61-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,45 @@
11
//! Utilities for retrying a network operation.
2+
//!
3+
//! Some network errors are considered "spurious", meaning it is not a real
4+
//! error (such as a 404 not found) and is likely a transient error (like a
5+
//! bad network connection) that we can hope will resolve itself shortly. The
6+
//! [`Retry`] type offers a way to repeatedly perform some kind of network
7+
//! operation with a delay if it detects one of these possibly transient
8+
//! errors.
9+
//!
10+
//! This supports errors from [`git2`], [`gix`], [`curl`], and
11+
//! [`HttpNotSuccessful`] 5xx HTTP errors.
12+
//!
13+
//! The number of retries can be configured by the user via the `net.retry`
14+
//! config option. This indicates the number of times to retry the operation
15+
//! (default 3 times for a total of 4 attempts).
16+
//!
17+
//! There are hard-coded constants that indicate how long to sleep between
18+
//! retries. The constants are tuned to balance a few factors, such as the
19+
//! responsiveness to the user (we don't want cargo to hang for too long
20+
//! retrying things), and accommodating things like Cloudfront's default
21+
//! negative TTL of 10 seconds (if Cloudfront gets a 5xx error for whatever
22+
//! reason it won't try to fetch again for 10 seconds).
23+
//!
24+
//! The timeout also implements a primitive form of random jitter. This is so
25+
//! that if multiple requests fail at the same time that they don't all flood
26+
//! the server at the same time when they are retried. This jitter still has
27+
//! some clumping behavior, but should be good enough.
28+
//!
29+
//! [`Retry`] is the core type for implementing retry logic. The
30+
//! [`Retry::try`] method can be called with a callback, and it will
31+
//! indicate if it needs to be called again sometime in the future if there
32+
//! was a possibly transient error. The caller is responsible for sleeping the
33+
//! appropriate amount of time and then calling [`Retry::try`] again.
34+
//!
35+
//! [`with_retry`] is a convenience function that will create a [`Retry`] and
36+
//! handle repeatedly running a callback until it succeeds, or it runs out of
37+
//! retries.
38+
//!
39+
//! Some interesting resources about retries:
40+
//! - <https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/>
41+
//! - <https://en.wikipedia.org/wiki/Exponential_backoff>
42+
//! - <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After>
243
344
use crate::util::errors::HttpNotSuccessful;
445
use crate::{CargoResult, Config};
@@ -7,15 +48,32 @@ use rand::Rng;
748
use std::cmp::min;
849
use std::time::Duration;
950

51+
/// State for managing retrying a network operation.
1052
pub struct Retry<'a> {
1153
config: &'a Config,
54+
/// The number of failed attempts that have been done so far.
55+
///
56+
/// Starts at 0, and increases by one each time an attempt fails.
1257
retries: u64,
58+
/// The maximum number of times the operation should be retried.
59+
///
60+
/// 0 means it should never retry.
1361
max_retries: u64,
1462
}
1563

64+
/// The result of attempting some operation via [`Retry::try`].
1665
pub enum RetryResult<T> {
66+
/// The operation was successful.
67+
///
68+
/// The wrapped value is the return value of the callback function.
1769
Success(T),
70+
/// The operation was an error, and it should not be tried again.
1871
Err(anyhow::Error),
72+
/// The operation failed, and should be tried again in the future.
73+
///
74+
/// The wrapped value is the number of milliseconds to wait before trying
75+
/// again. The caller is responsible for waiting this long and then
76+
/// calling [`Retry::try`] again.
1977
Retry(u64),
2078
}
2179

@@ -40,7 +98,9 @@ impl<'a> Retry<'a> {
4098
})
4199
}
42100

43-
/// Returns `Ok(None)` for operations that should be re-tried.
101+
/// Calls the given callback, and returns a [`RetryResult`] which
102+
/// indicates whether or not this needs to be called again at some point
103+
/// in the future to retry the operation if it failed.
44104
pub fn r#try<T>(&mut self, f: impl FnOnce() -> CargoResult<T>) -> RetryResult<T> {
45105
match f() {
46106
Err(ref e) if maybe_spurious(e) && self.retries < self.max_retries => {

0 commit comments

Comments
 (0)