PollyGrpc 1.0.0
See the version list below for details.
dotnet add package PollyGrpc --version 1.0.0
NuGet\Install-Package PollyGrpc -Version 1.0.0
<PackageReference Include="PollyGrpc" Version="1.0.0" />
<PackageVersion Include="PollyGrpc" Version="1.0.0" />
<PackageReference Include="PollyGrpc" />
paket add PollyGrpc --version 1.0.0
#r "nuget: PollyGrpc, 1.0.0"
#:package PollyGrpc@1.0.0
#addin nuget:?package=PollyGrpc&version=1.0.0
#tool nuget:?package=PollyGrpc&version=1.0.0
PollyGrpc
Polly v8 resilience for gRPC .NET clients — retry, circuit breaker, and per-call timeout via a lightweight Interceptor, no protobuf changes required.
Why PollyGrpc?
gRPC services can fail transiently (network blips, overloaded servers, rolling restarts). Without resilience, a single Unavailable or ResourceExhausted status crashes your call. PollyGrpc wraps every call in a Polly v8 pipeline so transient failures are retried automatically, persistent failures trip the circuit breaker, and runaway calls are cancelled by a configurable timeout.
| Feature | Built-in gRPC | PollyGrpc |
|---|---|---|
| Automatic retry | ❌ | ✅ |
| Circuit breaker | ❌ | ✅ |
| Per-call timeout | ❌ | ✅ |
| Configurable transient codes | ❌ | ✅ |
Works with AddGrpcClient<T>() |
❌ | ✅ |
Works with raw CallInvoker |
❌ | ✅ |
Installation
dotnet add package PollyGrpc
Quick Start
With AddGrpcClient<T>() (recommended)
builder.Services
.AddGrpcClient<Greeter.GreeterClient>(o =>
o.Address = new Uri("https://my-grpc-service"))
.AddPollyGrpcResilience(o =>
{
o.MaxRetries = 3;
o.BaseDelay = TimeSpan.FromMilliseconds(200);
o.CallTimeout = TimeSpan.FromSeconds(10);
});
With a raw CallInvoker
var channel = GrpcChannel.ForAddress("https://my-grpc-service");
var invoker = channel.CreateCallInvoker()
.WithPollyResilience(o => o.MaxRetries = 3);
var client = new Greeter.GreeterClient(invoker);
Configuration
var options = new PollyGrpcOptions
{
// Retry
MaxRetries = 3, // 0 = no retry
BaseDelay = TimeSpan.FromMilliseconds(200), // exponential base
MaxDelay = TimeSpan.FromSeconds(30),
// Circuit breaker
CircuitBreakerFailureRatio = 0.5,
CircuitBreakerMinimumThroughput = 10,
CircuitBreakerSamplingDuration = TimeSpan.FromSeconds(30),
CircuitBreakerBreakDuration = TimeSpan.FromSeconds(5),
// Timeout
CallTimeout = TimeSpan.FromSeconds(10),
// Which gRPC status codes are treated as transient (and therefore retried)
TransientStatusCodes = new HashSet<StatusCode>
{
StatusCode.Unavailable,
StatusCode.DeadlineExceeded,
StatusCode.ResourceExhausted,
StatusCode.Aborted,
StatusCode.Internal,
},
};
| Property | Default | Description |
|---|---|---|
MaxRetries |
3 |
Number of retry attempts (0 = disabled) |
BaseDelay |
200 ms |
Base delay for exponential back-off with jitter |
MaxDelay |
30 s |
Cap for exponential back-off delay |
CircuitBreakerFailureRatio |
0.5 |
Failure ratio to open the circuit |
CircuitBreakerMinimumThroughput |
10 |
Minimum calls before CB can open |
CircuitBreakerSamplingDuration |
30 s |
Sliding window for failure ratio |
CircuitBreakerBreakDuration |
5 s |
How long the circuit stays open |
CallTimeout |
10 s |
Maximum time per call before TimeoutRejectedException |
TransientStatusCodes |
see above | StatusCode set that triggers retry/CB |
Error Handling
Non-transient RpcExceptions (e.g. NotFound, PermissionDenied) are rethrown as-is. After all retries are exhausted the last exception is a TransientRpcException wrapping the original RpcException:
try
{
var reply = await client.SayHelloAsync(new HelloRequest { Name = "World" });
}
catch (TransientRpcException ex)
{
// All retries failed
Console.WriteLine($"gRPC status: {ex.StatusCode}");
Console.WriteLine($"Original error: {ex.RpcException.Status.Detail}");
}
catch (BrokenCircuitException)
{
// Circuit is open — fail fast without calling the server
}
catch (TimeoutRejectedException)
{
// Call exceeded CallTimeout
}
Resilience Pipeline Order
Calls flow through the pipeline in this order:
Retry → Circuit Breaker → Timeout → gRPC call
Related Packages
| Package | Description |
|---|---|
| PollyBackoff | Advanced back-off strategies with jitter |
| PollyChaos | Chaos engineering — inject faults in tests |
| PollyMediatR | Polly pipeline behaviour for MediatR |
| PollyEFCore | Resilient EF Core execution strategies |
| PollyHealthChecks | Health check endpoints for Polly circuits |
| PollyOpenAI | Retry + rate-limit handling for OpenAI / Azure OpenAI |
| PollyRedis | Resilient StackExchange.Redis wrapper |
| PollySignalR | Reconnect policy for SignalR HubConnection |
| PollyCaching | Distributed cache with Polly resilience |
| PollyBulkhead | Bulkhead isolation for concurrent workloads |
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Grpc.Net.Client (>= 2.80.0)
- Grpc.Net.ClientFactory (>= 2.80.0)
- Microsoft.Extensions.Http (>= 8.0.1)
- Polly.Core (>= 8.7.0)
-
net9.0
- Grpc.Net.Client (>= 2.80.0)
- Grpc.Net.ClientFactory (>= 2.80.0)
- Microsoft.Extensions.Http (>= 8.0.1)
- Polly.Core (>= 8.7.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Initial release. PollyClientInterceptor wraps unary and server-streaming gRPC calls with Polly v8 retry, circuit breaker, and timeout. AddPollyGrpcResilience() extension for IHttpClientBuilder.