Copper.Retry
0.0.5
dotnet add package Copper.Retry --version 0.0.5
NuGet\Install-Package Copper.Retry -Version 0.0.5
<PackageReference Include="Copper.Retry" Version="0.0.5" />
<PackageVersion Include="Copper.Retry" Version="0.0.5" />
<PackageReference Include="Copper.Retry" />
paket add Copper.Retry --version 0.0.5
#r "nuget: Copper.Retry, 0.0.5"
#:package Copper.Retry@0.0.5
#addin nuget:?package=Copper.Retry&version=0.0.5
#tool nuget:?package=Copper.Retry&version=0.0.5
Copper.Retry
🔁 A small and focused retry library for .NET.
Copper.Retry provides Polly-style retry policies without the additional resilience features you may not need.
Define which exceptions should trigger a retry, configure the delays between attempts, and execute your operation through the policy.
Binaries
| Package | Version | Description |
|---|---|---|
| Copper.Retry | Core retry library |
Quick start
Define a retry policy:
static readonly RetryPolicy RetryPolicy = RetryPolicy
.Handle<IOException>()
.WaitAndRetry([
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(1)
]);
Then execute an operation through the policy:
await RetryPolicy.ExecuteAsync(
async token => await DoSomethingAsync(token),
cancellationToken
);
If DoSomethingAsync throws an IOException, the policy waits for the configured delay and tries again.
If the operation succeeds, execution returns immediately.
If all retries are exhausted, the last exception is propagated to the caller.
Retry policies
A retry policy defines two things:
- What should be retried
- When it should be retried
For example:
static readonly RetryPolicy RetryPolicy = RetryPolicy
.Handle<HttpRequestException>()
.WaitAndRetry([
TimeSpan.FromMilliseconds(100),
TimeSpan.FromMilliseconds(500),
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(5)
]);
The operation is attempted immediately.
If it fails with a handled exception, the first delay is applied before the second attempt:
Attempt 1
↓ failure
wait 100 ms
Attempt 2
↓ failure
wait 500 ms
Attempt 3
↓ failure
wait 1 second
Attempt 4
↓ failure
wait 5 seconds
Attempt 5
The number of configured delays therefore determines the number of retries, not the total number of attempts.
Four delays means up to five attempts.
Retrying forever
Use WaitAndRetryForever to keep retrying until the operation succeeds, waiting for the same delay before each retry:
var policy = RetryPolicy
.Handle<HttpRequestException>()
.WaitAndRetryForever(TimeSpan.FromSeconds(5));
The execution then only ends when the operation succeeds, throws an exception not handled by the policy, or is cancelled – so be sure to pass a CancellationToken to ExecuteAsync to be able to stop it.
Handling exceptions
Use Handle<TException>() to specify which exceptions should trigger a retry:
var policy = RetryPolicy
.Handle<HttpRequestException>()
.WaitAndRetry([
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(5)
]);
Pass a predicate to Handle<TException>() to apply arbitrary logic when checking whether to handle the exception:
var policy = RetryPolicy
.Handle<HttpRequestException>(e => e.StatusCode != 400) //< bad requests stay bad
.WaitAndRetry([
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(5)
]);
Call Handle<TException>() multiple times to add more filters:
var policy = RetryPolicy
.Handle<HttpRequestException>(e => e.StatusCode != 400) //< bad requests stay bad
.Handle<IOException>()
.WaitAndRetry([
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(5)
]);
Exceptions not handled by the policy are propagated immediately without retrying.
Retry callbacks
Use OnRetry to get notified each time an operation is about to be retried, e.g. to log transient errors:
var policy = RetryPolicy
.Handle<HttpRequestException>()
.OnRetry((exception, delay) => logger.LogWarning(exception, "Request failed - retrying in {Delay}", delay))
.WaitAndRetry([
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(5)
]);
The callback receives the exception that caused the retry and the delay that will be waited before the next attempt.
Call OnRetry multiple times to add more callbacks. They are invoked in the order they were added.
Callbacks are only invoked when a retry will actually happen, so they are not invoked for the last exception, which is propagated to the caller.
Executing operations
Execute asynchronous operations using ExecuteAsync:
await policy.ExecuteAsync(await SendRequestAsync(token), cancellationToken);
Operations returning a value can be executed in the same way:
var result = await policy.ExecuteAsync(
async cancellationToken =>
{
return await GetSomethingAsync(cancellationToken);
},
cancellationToken
);
Cancellation
ExecuteAsync accepts a CancellationToken:
await policy.ExecuteAsync(
async cancellationToken =>
{
await DoSomethingAsync(cancellationToken);
},
cancellationToken
);
Cancellation stops execution and prevents further retry attempts.
The cancellation token is also used while waiting between attempts, allowing pending retry delays to be cancelled immediately.
Reusing policies
Retry policies are intended to be created once and reused:
static readonly RetryPolicy ApiRetryPolicy = RetryPolicy
.Handle<HttpRequestException>()
.WaitAndRetry([
TimeSpan.FromMilliseconds(250),
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(5)
]);
The same policy can then be used by multiple operations:
await ApiRetryPolicy.ExecuteAsync(
token => CallApiAsync(token),
cancellationToken
);
Policies are safe to reuse across executions.
Why Copper.Retry?
Libraries such as Polly provide a comprehensive set of resilience strategies, including retries, circuit breakers, timeouts, rate limiting, hedging, and fallback policies.
Copper.Retry deliberately has a much smaller scope.
It is for applications that need retries — and just retries.
The basic model is simple:
Try. If it fails with an exception you care about, wait a while and try again.
License
Copper.Retry is licensed under the MIT License.
| 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 is compatible. 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. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
-
net8.0
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.