Skip to content
Community Question

How can the Polly resilience framework be used in .NET applications to handle retries, timeouts, circuit breakers, and transient failures?

Share knowledge. Learn from experts. Build together.

Question

Enterprise applications frequently communicate with APIs, databases, and cloud services where temporary failures can occur. Explore how Polly-based resilience strategies can handle transient errors using retries, timeouts, circuit breakers, fallback strategies, and controlled execution policies. Learn why blindly retrying every exception can make an outage worse and how resilience should be designed around the type of dependency and failure. These concepts are highly relevant for .NET developers building reliable cloud-native applications.
122 Views Community Discussion

Answers

A production application cannot assume that every dependency will always respond successfully.

External APIs can experience:

  • Temporary network failures
  • HTTP 429 responses
  • Timeouts
  • Service interruptions
  • Slow responses
  • Connection failures
  • Temporary database or service unavailability

A resilient application should distinguish between a transient failure and a permanent failure.

Polly is a widely used .NET resilience and transient-fault-handling library. Microsoft describes it as supporting strategies including retry, circuit breaker, timeout, rate limiting, fallback and hedging. Current Microsoft guidance uses the Microsoft.Extensions.Resilience and Microsoft.Extensions.Http.Resilience packages, which are built on Polly; the older Microsoft.Extensions.Http.Polly package is deprecated.

Retry

Retry means:

"The operation failed, but the failure may be temporary, so try again."

For example:

API Call
   ↓
Failure
   ↓
Wait
   ↓
Retry
   ↓
Success

Retries are appropriate for selected transient failures.

They should not blindly retry every exception.

For example:

401 Unauthorized

usually requires an authentication/configuration correction, not repeated retries.

Similarly, a validation error such as:

400 Bad Request

normally should not be retried unchanged.

Exponential backoff

A common strategy is to progressively increase the delay:

Attempt 1 → immediate
Attempt 2 → short delay
Attempt 3 → longer delay
Attempt 4 → longer delay

This prevents thousands of clients from repeatedly hitting an already unhealthy service.

For distributed systems, jitter can also help avoid synchronized retry storms.

Timeout

A timeout establishes:

"How long are we willing to wait?"

Without timeouts, an application may accumulate blocked requests:

Request 1 → waiting
Request 2 → waiting
Request 3 → waiting
Request 4 → waiting
...

Eventually, application resources can become exhausted.

A timeout creates an explicit boundary.

Circuit breaker

The circuit breaker is one of the most important resilience concepts.

Imagine an external payment service is unavailable.

Without a circuit breaker:

Application
    ↓
Payment API
    ↓
Failure

Application
    ↓
Payment API
    ↓
Failure

Application
    ↓
Payment API
    ↓
Failure

The application keeps hammering the unhealthy dependency.

With a circuit breaker:

Normal
  ↓
Failures increase
  ↓
Circuit opens
  ↓
Calls rejected quickly
  ↓
Recovery period
  ↓
Test request
  ↓
Circuit closes if healthy

This protects both systems.

Resilience strategies work together

Modern .NET resilience pipelines can combine multiple strategies.

Microsoft's current HTTP standard resilience configuration includes strategies such as rate limiting/bulkhead protection, total request timeout, retry, circuit breaker and per-attempt timeout. The order in which resilience strategies are configured matters.

Conceptually:

Incoming Request
      ↓
Concurrency / Rate Protection
      ↓
Total Timeout
      ↓
Retry
      ↓
Circuit Breaker
      ↓
Attempt Timeout
      ↓
External API

The important lesson is:

Resilience is a system design, not simply "add retry".

Retry can actually make an outage worse

This is a critical production consideration.

Suppose 10,000 requests fail simultaneously.

If every request retries three times immediately:

10,000 original requests
+
30,000 retries
=
40,000 requests

The dependency may become even more overloaded.

Therefore, enterprise retry design should consider:

  • Which errors are transient
  • Maximum retry count
  • Backoff
  • Jitter
  • Request idempotency
  • Rate limiting
  • Timeout
  • Circuit breaking
  • Dependency capacity

Idempotency matters

Consider:

POST /payments

If the server processed the payment but the response was lost, a client might retry.

That could potentially create a duplicate operation.

Therefore, developers must understand whether the operation is safe to retry.

Resilience mechanisms cannot compensate for a fundamentally non-idempotent integration design.

Practical example

Imagine a .NET API calling an external employee service:

.NET API
   ↓
HttpClient
   ↓
Resilience Pipeline
   ↓
Employee API

The design could apply:

  • Timeout for slow requests
  • Retry for appropriate transient failures
  • Circuit breaker for repeated failures
  • Rate/concurrency protection
  • Logging and telemetry

The goal is not:

"Never fail."

The goal is:

Fail predictably, recover where possible, and protect the application and its dependencies when recovery is not possible.

Your Answer

Connect