Retries and Resilience Shields
When creating modules, you can configure retries per module using the Configure(ModuleConfigurationBuilder) method.
The standard API supports exponential backoff, jitter, and exception filtering without exposing
the underlying resilience library.
Using ModuleConfiguration
Simple Retries
The easiest way to add retries is with WithRetry():
public class MyModule : Module<CommandResult>
{
protected override void Configure(ModuleConfigurationBuilder module) => module
.WithRetry(3); // Retry up to 3 times with exponential backoff and jitter
protected override async Task<CommandResult> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken)
{
// Do something that might fail transiently
}
}
The default base delay is 100 milliseconds. Each retry uses equal jitter between half and all of its exponential-backoff ceiling. You can set a different base delay and limit retries to selected exceptions:
protected override void Configure(ModuleConfigurationBuilder module) => module
.WithRetry(
count: 5,
baseDelay: TimeSpan.FromSeconds(1),
shouldRetry: exception => exception is HttpRequestException);
Custom Resilience Shield
For resilience features outside the standard retry API, configure a Kevlar Shield directly:
public class MyModule : Module<CommandResult>
{
protected override void Configure(ModuleConfigurationBuilder module) => module
.WithShield(
Shield.When<HttpRequestException>()
.Retry(5, Backoff.Custom(i => TimeSpan.FromSeconds(i * i))));
protected override async Task<CommandResult> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken)
{
// Do something
}
}
Context-Aware Resilience Shield
If you need access to the pipeline context when building your shield:
public class MyModule : Module<CommandResult>
{
protected override void Configure(ModuleConfigurationBuilder module) => module
.WithShield(ctx =>
{
var retryCount = ctx.Environment.IsCI ? 5 : 2;
return Shield.When<Exception>()
.Retry(retryCount, Backoff.Custom(i => TimeSpan.FromSeconds(i)));
});
}
Combining with Other Behaviors
Retry configuration can be combined with other module behaviors:
public class ResilientModule : Module<CommandResult>
{
protected override void Configure(ModuleConfigurationBuilder module) => module
.WithRetry(3)
.WithTimeout(TimeSpan.FromMinutes(10))
.WithIgnoreFailures(); // Don't fail the pipeline even after all retries
}
WithTimeout applies to each execution attempt, not to the whole retry chain. Backoff delays run
outside that timeout. A timed-out attempt is passed to the resilience shield as a
ModuleTimeoutException, so exception filters still decide whether it should be retried. If the
attempt remains active after the cancellation grace period, the engine bypasses retries to avoid
running the same module instance concurrently with its abandoned attempt.
Default Retry Configuration
Retries are off by default. You can set a default retry count on the PipelineOptions:
var builder = Pipeline.CreateBuilder(args);
builder
.AddModule<Module1>()
.AddModule<Module2>()
.AddModule<Module3>();
builder.ConfigureOptions(options => options with
{
DefaultRetryCount = 3,
});
await builder.RunAsync();
This applies to all modules that don't override their retry configuration. Modules can override this default by configuring retries in Configure(ModuleConfigurationBuilder).