Polly Async API Demo

Learn how to build resilient .NET 8 web applications using Polly for advanced retry strategies. Explore the power of asynchronous programming to handle transient faults in HTTP requests efficiently.

Loop Count
40
Max Time (ms)
1500
Result
780
RunTime (ms)
886
Message
Task Complete
Submit Timeout Test

This demonstration showcases how to handle transient errors in HTTP requests using Polly's async retry strategies. Select the number of loops and the maximum allowed response time to see Polly in action.

Understanding Polly: Features, Purpose, and Best Practices

Features of Polly

Polly provides a suite of resilience strategies that help make your .NET applications robust and fault-tolerant. Key features include:

  • Retry: Polly’s retry feature allows you to automatically retry failed operations, with customizable delay strategies such as exponential backoff and jitter to avoid synchronized retries that can overwhelm a service.
  • Circuit Breaker: Circuit breakers stop repetitive attempts to access a service when it’s known to be down, allowing the service time to recover before new requests are allowed.
  • Timeouts: Timeouts automatically cancel requests that exceed a specified duration, helping to prevent your application from hanging due to unresponsive services.
  • Bulkhead Isolation: Bulkheads limit the number of concurrent calls to a particular resource, preventing overloads by isolating failures to a specific section of the application.
  • Fallback: Fallback strategies provide alternative actions when all other policies fail, such as returning a default response or invoking a secondary service.

Purpose and Best Practices

The primary purpose of Polly is to help developers build resilient applications that can handle transient faults gracefully. By using Polly, you can create systems that are not only robust against temporary outages but also optimize user experience by avoiding unnecessary failures and retries.

Best Practices

  • Start with Retries: Begin with basic retry logic, but ensure that retries are well-thought-out with exponential backoff to avoid overwhelming the service.
  • Use Circuit Breakers: Combine retries with circuit breakers to prevent cascading failures and avoid self-inflicted Denial of Service (DoS) scenarios where too many retries flood a struggling backend service.
  • Combine Multiple Policies: Mix retries with fallbacks, circuit breakers, and bulkhead isolation to build a comprehensive resilience strategy.
  • Monitor and Log: Implement logging and monitoring to track the performance of Polly policies in real-time, allowing for adjustments based on actual system behavior.
  • Test in Staging Environments: Regularly test the configurations in a staging environment to validate that policies behave as expected under simulated failure conditions.

Understanding the Retry Model

The retry model in Polly is designed to handle transient faults such as network blips or temporary service outages. By retrying an operation a specified number of times with defined delays, you can often succeed without user intervention. However, it’s crucial to configure retries correctly:

Potential Pitfall - Self-inflicted DoS: Poorly configured retries, especially with immediate retries or too many attempts, can lead to a self-inflicted Denial of Service (DoS) attack. When a backend service is under load or stress, excessive retries can exacerbate the problem by increasing traffic precisely when the service is least capable of handling it. To mitigate this risk, use strategies like:

  • Exponential Backoff: Increase the delay between retries exponentially to reduce the retry rate over time.
  • Jitter: Add randomness to retry intervals to avoid synchronized retries from multiple clients.
  • Max Retry Attempts: Limit the number of retries to prevent excessive load on struggling services.

Circuit Breakers - When to Use Them

Circuit breakers are critical in preventing repeated calls to a failing service, which can lead to cascading failures in a distributed system. They work by opening a circuit after a specified number of failures, temporarily stopping requests to give the service time to recover.

Use Cases for Circuit Breakers

  • Protecting Services: Use circuit breakers to protect critical services from being overwhelmed by repeated failure requests.
  • Graceful Degradation: During outages, circuit breakers allow your application to degrade gracefully rather than fail completely.
  • Recovery Monitoring: Circuit breakers automatically test the service periodically to determine if it has recovered and, if so, resume normal operations.

By incorporating circuit breakers alongside retries, you can create a balanced approach that keeps your application responsive without overloading your backend systems.

By leveraging Polly, you can significantly enhance the resilience of your .NET applications, making them robust against a wide range of transient failures. Whether you’re building microservices, APIs, or client applications, Polly’s powerful and flexible approach to fault handling ensures that your applications remain reliable and user-friendly under stress.

Learn More About Polly on GitHub

Understanding PollyController

Learn how to implement resilient HTTP requests in ASP.NET Core using Polly for retries and circuit breakers.

Introduction to PollyController

The PollyController demonstrates the use of Polly for handling retries and circuit breakers in HTTP requests within an ASP.NET Core application. This approach helps in managing transient faults and maintaining service resilience.

Code Samples

Below are key code snippets from the PollyController that showcase how to set up and use retry and circuit breaker policies with Polly.

1. Setting Up Retry Policy

This method defines a retry policy with jitter to handle failed HTTP requests by retrying the operation with a delay.


