🔥 Pragmatic .NET Code Rules Course is on Presale - 40% off!BUY NOW

Retry Failed API calls with Polly

June 03 2024

Sponsored

• Discover the principles and best practices of API design with Postman's comprehensive guide. Learn how to create adaptable, testable, and well-documented APIs that support your business objectives. Dive into the full process and enhance your API design capabilities at Postman API Design.

Many thanks to the sponsors who make it possible for this newsletter to be free for readers. Become a sponsor.

The background

Polly

Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner.

Retry Policy

A Retry Policy is a strategy used in software development, particularly in distributed systems and microservices, to handle transient failures. This policy dictates that an operation, such as a network request or database transaction, should be attempted again if it fails, for a certain number of times.

Today I will tell you about 3 types of retry policies and I will give the implementation of all 3. Let's see what Solution looks like first. Note: The implemented code is not in accordance with best practices because the point is to show the implementation of the Retry policy in the simplest possible way.

Project Setup

Solution consists of 2 projects: API - Which represents the "Response" project, ie. which has an endpoint that we will call from another application and for which we will simulate that it does not work at certain moments. WebApp - Simulation of a Web project that calls an endpoint from an API project. In this project, we implement the Retry Policy in order to handle failed requests. Polly Retry Policy Web Application

API Project:

The project consists of one simple API controller with one method, ie. one endpoint. This endpoint for the passed Id should return the user. Inside the GetUser method, a random number between 1 and 50 is generated. If the provided id is less than this random number, the method simulates a successful operation and returns a 200 OK status code . If the id is greater or equal to the random number, it simulates a server error and returns a 500 Internal Server Error status code . In a real-world scenario, instead of randomly determining the success or failure, you'd typically interact with some business logic or data access layer, and the result could depend on various factors.

C#
[Route("api/[controller]")][ApiController]public class UserController : ControllerBase{ [Route("getUser/{id}")] [HttpGet] public ActionResult GetUser(int id) { Random random = new Random();  var failEdge = random.Next(1, 50);  if (id < failEdge) { Console.WriteLine("I'm returning Success - 200"); return Ok(); }  Console.WriteLine("I'm returning Error - 500"); return StatusCode(StatusCodes.Status500InternalServerError); }}

Retry Policies

Immediate or "Basic" Retry

Retry the operation immediately after a failure. This policy retries a specified number of times when an operation fails. This might be used in situations where the operation is expected to succeed most of the time, and failures are highly transient. Example below: When operation fails, retry the operation 3 times.

Fixed Interval Retry

Wait for a specific duration before each retry. While the basic retry is helpful, it might not always be the best approach. Sometimes, immediately retrying isn't the solution, especially when dealing with services that need a cooldown period after a fault. Example below: When operation fails, retry the operation 3 times every 5 seconds.

Exponential Backoff

Each retry waits for a longer period. It increases the wait time between retries exponentially, rather than keeping it constant. This approach can help in situations where repeated immediate retries could exacerbate the problem or where the service you're calling could be undergoing a restart and needs a longer grace period to recover. Example below: The first retry will happen after 2 seconds, the second retry after 4 seconds, the third after 8 seconds, and so on.

C#
public class ClientRetryPolicy{ public AsyncRetryPolicy<HttpResponseMessage> JustHttpRetry { get; set; } public AsyncRetryPolicy<HttpResponseMessage> HttpRetryWithWaiting { get; set; } public AsyncRetryPolicy<HttpResponseMessage> ExponentialHttpRetry { get; set; }  public ClientRetryPolicy() { JustHttpRetry = Policy.HandleResult<HttpResponseMessage>( response => !response.IsSuccessStatusCode) .RetryAsync(3);  HttpRetryWithWaiting = Policy.HandleResult<HttpResponseMessage>( response => !response.IsSuccessStatusCode) .WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(5));  ExponentialHttpRetry = Policy.HandleResult<HttpResponseMessage>( response => !response.IsSuccessStatusCode) .WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt))); }}

Using Policy with HttpClient

As I mentioned, within the Web project we call the API endpoint to get the necessary data. We call endpoint with the help of HTTP Client, with the fact that we will use the Retry Policy by calling that call.

C#
[HttpGet][Route("returnUser/{id}")]public async Task<ActionResult> ReturnUser(int id){ string apiURL = $"https://localhost:7071/api/User/getUser/{id}";  var response = await _retryPolicy.HttpRetryWithWaiting.ExecuteAsync(() => _client.GetAsync(apiURL));  if (response.IsSuccessStatusCode) { Console.WriteLine("Success 200");  return Ok(response); } else { Console.WriteLine("Error 500");  return StatusCode(StatusCodes.Status500InternalServerError); }}

In this example I used the "Fixed Interval Retry" policy. Which would practically mean that if the API returns an error, it will be called 3 times with an interval of 5 seconds between each or less times if it returns a successful result. Let's see that. Testing Retry Policy with Postman

With the help of Postman, we will call the WebApp project method from the UserManagement controller. The code inside this method calls the API endpoint with the Retry policy. This is what happened: Retry Policy Console result On the left side we have an API running project, and on the right side there is a WebApp project. We can see that just one call to the WebApp called API 4 times. Why? Because the first time result was 500 - Error. And 2nd time. And 3rd time. After 3 calls (in 5 seconds delay - check the time) the API returns a Success 200 response. In that momment we can return Success 200 to the user from the WebApp call.

What next?

Test the other 2 Retry Policies and think about how to solve this in a better way using DI in the Program.cs file. That's all from me for today. Make a coffee and check out source code directly on my GitHub repository.

For more resilience patterns, check out Building Resilient APIs in ASP.NET Core.

Wrapping Up

About the Author

Stefan Djokic is a Microsoft MVP and senior .NET engineer with extensive experience designing enterprise-grade systems and teaching architectural best practices.

There are 3 ways I can help you:

1. Pragmatic .NET Code Rules Course

Stop arguing about code style. In this course you get a production-proven setup with analyzers, CI quality gates, and architecture tests — the exact system I use in real projects. Join here.

Not sure yet? Grab the free Starter Kit — a drop-in setup with the essentials from Module 01.

2. Design Patterns Ebooks

Design Patterns that Deliver — Solve real problems with 5 battle-tested patterns (Builder, Decorator, Strategy, Adapter, Mediator) using practical, real-world examples. Trusted by 650+ developers.

Just getting started? Design Patterns Simplified covers 10 essential patterns in a beginner-friendly, 30-page guide for just $9.95.

3. Join 20,000+ subscribers

Every Monday morning, I share 1 actionable tip on C#, .NET & Architecture that you can use right away. Join here.

Join 20,000+ subscribers who mass-improve their .NET skills with actionable tips on C#, Architecture & Best Practices.

Subscribe to
TheCodeMan.net

Subscribe to the TheCodeMan.net and be among the 20,000+ subscribers gaining practical tips and resources to enhance your .NET expertise.