Polly v8 resilience for RabbitMQ.Client v7+ — retry, circuit-breaker, and timeout for IChannel operations, with a built-in RabbitMqTransientErrors predicate covering the most common RabbitMQ transient exceptions. Includes automatic channel recreation between retries so AlreadyClosedException never kills your publisher.
// Before — crashes on broker restart
await channel.BasicPublishAsync("orders", "order.placed", body: payload);
// After — retry + channel recreation + circuit breaker
var resilient = await connection.CreateResilientChannelAsync(options =>
{
options.MaxRetries = 3;
options.RecreateChannelOnFailure = true; // rebuild channel between retries ✔
});
await resilient.BasicPublishAsync("orders", "order.placed", body: payload);dotnet add package PollyRabbitMQTargets net6.0, net8.0, and net9.0.
Dependencies: Polly.Core 8.*, RabbitMQ.Client 7.*, Microsoft.Extensions.DependencyInjection.Abstractions 8.*
PollyRabbitMQ ships RabbitMqTransientErrors.IsTransient — a pre-built PredicateBuilder covering the four most common transient RabbitMQ exceptions:
new RetryStrategyOptions
{
MaxRetryAttempts = 3,
ShouldHandle = RabbitMqTransientErrors.IsTransient, // built-in ✔
}| Exception | When it occurs |
|---|---|
AlreadyClosedException |
Channel or connection closed by the broker (most common) |
OperationInterruptedException |
Operation interrupted mid-flight (e.g. during broker restart) |
BrokerUnreachableException |
Cannot reach the broker — all endpoints tried |
ConnectFailureException |
TCP connect to a broker endpoint failed |
The critical one:
AlreadyClosedExceptionis thrown when the broker closes a channel while your app is publishing. Without retry, this crashes your publisher. WithPollyRabbitMQandRecreateChannelOnFailure = true, the channel is rebuilt transparently before each retry.
using PollyRabbitMQ;
var factory = new ConnectionFactory { HostName = "localhost" };
await using var connection = await factory.CreateConnectionAsync();
await using var resilient = await connection.CreateResilientChannelAsync(options =>
{
options.MaxRetries = 3;
options.BaseDelay = TimeSpan.FromMilliseconds(500);
options.MaxDelay = TimeSpan.FromSeconds(30);
options.OperationTimeout = TimeSpan.FromSeconds(15);
options.RecreateChannelOnFailure = true;
options.CircuitBreakerFailureRatio = 0.5;
options.CircuitBreakerMinimumThroughput = 10;
options.CircuitBreakerBreakDuration = TimeSpan.FromSeconds(5);
});
// Publish
await resilient.BasicPublishAsync("orders", "order.placed", body: payload);
// Consume single message
var message = await resilient.BasicGetAsync("order-queue", autoAck: false);
if (message is not null)
{
// Process...
await resilient.BasicAckAsync(message.DeliveryTag);
}
// Topology
await resilient.QueueDeclareAsync("order-queue", durable: true);
await resilient.ExchangeDeclareAsync("orders", ExchangeType.Direct, durable: true);// Program.cs
builder.Services.AddResilientRabbitMqChannel(
configureFactory: factory =>
{
factory.HostName = "localhost";
factory.UserName = "guest";
factory.Password = "guest";
},
configureOptions: options =>
{
options.MaxRetries = 3;
options.RecreateChannelOnFailure = true;
});
// Publisher
public class OrderPublisher(ResilientChannel channel)
{
public Task PublishAsync(ReadOnlyMemory<byte> payload, CancellationToken ct = default) =>
channel.BasicPublishAsync("orders", "order.placed", payload, cancellationToken: ct);
}| Method | Description |
|---|---|
BasicPublishAsync |
Publish a message to an exchange |
BasicGetAsync |
Poll a queue for one message |
BasicAckAsync |
Acknowledge a delivered message |
BasicNackAsync |
Negatively acknowledge (optionally requeue) |
QueueDeclareAsync |
Declare a queue |
ExchangeDeclareAsync |
Declare an exchange |
When RecreateChannelOnFailure = true (default) and a transient error occurs, ResilientChannel will:
- Detect the failure (catch
AlreadyClosedExceptionetc.) - Check if the current channel is closed
- Create a new
IChannelfrom the same connection before the next retry attempt - Transparently continue — your code doesn't change
This is critical for long-running publishers that survive broker restarts.
[Timeout] → [Retry + channel recreation] → [Circuit Breaker] → [RabbitMQ broker]
| Package | Downloads | Description |
|---|---|---|
| PollyHealthChecks | ASP.NET Core health checks for Polly v8 circuit breakers — expose circuit-breaker state (Closed, HalfOpen, Open, Isolated) as /health endpoint responses | |
| PollyBackoff | Backoff delay strategies for Polly v8 resilience pipelines | |
| PollyGrpc | Polly v8 resilience interceptor for gRPC | |
| PollyEFCore | Polly v8 resilience pipelines for Entity Framework Core — wrap every EF Core query and SaveChanges with retry, timeout and circuit-breaker via a single AddPollyResilience() call | |
| PollyMailKit | Polly v8 resilience pipelines for MailKit — retry, timeout, and circuit-breaker for SmtpClient.SendAsync and any MailKit SMTP operation | |
| PollyMassTransit | Polly v8 resilience pipelines for MassTransit — retry, timeout, and circuit-breaker for IBus.Publish and ISendEndpointProvider.Send | |
| PollyNpgsql | Polly v8 resilience pipelines for Npgsql (PostgreSQL) — retry, timeout, and circuit-breaker for NpgsqlConnection queries and commands, plus a built-in PostgresTransientErrors predicate covering all common PostgreSQL transient SQLSTATE codes | |
| PollyOpenAI | Polly v8 resilience for OpenAI and Azure OpenAI API calls | |
| PollyAzureEventHub | Polly v8 resilience pipelines for Azure Event Hubs — retry, timeout, and circuit-breaker for EventHubProducerClient and EventHubConsumerClient | |
| PollyElasticsearch | Polly v8 resilience pipelines for Elastic.Clients.Elasticsearch 8+ — retry, timeout, and circuit-breaker for any Elasticsearch operation, plus a built-in ElasticTransientErrors predicate covering rate limiting (429), service unavailability (503), gateway timeouts (504), and connection failures | |
| PollyHangfire | Polly v8 resilience pipelines for Hangfire — retry, timeout, and circuit-breaker for IBackgroundJobClient.Enqueue and Schedule | |
| PollyCosmosDb | Polly v8 resilience pipelines for Azure Cosmos DB — retry, timeout, and circuit-breaker for Container operations, plus a built-in CosmosTransientErrors predicate covering rate limiting (429), timeouts (408), partition failovers (410), and service unavailability (503) | |
| PollySendGrid | Polly v8 resilience pipelines for SendGrid — retry, timeout, and circuit-breaker for ISendGridClient.SendEmailAsync | |
| PollyMongo | Polly v8 resilience pipelines for MongoDB.Driver — wrap Find, InsertOne, UpdateOne, DeleteOne and other IMongoCollection calls with retry, timeout, circuit-breaker, and more using a single ResilientMongoCollection decorator | |
| PollyDapper | Polly v8 resilience pipelines for Dapper — wrap QueryAsync, ExecuteAsync, and other Dapper calls with retry, timeout, circuit-breaker, and more using a single ResilientDbConnection decorator | |
| PollyMediatR | Polly v8 resilience pipelines for MediatR — add retry, timeout, circuit-breaker, rate-limiting, hedging, and chaos engineering to any MediatR request handler with a single line of DI registration | |
| PollySqlClient | Polly v8 resilience pipelines for Microsoft.Data.SqlClient (SQL Server and Azure SQL) — retry, timeout, and circuit-breaker for SqlConnection queries and commands, plus a built-in SqlServerTransientErrors predicate covering all common SQL Server and Azure SQL transient error numbers | |
| PollyAzureKeyVault | Polly v8 resilience pipelines for Azure Key Vault — retry, timeout, and circuit-breaker for SecretClient, KeyClient, and CertificateClient | |
| PollyAzureQueueStorage | Polly v8 resilience pipelines for Azure Queue Storage — retry, timeout, and circuit-breaker for Azure.Storage.Queues QueueClient | |
| PollyRedis | Polly v8 resilience for StackExchange.Redis | |
| PollyAzureServiceBus | Polly v8 resilience for Azure Service Bus — retry, circuit breaker, and timeout for sending and receiving messages | |
| PollyAzureTableStorage | Polly v8 resilience pipelines for Azure Table Storage — retry, timeout, and circuit-breaker for Azure.Data.Tables TableClient |
The author of this package is available for consulting on Polly v8 resilience, Azure cloud architecture, and clean .NET design.
→ solidqualitysolutions.com · LinkedIn
MIT