Luger.Async.ExponentialBackoff
1.0.0
dotnet add package Luger.Async.ExponentialBackoff --version 1.0.0
NuGet\Install-Package Luger.Async.ExponentialBackoff -Version 1.0.0
<PackageReference Include="Luger.Async.ExponentialBackoff" Version="1.0.0" />
<PackageVersion Include="Luger.Async.ExponentialBackoff" Version="1.0.0" />
<PackageReference Include="Luger.Async.ExponentialBackoff" />
paket add Luger.Async.ExponentialBackoff --version 1.0.0
#r "nuget: Luger.Async.ExponentialBackoff, 1.0.0"
#addin nuget:?package=Luger.Async.ExponentialBackoff&version=1.0.0
#tool nuget:?package=Luger.Async.ExponentialBackoff&version=1.0.0
Luger.Async.ExponentialBackoff
Utility for configuring and awaiting exponential backoff over asynchronous functions.
That is all it does. If you're looking for a robust tool with this functionality and much more you should have a look at Polly.
Exponential backoff is an algorithm that uses feedback to multiplicatively decrease the rate of some process, in order to gradually find an acceptable rate. <sup>Wikipedia page Exponential Backoff (this version)</sup>
Luger.Async.ExponentialBackoff provides an implementation that retries an asynchronous function (process) after an increasingly longer delay (multiplicatively decrease the rate) if it fails (feedback) in order to mitigate transient failures.
Usage
Wrap an asynchronous function func
in exponential backoff with default
options and await it.
await ExponentialBackoff.Over(func);
The return type will be T
for func
of type Func<Task<T>>
.
The return type will be System.ValueTuple
for func
of type Func<Task>
.
Exponential backoff can be configured before await by calling methods making up its fluent configuration API.
await ExponentialBackoff.Over(func).WithRetries(2);
Configuration
Limit number of retries
Number of retries before giving up and throwing the last encountered exception.
WithRetries(uint retries)
Default limit number of retries is 8.
Base delay
The mean duration of the first delay before retrying.
WithBaseDelay(DelayTimeSpan baseDelay)
Default base delay is 100 ms.
DelayTimeSpan
is a non-negative System.TimeSpan
.
Synchronization context marshalling
Configure exponential backoff to attempt to marshal the delays and retries back to the original context captured.
ConfigureRetryAwait(bool retryOnCapturedContext)
Default is to not attempt to marshal back to the original context captured.
Custom random number generation
Inject a custom random number generator.
WithCustomRNG(RNG rng)
Default random number generator is System.Random.NextDouble
with a fresh
instance of System.Random
shared by all iterations of one single exponential
backoff.
The random number generator is expected to produce values in the range $[0..1)$. Values outside this range will cause an exception to be thrown.
RNG
has the following declaration;
delegate double RNG();
Progress reporting
Inject a progress reporting sink.
WithProgress(IProgress<ExponentialBackoffProgress> progress)
Default is to not report progress.
If you need to have progress reporting performed according to a specific
synchronization context (e.g. for updating WinForm GUI elements on the GUI
thread) it's advisable to use System.Progress<T>
as it uses the
synchronization context captured at construction.
ExponentialBackoffProgress
has the following declaration;
record struct ExponentialBackoffProgress(
uint Retries,
TimeSpan BackoffDelay,
Exception Exception);
If configured, progress is reported before each delay.
Retries
is the number of retries left.
BackoffDelay
is the duration of this delay.
Exception
is the exception caught from the recent await of the asynchronous
function.
Custom delay implementation
Inject a custom delay implementation.
WithCustomDelay(Delay delay)
Default delay implementation is System.Threading.Task.Delay
.
This configuration is only useful for testing purposes when you want deterministic control over execution and therefore want to isolate from environmental concerns like time.
Another possible use of custom delay would be to use spin wait in high
performance scenarios, but in that case it would be better to implement a more
suitable exponential backoff to avoid the overhead of wrapping the function and
delay in Task
. This implementation is better suited for mitigating I/O with
transient failures.
Delay
has the following declaration;
delegate Task Delay(TimeSpan delay, CancellationToken cancellationToken);
Delay scale factor
Set scale factor to multiply mean delay duration with each iteration.
WithFactor(TimeScaleFactor factor)
Default is $2.0$.
TimeScaleFactor
is a finite, non-negative double
.
Cancellation
Set cancellation token with which to cancel exponential backoff.
WithCancellation(CancellationToken cancellationToken)
Default is CancellationToken.None
.
If configured, this cancellation token is used by the delay. It is not automatically monitored by the asynchronous function. If you need the asynchronous function to monitor the same cancellation token you have to configure its cancellation separately.
Details
The exponential scaling of backoff duration affects the mean durations of the delays.
The effective durations of the delays are the mean durations multiplied by a random jitter factor. The jitter factor is exponentially distributed with mean 1.0 . The reason for this is, I don't know, it's more interesting than uniform distribution.
In theory this can result in very long delays. If this becomes a practical problem, or if someone constructively points out how stupid this is, it will be changed.
If you want the jitter factor to be constant, inject a constant random number generator.
If you want the jitter factor to be constant 1.0, inject a constant random number generator producing $1-e^{-1}$.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. |
-
net8.0
- Luger.Functional.Maybe (>= 2.0.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
1.0.0 | 107 | 2 months ago |
1.0.0-beta | 168 | 1/7/2023 |