#[non_exhaustive]pub enum RetryTime {
Immediate,
AfterWaiting,
After(Duration),
At(Instant),
Never,
}
Expand description
A description of when an operation may be retried.
§Retry times values are contextual.
Note that retrying is necessarily contextual, depending on what exactly we’re talking about retrying.
For an example of how context matters: suppose that we try to build a circuit, and encounter a failure extending to the second hop. If we try to build a circuit through the same path immediately, it’s likely to fail again. But if we try to build a circuit through a different path, then there’s no reason to expect that same kind of error.
Thus, the same inner error condition (“failed to extend to the nth hop”) can indicate either a “Retry after waiting for a while” or “Retry immediately.”
§Retry times depend on what we think might change.
Whether retrying will help depends on what we think is likely to change in the near term.
For example, we generally assume an unreachable relay has some likelihood of becoming reachable in the near future, and therefore connecting to such a relay is worth retrying.
On the other hand, we don’t assume that the network is changing wildly over time. Thus, if there is currently no relay that supports delivering traffic to port 23 (telnet), we say that building a request for such a relay is not retriable, even though technically such a relay might appear in the next consensus.
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
Immediate
The operation can be retried immediately, and no delay is needed.
The recipient of this RetryTime
variant may retry the operation
immediately without waiting.
This case should be used cautiously: it risks making code retry in a loop without delay. It should only be used for error conditions that are necessarily produced via a process that itself introduces a delay. (For example, this case is suitable for errors caused by a remote timeout.)
AfterWaiting
The operation can be retried after a short delay, to prevent overloading the network.
The recipient of this RetryTime
variant should delay a short amount of
time before retrying. The amount of time to delay should be randomized,
and should tend to grow larger the more failures there have been
recently for the given operation. (The RetryDelay
type from
tor-basic-utils
is suitable for managing this calculation.)
This case should be used for problems that tend to be “self correcting”, such as remote server failures (the server might come back up).
After(Duration)
The operation can be retried after a particular delay.
The recipient of this RetryTime
variant should wait for at least the
given duration before retrying the operation.
This case should only be used if there is some reason not to return
AfterWaiting
: for example, if the implementor is providing their own
back-off algorithm instead of using RetryDelay.
(This is a separate variant from At
, since the constructor may not
have convenient access to (a mocked view of) the current time. If you
know that the current time is now
, then After(d)
is equivalent to
At(now + d)
.)
At(Instant)
The operation can be retried at some particular time in the future.
The recipient of this this RetryTime
variant should wait until the
current time (as returned by Instant::now
or SleepProvider::now
as
appropriate) is at least this given instant.
This case is appropriate for when we have a failure condition caused by waiting for multiple other timeouts. (For example, if we believe that all our guards are down, then we won’t be able to try getting a guard until the next time guard is scheduled to be marked as retriable.)
Never
Retrying is unlikely to make this operation succeed, unless something else is fixed first.
The recipient of this RetryTime
variant should generally give up, and
stop retrying the given operation.
We don’t mean “literally” that the operation will never succeed: only that retrying it in the near future without fixing the underlying cause is unlikely to help.
This case is appropriate for issues like misconfiguration, internal errors, and requests for operations that the network doesn’t support.
This case is also appropriate for a problem that is “technically” retriable, but where any resolution is likelier to take days or weeks instead of minutes or hours.
Implementations§
Source§impl RetryTime
impl RetryTime
Sourcepub fn absolute<F>(self, now: Instant, choose_delay: F) -> AbsRetryTime
pub fn absolute<F>(self, now: Instant, choose_delay: F) -> AbsRetryTime
Convert this RetryTime
in to an absolute time.
Requires that now
is the current time, and choose_delay
is a
function to choose a delay for RetryTime::AfterWaiting
.
Sourcepub fn earliest_absolute<I, F>(
items: I,
now: Instant,
choose_delay: F,
) -> Option<AbsRetryTime>
pub fn earliest_absolute<I, F>( items: I, now: Instant, choose_delay: F, ) -> Option<AbsRetryTime>
Convert all the provided items
into AbsRetryTime
values, and
return the earliest one.
Requires that now
is the current time, and choose_delay
is a
function to choose a delay for RetryTime::AfterWaiting
.
Differs from items.map(AbsRetryTime::absolute(now, choose_delay)).min()
in that it calls choose_delay
at most once.
Sourcepub fn earliest_approx<I>(items: I) -> Option<RetryTime>
pub fn earliest_approx<I>(items: I) -> Option<RetryTime>
Return the “approximately earliest” item for an iterator of retry times.
This is necessarily an approximation, since we can’t be sure what time
will be chosen if the retry is supposed to happen at a random time, and
therefore cannot tell whether AfterWaiting
comes before or after
particular At
and After
instances.
If you need an exact answer, use earliest_absolute.
Sourcepub fn loose_cmp(&self, other: &Self) -> Ordering
pub fn loose_cmp(&self, other: &Self) -> Ordering
A loose-but-total comparison operator, suitable for choosing a retry time when multiple attempts have failed.
If you need an absolute comparison operator, convert to AbsRetryTime
first.
See also:
LooseCmpRetryTime
, a wrapper for RetryTime
that uses this comparison.