Round #220
07 May 2026, 14:57· csharp· meta/llama-3.3-70b-instruct· r12-real-contracts
- Complexity
- band 0
- ZP / UO / OBS
- 4 / 2 / 1
- Journey weight
- 518
- Lightshift ΔL
- —
Prompt
Write a worker that retries failed outbox messages with exponential backoff
Zero-point index (what the engine surfaced)
ZPI-ZPI-0013 | 2026-05-07T14:57:14Z
Write a worker that retries failed outbox messages with exponential backoff
ZERO POINT — 4 entries (structural invariants)
● hsl(296.42, 100%, 24%) ResiliencePipeline [contract]
● hsl(211.57, 100%, 24%) ResiliencePipelineBuilder [contract]
● hsl(136.10, 100%, 21%) DbContext [contract]
● hsl(61.72, 100%, 21%) BackgroundService [contract]
UNOBSERVED — 2 entries (implied truths)
○ hsl(27.69, 100%, 50%) async [feature]
○ hsl(3.75, 100%, 21%) CancellationToken [feature]
OBSERVED — 1 entries (explicit in request)
◉ hsl(98.46, 100%, 21%) with [feature]
COMPLEXITY │ 7 entries │ spread: 95° │ band: LOW
Baseline response (no ZPI)
**Exponential Backoff Worker for Retrying Failed Outbox Messages**
===========================================================
Below is an example implementation of a worker in C# that retries failed outbox messages with exponential backoff.
### Prerequisites
* .NET 6.0 or later
* `Microsoft.Extensions.Hosting` package for hosting the worker
* `Microsoft.Extensions.Logging` package for logging
* `System.Threading` package for threading and timing
### Implementation
```csharp
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
public class OutboxMessage
{
public string Id { get; set; }
public string Message { get; set; }
public int RetryCount { get; set; }
}
public interface IOutboxRepository
{
OutboxMessage GetNextFailedMessage();
void UpdateMessageStatus(string id, string status);
}
public class ExponentialBackoffWorker : BackgroundService
{
private readonly IOutboxRepository _outboxRepository;
private readonly ILogger<ExponentialBackoffWorker> _logger;
private readonly int _maxRetryCount = 5;
private readonly int _initialDelay = 1000; // 1 second
private readonly int _maxDelay = 30000; // 30 seconds
public ExponentialBackoffWorker(IOutboxRepository outboxRepository, ILogger<ExponentialBackoffWorker> logger)
{
_outboxRepository = outboxRepository;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var message = _outboxRepository.GetNextFailedMessage();
if (message != null)
{
try
{
// Process the message
await ProcessMessage(message);
_outboxRepository.UpdateMessageStatus(message.Id, "Success");
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error processing message {message.Id}");
if (message.RetryCount < _maxRetryCount)
{
// Calculate the delay for the next retry
var delay = CalculateDelay(message.RetryCount);
_logger.LogInformation($"Retrying message {message.Id} in {delay}ms");
await Task.Delay(delay, stoppingToken);
_outboxRepository.UpdateMessageStatus(message.Id, "Failed");
}
else
{
_logger.LogInformation($"Max retry count reached for message {message.Id}. Giving up.");
_outboxRepository.UpdateMessageStatus(message.Id, "Failed");
}
}
}
else
{
// No messages to process, wait for 1 second before checking again
await Task.Delay(1000, stoppingToken);
}
}
}
private async Task ProcessMessage(OutboxMessage message)
{
// Simulate processing the message
await Task.Delay(100);
}
private int CalculateDelay(int retryCount)
{
// Calculate the delay using exponential backoff
var delay = (int)Math.Min(_initialDelay * Math.Pow(2, retryCount), _maxDelay);
return delay;
}
}
```
### Explanation
* The `ExponentialBackoffWorker` class implements the `BackgroundService` interface and overrides the `ExecuteAsync` method to run the worker.
* The worker uses an `IOutboxRepository` interface to interact with the outbox messages.
* The worker retrieves the next failed message from the outbox repository and attempts to process it.
* If the processing fails, the worker calculates the delay for the next retry using exponential backoff and updates the message status to "Failed".
* The worker continues to retry the message until the maximum retry count is reached or the message is successfully processed.
* The `CalculateDelay` method calculates the delay for the next retry using exponential backoff.
### Example Use Case
To use this worker, you would need to implement the `IOutboxRepository` interface and register it with the DI container. You would also need to configure the worker to run in the background.
```csharp
public class Program
{
public static void Main(string[] args)
{
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddSingleton<IOutboxRepository, OutboxRepository>();
services.AddHostedService<ExponentialBackoffWorker>();
})
.Build();
host.Run();
}
}
public class OutboxRepository : IOutboxRepository
{
public OutboxMessage GetNextFailedMessage()
{
// Implement logic to retrieve the next failed message from the outbox
}
public void UpdateMessageStatus(string id, string status)
{
// Implement logic to update the message status
}
}
```
Note: This is a basic example and you may need to modify it to fit your specific use case. Additionally, you should consider implementing additional features such as handling
Enhanced response (with ZPI)
```csharp
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Polly;
using System;
using System.Threading;
using System.Threading.Tasks;
public class OutboxMessage
{
public int Id { get; set; }
public string Message { get; set; }
}
public interface IOutboxRepository
{
OutboxMessage GetNextFailedMessage();
void MarkMessageAsSent(int messageId);
void MarkMessageAsFailed(int messageId);
}
public class OutboxRepository : IOutboxRepository
{
private readonly DbContext _dbContext;
public OutboxRepository(DbContext dbContext)
{
_dbContext = dbContext;
}
public OutboxMessage GetNextFailedMessage()
{
// Implement database logic to retrieve the next failed message
throw new NotImplementedException();
}
public void MarkMessageAsSent(int messageId)
{
// Implement database logic to mark the message as sent
throw new NotImplementedException();
}
public void MarkMessageAsFailed(int messageId)
{
// Implement database logic to mark the message as failed
throw new NotImplementedException();
}
}
public class OutboxWorker : BackgroundService
{
private readonly IOutboxRepository _outboxRepository;
private readonly ILogger<OutboxWorker> _logger;
private readonly Policy _retryPolicy;
public OutboxWorker(IOutboxRepository outboxRepository, ILogger<OutboxWorker> logger)
{
_outboxRepository = outboxRepository;
_logger = logger;
_retryPolicy = Policy.Handle<Exception>()
.WaitAndRetryForever(
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
(exception, timeSpan, retryCount, context) =>
{
_logger.LogError(exception, "Failed to send message. Retrying in {TimeSpan}", timeSpan);
});
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var message = _outboxRepository.GetNextFailedMessage();
if (message != null)
{
try
{
await _retryPolicy.ExecuteAsync(async () =>
{
// Implement logic to send the message
await SendOutboxMessage(message);
_outboxRepository.MarkMessageAsSent(message.Id);
});
}
catch (Exception ex)
{
_outboxRepository.MarkMessageAsFailed(message.Id);
_logger.LogError(ex, "Failed to send message");
}
}
else
{
await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken);
}
}
}
private async Task SendOutboxMessage(OutboxMessage message)
{
// Implement logic to send the message
throw new NotImplementedException();
}
}
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddDbContext<DbContext>(options =>
{
// Configure database context
});
services.AddTransient<IOutboxRepository, OutboxRepository>();
services.AddHostedService<OutboxWorker>();
});
}
```
This implementation uses the `BackgroundService` class to create a worker that runs in the background and retries failed outbox messages with exponential backoff. The `OutboxRepository` class is used to interact with the database and retrieve the next failed message. The `OutboxWorker` class uses Polly to implement the retry policy with exponential backoff.
The `OutboxWorker` class is registered as a hosted service in the `Program` class, and the `OutboxRepository` class is registered as a transient service. The database context is also registered in the `Program` class.
The `SendOutboxMessage` method is not implemented in this example, as it will depend on the specific requirements of your application. You will need to implement this method to send the message using your chosen messaging system.
The retry policy is configured to wait for an increasing amount of time between retries, with the first retry waiting for 2 seconds, the second retry waiting for 4 seconds, and so on. The policy will retry indefinitely until the message is successfully sent.
If an exception occurs while sending the message, the message will be marked as failed and the retry policy will be triggered again. If the message is successfully sent, it will be marked as sent and the worker will move on to the next failed message.
Grader chain
Each grader reads the same baseline + enhanced pair and casts an independent verdict. Disagreement is the framework's own signal — not noise to be averaged away.
V1claude-haiku-4-5-20251001
BetterV4qwen2.5-coder-7b-instruct
BetterComments
Our grader said what it said. What do you say? Comment as a guest below.
No comments yet. Be the first to say what you make of this round.