Operations Framework: Reprocessing
Operation Reprocessing automatically retries commands that fail with transient errors. This is essential for handling temporary failures like:
- Database connection issues
- Deadlocks
- Network timeouts
- Concurrent modification conflicts
Enabling Reprocessing
var fusion = services.AddFusion();
fusion.AddOperationReprocessor(); // Enable operation reprocessingHow It Works
- Command execution starts
- If a transient error occurs,
OperationReprocessorcatches it - Checks if retry is allowed (filter, retry count, error type)
- If allowed, waits for delay and retries the command
- Repeats until success or max retries exceeded
Configuration Options
fusion.AddOperationReprocessor(_ => new() {
MaxRetryCount = 3, // Default: 3
RetryDelays = RetryDelaySeq.Exp(0.50, 3, 0.33), // Exponential backoff
Filter = (command, context) => /* custom filter */,
});Options Reference
| Option | Type | Default | Description |
|---|---|---|---|
MaxRetryCount | int | 3 | Maximum retry attempts |
RetryDelays | RetryDelaySeq | Exp(0.50, 3, 0.33) | Delay sequence between retries |
DelayClock | MomentClock? | null | Custom clock for delays |
Filter | Func<ICommand, CommandContext, bool> | DefaultFilter | Custom retry filter |
Retry Delays
The default retry delays use exponential backoff:
RetryDelaySeq.Exp(0.50, 3, 0.33)
// Base: 0.5 seconds
// Max multiplier: 3x
// Jitter: ±33%
// Produces delays approximately:
// Retry 1: ~0.5s (0.33s - 0.67s)
// Retry 2: ~1.65s (1.1s - 2.2s)
// Retry 3: ~5.45s (3.6s - 7.3s)Default Filter
The default filter determines which commands can be retried:
public static bool DefaultFilter(ICommand command, CommandContext context)
{
// Only on server
if (!RuntimeInfo.IsServer)
return false;
// Skip delegating commands (proxies)
if (command is IDelegatingCommand)
return false;
// Skip scoped Commander commands (UI commands)
if (context.Commander.Hub.IsScoped)
return false;
// Only root-level commands
return true;
}Filtering Conditions
A command is eligible for reprocessing when all of these are true:
- The error is classified as transient
Filterfunction returnstrue- Retry count hasn't exceeded
MaxRetryCount - No existing
Operationhas started - No
Invalidationis active - Not a nested command
- Not an
ISystemCommand
Transient Error Detection
The reprocessor uses TransiencyResolver<IOperationReprocessor> to classify errors. See Transiency documentation for details on how exception classification works.
| Error Type | Transiency |
|---|---|
ITransientException | Transient |
DbUpdateConcurrencyException | Transient |
SocketException | Transient |
TimeoutException | Transient |
| Other exceptions | Non-transient (no retry) |
Super-Transient Errors
Some errors are classified as super-transient, meaning they can retry indefinitely (ignoring MaxRetryCount):
// Super-transient errors retry without limit
if (transiency == Transiency.SuperTransient)
return true; // Always retryThis is useful for errors like:
- Connection pool exhausted (will resolve when connections free up)
- Service temporarily unavailable
Custom Transiency Detection
Register custom error classifiers:
services.AddSingleton<ITransiencyResolver<IOperationReprocessor>, MyTransiencyResolver>();
public class MyTransiencyResolver : ITransiencyResolver<IOperationReprocessor>
{
public Transiency GetTransiency(Exception error)
{
if (error is MyCustomRetryableException)
return Transiency.Transient;
if (error is MyRateLimitException)
return Transiency.SuperTransient; // Retry indefinitely
return Transiency.Unknown; // Fall through to other resolvers
}
}Context Reset on Retry
When a command is retried, the execution context is reset:
- New
CommandContextcreated - Previous
Operationdiscarded Itemscollections cleared- New execution ID assigned
This ensures each retry attempt starts fresh.
Logging
Reprocessing logs warnings for retries:
[WRN] OperationReprocessor: Reprocessing MyCommand after 500ms delay (attempt 1/3)Best Practices
- Keep commands idempotent – Retries may execute the same logic multiple times
- Use appropriate retry counts – Too many retries delay failure reporting
- Consider error types – Not all errors should be retried
- Log retry context – Include relevant information for debugging
- Set reasonable delays – Give systems time to recover
Example: Custom Retry Policy
fusion.AddOperationReprocessor(_ => new() {
MaxRetryCount = 5,
RetryDelays = RetryDelaySeq.Exp(
TimeSpan.FromSeconds(1), // Base delay
TimeSpan.FromSeconds(30)), // Max delay
Filter = (command, context) => {
// Don't retry admin commands
if (command is IAdminCommand)
return false;
// Don't retry commands from specific users
if (command is IUserCommand userCmd && userCmd.UserId == SpecialUserId)
return false;
return OperationReprocessor.DefaultFilter(command, context);
},
});