Skip to main content
Version: Next

Dead-Letter Background Worker

Call WithReplayWorker() to add a hosted service that polls the store and replays pending dead letters in the background.

Registration

// Register a named publisher pipeline for replay
services.AddEventPublisher("transport", builder => builder
.Configure(options =>
{
options.Source = new Uri("https://orders.example.com/transport");
options.ThrowOnErrors = true;
})
.AddRabbitMq(options =>
{
options.ConnectionString = "amqp://guest:guest@localhost:5672";
options.ExchangeName = "orders";
}));

// Register dead-letter with background worker
services.AddEventPublisher(options => options.Source = new Uri("https://orders.example.com/replay"))
.AddDeadLetter()
.UseRepository<MyDeadLetterMessage, MyDeadLetterStore>()
.WithFactory<MyDeadLetterMessage, MyDeadLetterMessageFactory>()
.WithReplayWorker(options =>
{
options.TransportPublisherName = "transport";
options.Interval = TimeSpan.FromSeconds(15);
options.MaxBatchSize = 50;
options.MaxRetryCount = 5;
options.RetryInterval = TimeSpan.FromMinutes(2);
});

DeadLetterReplayOptions

PropertyTypeDefaultDescription
IntervalTimeSpan00:00:30Polling interval for the background worker
MaxBatchSizeint0Maximum pending messages processed per cycle (0 = no limit)
TransportPublisherNamestringemptyNamed publisher pipeline used for replay
MaxRetryCountint3Maximum automatic replay attempts before marking as Failed
RetryIntervalTimeSpan00:01:00Delay before the next replay attempt after a failure

How the Worker Works

The worker resolves IDeadLetterReplayProcessor, reads eligible pending messages, and replays them one by one:

  1. Query — Get pending messages where NextReplayAt <= UtcNow
  2. Claim — Mark each message as Replaying to prevent duplicate processing
  3. Replay — Publish the message through the configured publisher
  4. Update — On success: mark as Replayed; on failure: schedule retry or mark as Failed
  5. Repeat — Continue until MaxBatchSize is reached or no more pending messages