Skip to content

Swevo/PollySendGrid

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PollySendGrid

NuGet NuGet Downloads CI License: MIT

Polly v8 resilience for SendGrid — add retry, timeout, and circuit-breaker to any email send in two lines.

var client = new SendGridClient(apiKey);

var resilient = client.WithPolly(pipeline => pipeline
    .AddRetry(new RetryStrategyOptions
    {
        MaxRetryAttempts = 3,
        Delay = TimeSpan.FromSeconds(2),
        BackoffType = DelayBackoffType.Exponential,
        UseJitter = true,
        ShouldHandle = SendGridTransientErrors.IsTransient,
    })
    .AddTimeout(TimeSpan.FromSeconds(30)));

var response = await resilient.SendEmailAsync(msg);

Why PollySendGrid?

SendGrid imposes rate limits and occasionally returns transient errors. Dropped emails mean missed notifications, failed password resets, and lost revenue. This library wraps the response check in Polly v8 so every transient error is retried automatically:

Problem Solution
HTTP 429 rate limit exceeded Auto-thrown as SendGridTransientException and retried
HTTP 500 transient server error Auto-thrown as SendGridTransientException and retried
HTTP 503 service unavailable Auto-thrown as SendGridTransientException and retried
HttpRequestException network failure Caught by SendGridTransientErrors.IsTransient
Cascading failures during an outage Wrap with AddCircuitBreaker

Installation

dotnet add package PollySendGrid
dotnet add package Polly.Core

Quick-start

1. Manual wiring

var client = new SendGridClient(apiKey);

var resilient = client.WithPolly(p => p
    .AddRetry(new RetryStrategyOptions
    {
        MaxRetryAttempts = 3,
        Delay = TimeSpan.FromSeconds(2),
        BackoffType = DelayBackoffType.Exponential,
        UseJitter = true,
        ShouldHandle = SendGridTransientErrors.IsTransient,
    }));

var msg = MailHelper.CreateSingleEmail(from, to, subject, plainText, htmlContent);
var response = await resilient.SendEmailAsync(msg);

2. Dependency injection

builder.Services.AddPollySendGrid(apiKey, pipeline => pipeline
    .AddRetry(new RetryStrategyOptions
    {
        MaxRetryAttempts = 3,
        Delay = TimeSpan.FromSeconds(2),
        BackoffType = DelayBackoffType.Exponential,
        UseJitter = true,
        ShouldHandle = SendGridTransientErrors.IsTransient,
    })
    .AddTimeout(TimeSpan.FromSeconds(30)));

public class EmailService(ResilientSendGridClient client)
{
    public Task<Response> SendAsync(SendGridMessage msg, CancellationToken ct)
        => client.SendEmailAsync(msg, ct);
}

Transient error reference

Condition Why it's transient
SendGridTransientException (HTTP 429) Rate limit — back off and retry
SendGridTransientException (HTTP 500) Transient server error
SendGridTransientException (HTTP 503) Service maintenance or overload
HttpRequestException Network failure

Note: SendGridTransientException is thrown automatically by ResilientSendGridClient when the response status code is in SendGridTransientErrors.StatusCodes. You do not throw it yourself.

API reference

Member Description
ResilientSendGridClient.Inner The underlying ISendGridClient
SendEmailAsync(msg, ct) Sends an email through the pipeline
ExecuteAsync<T>(operation, ct) Runs any ISendGridClient operation through the pipeline
SendGridTransientErrors.IsTransient PredicateBuilder for 429/500/503 + HttpRequestException
SendGridTransientErrors.StatusCodes IReadOnlySet<HttpStatusCode> — TooManyRequests, InternalServerError, ServiceUnavailable
client.WithPolly(pipeline) Wraps ISendGridClient with a pre-built pipeline
client.WithPolly(configure) Builds pipeline inline and wraps the client
services.AddPollySendGrid(configure) DI registration (requires ISendGridClient in DI)
services.AddPollySendGrid(apiKey, configure) DI registration with API key shortcut

Target frameworks

.NET 6 ✅ · .NET 8 ✅ · .NET 9 ✅

Related packages

Package Description
PollyAzureBlob Polly v8 for Azure Blob Storage
PollyAzureServiceBus Polly v8 for Azure Service Bus
PollyAzureKeyVault Polly v8 for Azure Key Vault
PollyAzureEventHub Polly v8 for Azure Event Hubs
PollyCosmosDb Polly v8 for Azure Cosmos DB
PollyElasticsearch Polly v8 for Elasticsearch
PollyRedis Polly v8 for StackExchange.Redis
PollyEFCore Polly v8 for Entity Framework Core
PollyDapper Polly v8 for Dapper
PollyMongo Polly v8 for MongoDB
PollyNpgsql Polly v8 for Npgsql (PostgreSQL)
PollySqlClient Polly v8 for Microsoft.Data.SqlClient
PollyGrpc Polly v8 for gRPC
PollyRabbitMQ Polly v8 for RabbitMQ
PollyKafka Polly v8 for Confluent.Kafka
PollySignalR Polly v8 for SignalR
PollyOpenAI Polly v8 for OpenAI .NET SDK
PollyMediatR Polly v8 for MediatR
PollyHealthChecks Polly v8 for ASP.NET Core Health Checks
PollyMassTransit Polly v8 for MassTransit
PollyAzureTableStorage Polly v8 for Azure Table Storage
PollyMailKit MailKit SMTP email client
PollyAzureQueueStorage Azure Queue Storage QueueClient
PollyHangfire Hangfire IBackgroundJobClient
PollyBackoff Polly v8 backoff helpers

💼 Need .NET consulting?

The author of this package is available for consulting on Polly v8 resilience, Azure cloud architecture, and clean .NET design.

→ solidqualitysolutions.com · LinkedIn

License

MIT © Justin Bannister

Packages

 
 
 

Contributors

Languages