private AsyncRetryPolicy<HttpResponseMessage> GetAsyncRetryPolicy(Random jitter, string retryCountKey)
{
    var retryPolicy = Policy
        .HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
        .WaitAndRetryAsync(
            3,
            retryAttempt => TimeSpan.FromMilliseconds(100) + TimeSpan.FromMilliseconds(jitter.Next(0, 100)),
            onRetry: (response, timespan, retryCount, context) =>
            {
                context[retryCountKey] = retryCount;
                var message = response.Result?.StatusCode.ToString() ?? "Request failed without response.";
                if (response.Exception != null)
                {
                    message += $" Exception: {response.Exception.Message}";
                }
                logger.LogWarning("Request failed with {StatusCode}. Waiting {Timespan} before next retry. Retry attempt {RetryCount}.",
                                  response.Result?.StatusCode, timespan, retryCount);
            });
    return retryPolicy;
}
            

2. Setting Up Circuit Breaker Policy

The circuit breaker policy stops further requests when a specified number of consecutive failures occur, helping to avoid overwhelming a struggling service.


private AsyncCircuitBreakerPolicy<HttpResponseMessage> GetCircuitBreakPolicy()
{
    var circuitBreakerPolicy = Policy
        .HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
        .CircuitBreakerAsync(
            3, 
            TimeSpan.FromSeconds(10),
            onBreak: (outcome, breakDelay) =>
            {
                logger.LogWarning("Circuit breaker opened due to {StatusCode}. Waiting {BreakDelay} before next attempt.",
                                   outcome.Result?.StatusCode, breakDelay);
            },
            onReset: () => logger.LogInformation("Circuit breaker reset."),
            onHalfOpen: () => logger.LogInformation("Circuit breaker half-open: Testing the service again."));
    return circuitBreakerPolicy;
}
            

3. Executing HTTP Requests with Policies

This example shows how the retry and circuit breaker policies are applied to HTTP requests executed in the controller.


public async Task<IActionResult> Index(int loopCount = 1, int maxTimeMs = 1000)
{
    var context = new Context { { "retrycount", 0 }, { "ResultsList", new List<string>() } };
    var mockResults = new MockResults { LoopCount = loopCount, MaxTimeMS = maxTimeMs };
    var request = new HttpRequestMessage(HttpMethod.Post, $"{Request.Scheme}://{Request.Host}{Request.PathBase}/api/asyncspark/remote/results")
    {
        Content = JsonContent.Create(mockResults)
    };

    using var cts = new CancellationTokenSource(maxTimeMs);
    HttpResponseMessage response = await ExecuteRequestWithPoliciesAsync(request, context, cts.Token);

    await HandleResponse(response, mockResults);

    // Stop timing the operation and set the runtime
    mockResults.RunTimeMS = GetElapsedMilliseconds();

    // Return the results to the view
    return View("Index", mockResults);
}
            

Key Takeaways

  • Polly provides a powerful and flexible way to handle transient faults in HTTP communications.
  • Retry policies allow for controlled retries with customizable delays and error handling logic.
  • Circuit breaker policies prevent cascading failures by breaking the circuit when too many errors occur, allowing time for recovery.

Additional Resources

For more information on Polly and its use in ASP.NET Core, visit the official Polly documentation and explore the various policies and patterns available:

Understanding the AsyncMockService and Its Role in Polly Demos

What is AsyncMockService?

The AsyncMockService is a utility class used to simulate various asynchronous operations that are common in real-world applications. It includes methods that represent long-running tasks, support for cancellation, and handling of exceptions, making it an ideal target endpoint for demonstrating how Polly can be used to manage resilience in asynchronous programming.

This service is designed to mimic scenarios such as processing heavy computations, handling cancellations gracefully, and dealing with unexpected errors. These examples provide a practical demonstration of how Polly's resilience strategies can be applied to enhance the stability and reliability of .NET applications under varying conditions.

Key Methods in AsyncMockService

  • ExampleMethodAsync: Demonstrates continuous work that checks for cancellation requests, simulating a task that can be stopped gracefully using cancellation tokens.
  • LongRunningCancellableOperation: A long-running task that loops for a specified number of iterations, introducing delays and checking for cancellations. This method throws a TaskCanceledException when a cancellation is requested, illustrating proper cancellation handling in async operations.
  • LongRunningOperation: Simulates a long-running operation without cancellation support, useful for testing how Polly handles retries on operations that do not natively support cancellation.
  • LongRunningOperationWithCancellationTokenAsync: An advanced method that combines long-running operations with a cancellation token using a TaskCompletionSource. This pattern ensures that operations can be cancelled properly and results can be returned or cancelled based on task completion.
  • LongRunningTask: A comprehensive demonstration of handling long-running tasks with cancellation, exception management, and performance logging. This method showcases how to log progress, handle exceptions, and respect cancellation requests, making it ideal for testing Polly's circuit breakers and retry logic.
  • PerformTaskAsync: A utility method that performs a unit of work with optional delays and error simulation. It checks for cancellation requests and can throw exceptions to demonstrate error handling.

The AsyncMockService provides a versatile set of methods that simulate real-world async scenarios, making it an excellent tool for demonstrating how Polly's resilience strategies can be applied to manage transient faults, cancellations, and errors effectively. Whether you are testing retries, circuit breakers, or timeout strategies, the AsyncMockService offers practical examples to explore and refine your approach to building resilient .NET applications.

Learn More About AsyncMockService on GitHub repository for WebSpark