Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Docs] Add docs for individual resilience strategies #1553

Merged
merged 5 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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. |
martincostello marked this conversation as resolved.
Show resolved Hide resolved
| `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 open before attempting to reset. |
martincostello marked this conversation as resolved.
Show resolved Hide resolved
| `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