Round #489

09 May 2026, 19:44· csharp· qwen2.5-coder-7b-instruct· r13-clean-hints
Complexity
band 0
ZP / UO / OBS
4 / 2 / 1
Journey weight
384
Lightshift ΔL
+0.50

Prompt

Write a worker that retries failed outbox messages with exponential backoff

Zero-point index (what the engine surfaced)

ZPI-ZPI-0013 | 2026-05-09T19:44:03Z Write a worker that retries failed outbox messages with exponential backoff ZERO POINT — 4 entries (structural invariants) ● hsl(296.42, 100%, 46%) ResiliencePipeline [contract] ● hsl(211.57, 100%, 46%) ResiliencePipelineBuilder [contract] ● hsl(136.10, 100%, 50%) DbContext [contract] ● hsl(61.72, 100%, 43%) BackgroundService [contract] UNOBSERVED — 2 entries (implied truths) ○ hsl(27.69, 100%, 50%) async [feature] ○ hsl(3.75, 100%, 38%) CancellationToken [feature] OBSERVED — 1 entries (explicit in request) ◉ hsl(98.46, 100%, 44%) with [feature] COMPLEXITY │ 7 entries │ spread: 95° │ band: LOW

Baseline response (no ZPI)

Certainly! Below is an example of a worker in C# that retries failed outbox messages using exponential backoff. This example assumes you have a basic understanding of how to handle outbox messages and a message queue system (e.g., RabbitMQ, Azure Service Bus). First, let's define the necessary classes and interfaces: 1. `IOutboxMessage`: Represents an outbox message. 2. `IMessageQueue`: Represents a message queue system. ```csharp public interface IOutboxMessage { Guid Id { get; } string Content { get; } DateTime? RetryAt { get; set; } } public interface IMessageQueue { void SendMessage(IOutboxMessage message); IEnumerable<IOutboxMessage> GetFailedMessages(); } ``` Next, let's implement the worker: ```csharp using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; public class OutboxWorker { private readonly IMessageQueue _messageQueue; private readonly int _maxRetries; private readonly TimeSpan _initialDelay; public OutboxWorker(IMessageQueue messageQueue, int maxRetries = 5, TimeSpan initialDelay = default) { _messageQueue = messageQueue ?? throw new ArgumentNullException(nameof(messageQueue)); _maxRetries = maxRetries; _initialDelay = initialDelay == default ? TimeSpan.FromSeconds(1) : initialDelay; } public async Task RunAsync() { while (true) { var failedMessages = _messageQueue.GetFailedMessages().ToList(); foreach (var message in failedMessages) { if (message.RetryAt.HasValue && DateTime.UtcNow < message.RetryAt.Value) continue; try { // Process the message (e.g., send it to a queue, database, etc.) await ProcessMessageAsync(message); _messageQueue.SendMessage(message); // Re-enqueue the message for confirmation } catch (Exception ex) { HandleFailure(message, ex); } } await Task.Delay(_initialDelay); } } private async Task ProcessMessageAsync(IOutboxMessage message) { // Implement your message processing logic here Console.WriteLine($"Processing message: {message.Id}"); // Simulate a failure for demonstration purposes if (DateTime.UtcNow.Second % 2 == 0) throw new InvalidOperationException("Simulated processing error"); } private void HandleFailure(IOutboxMessage message, Exception ex) { if (message.RetryCount >= _maxRetries) { Console.WriteLine($"Max retries reached for message: {message.Id}. Giving up."); return; } var retryDelay = CalculateExponentialBackoff(message.RetryCount); message.RetryAt = DateTime.UtcNow.Add(retryDelay); message.RetryCount++; Console.WriteLine($"Failed to process message: {message.Id}. Retrying in {retryDelay.TotalSeconds} seconds."); } private TimeSpan CalculateExponentialBackoff(int retryCount) { return TimeSpan.FromSeconds(Math.Pow(2, retryCount) * _initialDelay.TotalSeconds); } } ``` Finally, let's implement a simple `IMessageQueue` implementation using an in-memory list for demonstration purposes: ```csharp public class InMemoryMessageQueue : IMessageQueue { private readonly List<IOutboxMessage> _messages = new List<IOutboxMessage>(); public void SendMessage(IOutboxMessage message) { _messages.Add(message); } public IEnumerable<IOutboxMessage> GetFailedMessages() { return _messages.Where(m => m.RetryAt.HasValue && DateTime.UtcNow >= m.RetryAt.Value); } } ``` To use the worker, you can create an instance of `OutboxWorker` and call its `RunAsync` method: ```csharp public class Program { public static async Task Main(string[] args) { var messageQueue = new InMemoryMessageQueue(); var worker = new OutboxWorker(messageQueue); // Simulate sending a failed outbox message var message = new OutboxMessage { Id = Guid.NewGuid(), Content = "Test message" }; message.RetryCount = 0; message.RetryAt = DateTime.UtcNow.AddSeconds(-1); // Mark as failed immediately messageQueue.SendMessage(message); await worker.RunAsync(); } } ``` This example demonstrates a basic implementation of an outbox worker with exponential backoff. You can extend and customize it according to your specific requirements, such as integrating with a real message queue system or handling different types of exceptions.

