Skip to content

Commit

Permalink
[Docs] Add docs for individual resilience strategies (#1553)
Browse files Browse the repository at this point in the history
  • Loading branch information
martintmk committed Sep 6, 2023
1 parent 3d2c3f0 commit 7d6f669
Show file tree
Hide file tree
Showing 16 changed files with 553 additions and 82 deletions.
90 changes: 21 additions & 69 deletions README_V8.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,20 +111,21 @@ Polly categorizes resilience strategies into two main groups:

| Strategy | Reactive | Premise | AKA | How does the strategy mitigate?|
| ------------- | --- | ------------- |:-------------: |------------- |
|**Retry** <br/>(strategy family)<br/><sub>([quickstart](#retry)&nbsp;;&nbsp;[deep](https://github.com/App-vNext/Polly/wiki/Retry))</sub> |Yes|Many faults are transient and may self-correct after a short delay.| *Maybe it's just a blip* | Allows configuring automatic retries. |
|**Circuit-breaker**<br/>(strategy family)<br/><sub>([quickstart](#circuit-breaker)&nbsp;;&nbsp;[deep](https://github.com/App-vNext/Polly/wiki/Circuit-Breaker))</sub>|Yes|When a system is seriously struggling, failing fast is better than making users/callers wait. <br/><br/>Protecting a faulting system from overload can help it recover. | *Stop doing it if it hurts* <br/><br/>*Give that system a break* | Breaks the circuit (blocks executions) for a period, when faults exceed some pre-configured threshold. |
|**Timeout**<br/><sub>([quickstart](#timeout)&nbsp;;&nbsp;[deep](https://github.com/App-vNext/Polly/wiki/Timeout))</sub>|No|Beyond a certain wait, a success result is unlikely.| *Don't wait forever* |Guarantees the caller won't have to wait beyond the timeout. |
|**Rate Limiter**<br/><sub>([quickstart](#rate-limiter)&nbsp;;&nbsp;[deep](https://github.com/App-vNext/Polly/wiki/Rate-Limit))</sub>|No|Limiting the rate a system handles requests is another way to control load. <br/><br/> This can apply to the way your system accepts incoming calls, and/or to the way you call downstream services. | *Slow down a bit, will you?* |Constrains executions to not exceed a certain rate. |
|**Fallback**<br/><sub>([quickstart](#fallback)&nbsp;;&nbsp;[deep](https://github.com/App-vNext/Polly/wiki/Fallback))</sub>|Yes|Things will still fail - plan what you will do when that happens.| *Degrade gracefully* |Defines an alternative value to be returned (or action to be executed) on failure. |
|**Hedging**<br/><sub>([quickstart](#hedging)&nbsp;;&nbsp;[deep](https://github.com/App-vNext/Polly/wiki/TODO))</sub>|Yes|Things can be slow sometimes, plan what you will do when that happens.| *Hedge your bets* | Executes parallel actions when things are slow and waits for the fastest one. |
|**Retry** <br/>(strategy family)<br/><sub>([quickstart](#retry)&nbsp;;&nbsp;[deep](docs/strategies/retry.md))</sub> |Yes|Many faults are transient and may self-correct after a short delay.| *Maybe it's just a blip* | Allows configuring automatic retries. |
|**Circuit-breaker**<br/>(strategy family)<br/><sub>([quickstart](#circuit-breaker)&nbsp;;&nbsp;[deep](docs/strategies/circuit-breaker.md))</sub>|Yes|When a system is seriously struggling, failing fast is better than making users/callers wait. <br/><br/>Protecting a faulting system from overload can help it recover. | *Stop doing it if it hurts* <br/><br/>*Give that system a break* | Breaks the circuit (blocks executions) for a period, when faults exceed some pre-configured threshold. |
|**Timeout**<br/><sub>([quickstart](#timeout)&nbsp;;&nbsp;[deep](docs/strategies/timeout.md))</sub>|No|Beyond a certain wait, a success result is unlikely.| *Don't wait forever* |Guarantees the caller won't have to wait beyond the timeout. |
|**Rate Limiter**<br/><sub>([quickstart](#rate-limiter)&nbsp;;&nbsp;[deep](docs/strategies/rate-limiter.md))</sub>|No|Limiting the rate a system handles requests is another way to control load. <br/><br/> This can apply to the way your system accepts incoming calls, and/or to the way you call downstream services. | *Slow down a bit, will you?* |Constrains executions to not exceed a certain rate. |
|**Fallback**<br/><sub>([quickstart](#fallback)&nbsp;;&nbsp;[deep](docs/strategies/fallback.md))</sub>|Yes|Things will still fail - plan what you will do when that happens.| *Degrade gracefully* |Defines an alternative value to be returned (or action to be executed) on failure. |
|**Hedging**<br/><sub>([quickstart](#hedging)&nbsp;;&nbsp;[deep](docs/strategies/hedging.md))</sub>|Yes|Things can be slow sometimes, plan what you will do when that happens.| *Hedge your bets* | Executes parallel actions when things are slow and waits for the fastest one. |

Visit [resilience strategies](docs/resilience-strategies.md) docs to explore how to configure individual resilience strategies in more detail.

### Retry

<!-- snippet: retry -->
```cs
// Add retry using the default options
// Add retry using the default options.
// See https://github.com/App-vNext/Polly/blob/main/docs/strategies/retry.md#defaults for default values.
new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions());

// For instant retries with no delay
Expand Down Expand Up @@ -200,13 +201,14 @@ new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions
```
<!-- endSnippet -->

If all retries fail, a retry strategy rethrows the final exception back to the calling code. For more details visit the [retry strategy documentation](https://github.com/App-vNext/Polly/wiki/Retry).
If all retries fail, a retry strategy rethrows the final exception back to the calling code. For more details visit the [retry strategy documentation](docs/strategies/retry.md).

### Circuit Breaker

<!-- snippet: circuit-breaker -->
```cs
// Add circuit breaker with default options.
// See https://github.com/App-vNext/Polly/blob/main/docs/strategies/circuit-breaker.md#defaults for default values.
new ResiliencePipelineBuilder().AddCircuitBreaker(new CircuitBreakerStrategyOptions());

// Add circuit breaker with customized options:
Expand Down Expand Up @@ -260,23 +262,13 @@ await manualControl.CloseAsync();
```
<!-- endSnippet -->

The Circuit Breaker strategy prevents execution by throwing a `BrokenCircuitException` when the circuit is open. For more details, refer to the [Circuit-Breaker documentation on GitHub](https://github.com/App-vNext/Polly/wiki/Advanced-Circuit-Breaker).

> [!NOTE]
> Be aware that the Circuit Breaker strategy [rethrows all exceptions](https://github.com/App-vNext/Polly/wiki/Circuit-Breaker#exception-handling), including those that are handled. A Circuit Breaker's role is to monitor faults and break the circuit when a certain threshold is reached; it does not manage retries. Combine the Circuit Breaker with a Retry strategy if needed.
For more insights on the Circuit Breaker pattern, you can visit:

- [Making the Netflix API More Resilient](https://techblog.netflix.com/2011/12/making-netflix-api-more-resilient.html)
- [Circuit Breaker by Martin Fowler](https://martinfowler.com/bliki/CircuitBreaker.html)
- [Circuit Breaker Pattern by Microsoft](https://msdn.microsoft.com/en-us/library/dn589784.aspx)
- [Original Circuit Breaking Article](https://web.archive.org/web/20160106203951/http://thatextramile.be/blog/2008/05/the-circuit-breaker)
For more details, refer to the [Circuit-Breaker documentation](docs/strategies/circuit-breaker.md).

### Fallback

<!-- snippet: fallback -->
```cs
// Use a fallback/substitute value if an operation fails.
// Add a fallback/substitute value if an operation fails.
new ResiliencePipelineBuilder<UserAvatar>()
.AddFallback(new FallbackStrategyOptions<UserAvatar>
{
Expand Down Expand Up @@ -321,18 +313,19 @@ new ResiliencePipelineBuilder<UserAvatar>()
```
<!-- endSnippet -->

For more details, refer to the [Fallback documentation](https://github.com/App-vNext/Polly/wiki/Fallback).
For more details, refer to the [Fallback documentation](docs/strategies/fallback.md).

### Hedging

<!-- snippet: Hedging -->
```cs
// Add hedging with default options.
// See https://github.com/App-vNext/Polly/blob/main/docs/strategies/hedging.md#defaults for default values.
new ResiliencePipelineBuilder<HttpResponseMessage>()
.AddHedging(new HedgingStrategyOptions<HttpResponseMessage>());

// Add a customized hedging strategy that retries up to 3 times if the execution
// takes longer than 1 second or if it fails due to an exception or returns an HTTP 500 Internal Server Error.
// takes longer than 1 second or if it fails due to an exception or returns an HTTP 500 Internal Server Error.
new ResiliencePipelineBuilder<HttpResponseMessage>()
.AddHedging(new HedgingStrategyOptions<HttpResponseMessage>
{
Expand Down Expand Up @@ -364,15 +357,16 @@ new ResiliencePipelineBuilder<HttpResponseMessage>()
```
<!-- endSnippet -->

If all hedged attempts fail, the hedging strategy will either re-throw the last exception or return the final failed result to the caller. For more information, refer to the [hedging strategy documentation](docs/hedging.md).
If all hedged attempts fail, the hedging strategy will either re-throw the last exception or return the final failed result to the caller. For more information, refer to the [hedging strategy documentation](docs/strategies/hedging.md).

### Timeout

The timeout resilience strategy assumes delegates you execute support [co-operative cancellation](https://learn.microsoft.com/dotnet/standard/threading/cancellation-in-managed-threads). You must use `Execute/Async(...)` overloads taking a `CancellationToken`, and the executed delegate must honor that `CancellationToken`.

<!-- snippet: timeout -->
```cs
// To add timeout using the default options
// Add timeout using the default options.
// See https://github.com/App-vNext/Polly/blob/main/docs/strategies/timeout.md#defaults for default values.
new ResiliencePipelineBuilder()
.AddTimeout(new TimeoutStrategyOptions());

Expand Down Expand Up @@ -409,31 +403,14 @@ new ResiliencePipelineBuilder()
```
<!-- endSnippet -->

Example execution:

<!-- snippet: timeout-execution -->
```cs
var pipeline = new ResiliencePipelineBuilder()
.AddTimeout(TimeSpan.FromSeconds(3))
.Build();

HttpResponseMessage httpResponse = await pipeline.ExecuteAsync(
async ct =>
{
// Execute a delegate that takes a CancellationToken as an input parameter.
return await httpClient.GetAsync(endpoint, ct);
},
cancellationToken);
```
<!-- endSnippet -->

Timeout strategies throw `TimeoutRejectedException` when a timeout occurs. For more details see [Timeout strategy documentation](https://github.com/App-vNext/Polly/wiki/Timeout).
Timeout strategies throw `TimeoutRejectedException` when a timeout occurs. For more details see [Timeout strategy documentation](docs/strategies/timeout.md).

### Rate Limiter

<!-- snippet: rate-limiter -->
```cs
// Add rate limiter with default options.
// See https://github.com/App-vNext/Polly/blob/main/docs/strategies/rate-limiter.md#defaults for default values.
new ResiliencePipelineBuilder()
.AddRateLimiter(new RateLimiterStrategyOptions());

Expand Down Expand Up @@ -475,32 +452,7 @@ new ResiliencePipelineBuilder()
```
<!-- endSnippet -->

Example execution:

<!-- snippet: rate-limiter-execution -->
```cs
var pipeline = new ResiliencePipelineBuilder().AddConcurrencyLimiter(100, 50).Build();

try
{
// Execute an asynchronous text search operation.
var result = await pipeline.ExecuteAsync(
token => TextSearchAsync(query, token),
cancellationToken);
}
catch (RateLimiterRejectedException ex)
{
// Handle RateLimiterRejectedException,
// that can optionally contain information about when to retry.
if (ex.RetryAfter is TimeSpan retryAfter)
{
Console.WriteLine($"Retry After: {retryAfter}");
}
}
```
<!-- endSnippet -->

Rate limiter strategy throws `RateLimiterRejectedException` if execution is rejected. For more details see [Rate Limiter strategy documentation](https://github.com/App-vNext/Polly/wiki/Rate-Limit).
Rate limiter strategy throws `RateLimiterRejectedException` if execution is rejected. For more details see [Rate Limiter strategy documentation](docs/strategies/rate-limiter.md).

## Next steps

Expand Down
9 changes: 9 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

If you're already familiar with the [basic features](../README.md) of Polly, delve deeper into its advanced functionalities here.

## Resilience strategies

- [Retry](strategies/retry.md): Allows for the automated re-execution of failed operations according to predefined conditions.
- [Circuit Breaker](strategies/circuit-breaker.md): Temporarily halts all operations when a defined threshold of failures is exceeded, in order to prevent further issues.
- [Fallback](strategies/fallback.md): Provides a backup value or executes an alternative action when the primary operation fails, ensuring graceful degradation.
- [Hedging](strategies/hedging.md): Initiates multiple identical operations in parallel when performance lags, and returns the result of the fastest-completing operation.
- [Timeout](strategies/timeout.md): Sets a maximum time limit for an operation to complete, preventing indefinite waiting.
- [Rate Limiter](strategies/rate-limiter.md): Regulates the frequency of operations to ensure they do not exceed a set rate, thereby maintaining system stability.

## Topics

- [General](general.md): General information about Polly.
Expand Down
99 changes: 99 additions & 0 deletions docs/strategies/circuit-breaker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Circuit breaker resilience strategy

## About

- **Options**:
- [`CircuitBreakerStrategyOptions`](../../src/Polly.Core/CircuitBreaker/CircuitBreakerStrategyOptions.cs)
- [`CircuitBreakerStrategyOptions<T>`](../../src/Polly.Core/CircuitBreaker/CircuitBreakerStrategyOptions.TResult.cs)
- **Extensions**: `AddCircuitBreaker`
- **Strategy Type**: Reactive
- **Exceptions**:
- `BrokenCircuitException`: Thrown when a circuit is broken and the action could not be executed.
- `IsolatedCircuitException`: Thrown when a circuit is isolated (held open) by manual override.

> [!NOTE]
> Version 8 documentation for this strategy has not yet been migrated. For more information on circuit breaker concepts and behavior, refer to the [older documentation](https://github.com/App-vNext/Polly/wiki/Circuit-Breaker).
> [!NOTE]
> Be aware that the Circuit Breaker strategy [rethrows all exceptions](https://github.com/App-vNext/Polly/wiki/Circuit-Breaker#exception-handling), including those that are handled. A Circuit Breaker's role is to monitor faults and break the circuit when a certain threshold is reached; it does not manage retries. Combine the Circuit Breaker with a Retry strategy if needed.
## Usage

<!-- snippet: circuit-breaker -->
```cs
// Add circuit breaker with default options.
// See https://github.com/App-vNext/Polly/blob/main/docs/strategies/circuit-breaker.md#defaults for default values.
new ResiliencePipelineBuilder().AddCircuitBreaker(new CircuitBreakerStrategyOptions());

// Add circuit breaker with customized options:
//
// The circuit will break if more than 50% of actions result in handled exceptions,
// within any 10-second sampling duration, and at least 8 actions are processed.
new ResiliencePipelineBuilder().AddCircuitBreaker(new CircuitBreakerStrategyOptions
{
FailureRatio = 0.5,
SamplingDuration = TimeSpan.FromSeconds(10),
MinimumThroughput = 8,
BreakDuration = TimeSpan.FromSeconds(30),
ShouldHandle = new PredicateBuilder().Handle<SomeExceptionType>()
});

// Handle specific failed results for HttpResponseMessage:
new ResiliencePipelineBuilder<HttpResponseMessage>()
.AddCircuitBreaker(new CircuitBreakerStrategyOptions<HttpResponseMessage>
{
ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
.Handle<SomeExceptionType>()
.HandleResult(response => response.StatusCode == HttpStatusCode.InternalServerError)
});

// Monitor the circuit state, useful for health reporting:
var stateProvider = new CircuitBreakerStateProvider();

new ResiliencePipelineBuilder<HttpResponseMessage>()
.AddCircuitBreaker(new() { StateProvider = stateProvider })
.Build();

/*
CircuitState.Closed - Normal operation; actions are executed.
CircuitState.Open - Circuit is open; actions are blocked.
CircuitState.HalfOpen - Recovery state after break duration expires; actions are permitted.
CircuitState.Isolated - Circuit is manually held open; actions are blocked.
*/

// Manually control the Circuit Breaker state:
var manualControl = new CircuitBreakerManualControl();

new ResiliencePipelineBuilder()
.AddCircuitBreaker(new() { ManualControl = manualControl })
.Build();

// Manually isolate a circuit, e.g., to isolate a downstream service.
await manualControl.IsolateAsync();

// Manually close the circuit to allow actions to be executed again.
await manualControl.CloseAsync();
```
<!-- endSnippet -->

## Defaults

| Property | Default Value | Description |
| ------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `ShouldHandle` | Predicate that handles all exceptions except `OperationCanceledException`. | Specifies which results and exceptions are managed by the circuit breaker strategy. |
| `FailureRatio` | 0.1 | The ratio of failures to successes that will cause the circuit to break/open. |
| `MinimumThroughput` | 100 | The minimum number of actions that must occur in the circuit within a specific time slice. |
| `SamplingDuration` | 30 seconds | The time period over which failure ratios are calculated. |
| `BreakDuration` | 5 seconds | The time period for which the circuit will remain broken/open before attempting to reset. |
| `OnClosed` | `null` | Event triggered when the circuit transitions to the `Closed` state. |
| `OnOpened` | `null` | Event triggered when the circuit transitions to the `Opened` state. |
| `OnHalfOpened` | `null` | Event triggered when the circuit transitions to the `HalfOpened` state. |
| `ManualControl` | `null` | Allows for manual control to isolate or close the circuit. |
| `StateProvider` | `null` | Enables the retrieval of the current state of the circuit. |

## Resources

- [Making the Netflix API More Resilient](https://techblog.netflix.com/2011/12/making-netflix-api-more-resilient.html)
- [Circuit Breaker by Martin Fowler](https://martinfowler.com/bliki/CircuitBreaker.html)
- [Circuit Breaker Pattern by Microsoft](https://msdn.microsoft.com/en-us/library/dn589784.aspx)
- [Original Circuit Breaking Article](https://web.archive.org/web/20160106203951/http://thatextramile.be/blog/2008/05/the-circuit-breaker)
Loading

0 comments on commit 7d6f669

Please sign in to comment.