Enhanced response (with ZPI)

To implement a worker that retries failed outbox messages with exponential backoff, we can use the `BackgroundService` class from the .NET framework. This class allows us to create a long-running service that performs background tasks. We will also use the `ResiliencePipelineBuilder` and `ResiliencePipeline` classes from the Polly library to implement the retry logic with exponential backoff. Here's the implementation: ```csharp using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Polly; using Polly.Retry; using System; using System.Threading; using System.Threading.Tasks; public class OutboxWorker : BackgroundService { private readonly IOutboxRepository _outboxRepository; private readonly ResiliencePipeline _retryPolicy; public OutboxWorker(IOutboxRepository outboxRepository, ResiliencePipeline retryPolicy) { _outboxRepository = outboxRepository ?? throw new ArgumentNullException(nameof(outboxRepository)); _retryPolicy = retryPolicy ?? throw new ArgumentNullException(nameof(retryPolicy)); } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { try { var failedMessages = await _outboxRepository.GetFailedMessagesAsync(stoppingToken); foreach (var message in failedMessages) { await _retryPolicy.ExecuteAsync(async () => { // Process the message here await _outboxRepository.MarkMessageAsProcessedAsync(message.Id, stoppingToken); }, stoppingToken); } } catch (Exception ex) { // Log the exception and retry later Console.WriteLine($"Error processing outbox messages: {ex}"); } // Wait for a short period before checking again await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken); } } } public static class OutboxWorkerExtensions { public static IServiceCollection AddOutboxWorker(this IServiceCollection services) { services.AddHostedService<OutboxWorker>(); // Configure Polly retry policy with exponential backoff services.AddSingleton<ResiliencePipeline>(provider => Policy .Handle<Exception>() .WaitAndRetryAsync(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))); return services; } } ``` Explanation: 1. **OutboxWorker Class**: - This class inherits from `BackgroundService`, which provides the infrastructure for running a long-running background task. - It takes an `IOutboxRepository` and a `ResiliencePipeline` as dependencies. - The `ExecuteAsync` method is overridden to implement the worker's logic. It continuously retrieves failed outbox messages, retries processing them using the Polly policy, and logs any exceptions. 2. **OutboxWorkerExtensions Class**: - This class provides an extension method to register the `OutboxWorker` and configure the Polly retry policy. - The `AddHostedService<OutboxWorker>` method registers the worker as a hosted service. - The `ResiliencePipeline` is configured with exponential backoff using Polly's `WaitAndRetryAsync` method. 3. **IOutboxRepository Interface**: - This interface defines methods for interacting with the outbox repository, such as retrieving failed messages and marking them as processed. 4. **DI Registration**: - The `AddOutboxWorker` extension method registers the worker and configures the Polly retry policy in the dependency injection container. This implementation provides a robust solution for retrying failed outbox messages with exponential backoff using a background service and Polly's resilience features.

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.

V1samba/Meta-Llama-3.3-70B-Instruct
Better
V4qwen2.5-coder-3b-instruct
Better
V5openrouter/qwen/qwen3-235b-a22b-2507
Better
V7qwen2.5-coder-7b-instruct
Better
V8openai/gpt-4.1
Better
V9anthropic/claude-opus-4-7
Worse
V12openai/gpt-4o
Better

Comments

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.

← back to all rounds