From 1296756c02f7dae7f11b93188fc9c525b80319d5 Mon Sep 17 00:00:00 2001 From: martintmk <103487740+martintmk@users.noreply.github.com> Date: Wed, 27 Sep 2023 16:09:10 +0200 Subject: [PATCH] Release v8 docs (#1599) --- README.md | 1257 +++++++-------------------- README_V8.md | 439 ---------- docs/advanced/resilience-context.md | 3 - docs/general.md | 3 - docs/index.md | 3 - docs/pipelines/index.md | 3 - docs/strategies/index.md | 3 - package-readme.md | 2 +- 8 files changed, 313 insertions(+), 1400 deletions(-) delete mode 100644 README_V8.md diff --git a/README.md b/README.md index 696a131ac4a..0b4bd5a549d 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,20 @@ -> **Note** -> -> **Important Announcement: Architectural changes in v8** -> -> Major performance improvements are on the way! Please see our [blog post](https://www.thepollyproject.org/2023/03/03/we-want-your-feedback-introducing-polly-v8/) to learn more and provide feedback in the [related GitHub issue](https://github.com/App-vNext/Polly/issues/1048). -> -> :rotating_light::rotating_light: **Polly v8 feature-complete!** :rotating_light::rotating_light: -> - Polly v8 Beta 2 is now available on [NuGet.org](https://www.nuget.org/packages/Polly/8.0.0-beta.2). -> - The Beta 2 version is considered feature-complete and the public API surface is stable. -> - Explore the [v8 documentation][polly-docs]. - # 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, Rate-limiting and Fallback in a fluent and thread-safe manner. - -Polly targets .NET Standard 1.1 ([coverage](https://docs.microsoft.com/en-us/dotnet/standard/net-standard#net-implementation-support): .NET Core 1.0, Mono, Xamarin, UWP, WP8.1+) and .NET Standard 2.0+ ([coverage](https://docs.microsoft.com/en-us/dotnet/standard/net-standard#net-implementation-support): .NET Core 2.0+, .NET Core 3.0, and later Mono, Xamarin and UWP targets). The NuGet package also includes direct targets for .NET Framework 4.6.1 and 4.7.2. - -For versions supporting earlier targets such as .NET4.0 and .NET3.5, see the [supported targets](https://github.com/App-vNext/Polly/wiki/Supported-targets) grid. +Polly is a .NET resilience and transient-fault-handling library that allows developers to express resilience strategies such as Retry, Circuit Breaker, Hedging, Timeout, Rate Limiter and Fallback in a fluent and thread-safe manner. [](https://www.dotnetfoundation.org/) We are a member of the [.NET Foundation](https://www.dotnetfoundation.org/about)! **Keep up to date with new feature announcements, tips & tricks, and other news through [www.thepollyproject.org](https://www.thepollyproject.org)** -[![Build status](https://github.com/App-vNext/Polly/workflows/build/badge.svg?branch=main&event=push)](https://github.com/App-vNext/Polly/actions?query=workflow%3Abuild+branch%3Amain+event%3Apush) [![Code coverage](https://codecov.io/gh/App-vNext/Polly/branch/main/graph/badge.svg)](https://codecov.io/gh/App-vNext/Polly) - - +[![Build status](https://github.com/App-vNext/Polly/workflows/build/badge.svg?branch=main&event=push)](https://github.com/App-vNext/Polly/actions?query=workflow%3Abuild+branch%3Amain+event%3Apush) [![Code coverage](https://codecov.io/gh/App-vNext/Polly/branch/main/graph/badge.svg)](https://codecov.io/gh/App-vNext/Polly) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/App-vNext/Polly/badge)](https://securityscorecards.dev/viewer/?uri=github.com/App-vNext/Polly) ![Polly logo](https://raw.github.com/App-vNext/Polly/main/Polly-Logo.png) -## NuGet Packages - -### Polly v7 +> [!IMPORTANT] +> This documentation describes the new Polly v8 API. If you are using the v7 API, please refer to the [previous version](https://github.com/App-vNext/Polly/tree/7.2.4) of the documentation. -[![NuGet vesion](https://buildstats.info/nuget/Polly?includePreReleases=false)](https://www.nuget.org/packages/Polly/) - -### Polly v8 Pre-releases +## NuGet Packages | **Package** | **Latest Version** | |:--|:--| @@ -48,1025 +26,414 @@ We are a member of the [.NET Foundation](https://www.dotnetfoundation.org/about) ## Documentation -Read the official documentation at [pollydocs.org][polly-docs]. +This README aims to give a quick overview of some Polly features - including enough to get you started with any resilience strategy. For deeper detail on any resilience strategy, and many other aspects of Polly, be sure also to check out the [documentation][polly-docs]. -## Get Started +## Quick start -### Installing via the .NET SDK +To use Polly, you must provide a callback and execute it using [**resilience pipeline**](https://www.pollydocs.org/pipelines). A resilience pipeline is a combination of one or more [**resilience strategies**](https://www.pollydocs.org/strategies) such as retry, timeout, and rate limiter. Polly uses **builders** to integrate these strategies into a pipeline. + +To get started, first add the [Polly.Core](https://www.nuget.org/packages/Polly.Core/) package to your project by running the following command: ```sh -dotnet add package Polly +dotnet add package Polly.Core ``` -### Supported targets - -For details of supported compilation targets by version, see the [supported targets](https://github.com/App-vNext/Polly/wiki/Supported-targets) grid. +You can create a `ResiliencePipeline` using the `ResiliencePipelineBuilder` class as shown below: -### Role of the readme and the wiki + +```cs +// Create a instance of builder that exposes various extensions for adding resilience strategies +ResiliencePipeline pipeline = new ResiliencePipelineBuilder() + .AddRetry(new RetryStrategyOptions()) // Add retry using the default options + .AddTimeout(TimeSpan.FromSeconds(10)) // Add 10 second timeout + .Build(); // Builds the resilience pipeline -This ReadMe aims to give a quick overview of all Polly features - including enough to get you started with any policy. For deeper detail on any policy, and many other aspects of Polly, be sure also to check out the [wiki documentation](https://github.com/App-vNext/Polly/wiki). +// Execute the pipeline asynchronously +await pipeline.ExecuteAsync(static async cancellationToken => { /*Your custom logic here */ }, cancellationToken); +``` + -## Release notes +### Dependency injection -* The [changelog](https://github.com/App-vNext/Polly/blob/main/CHANGELOG.md) describes changes by release. -* We tag Pull Requests and Issues with [milestones](https://github.com/App-vNext/Polly/milestones) which match to NuGet package release numbers. -* Breaking changes are called out in the wiki ([v7](https://github.com/App-vNext/Polly/wiki/Polly-v7-breaking-changes); [v6](https://github.com/App-vNext/Polly/wiki/Polly-v6-breaking-changes)) with simple notes on any necessary steps to upgrade. +If you prefer to define resilience pipelines using [`IServiceCollection`](https://learn.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.iservicecollection), you'll need to install the [Polly.Extensions](https://www.nuget.org/packages/Polly.Extensions/) package: -## Resilience policies +```sh +dotnet add package Polly.Extensions +``` -Polly offers multiple resilience policies. +You can then define your resilience pipeline using the `AddResiliencePipeline(...)` extension method as shown: -In addition to the detailed pages on each policy, an [introduction to the role of each policy in resilience engineering](https://github.com/App-vNext/Polly/wiki/Transient-fault-handling-and-proactive-resilience-engineering) is also provided in the wiki. + +```cs +var services = new ServiceCollection(); -|Policy| Premise | Aka| How does the policy mitigate?| -| ------------- | ------------- |:-------------: |------------- | -|**Retry**
(policy family)
([quickstart](#retry) ; [deep](https://github.com/App-vNext/Polly/wiki/Retry))|Many faults are transient and may self-correct after a short delay.| "Maybe it's just a blip" | Allows configuring automatic retries. | -|**Circuit-breaker**
(policy family)
([quickstart](#circuit-breaker) ; [deep](https://github.com/App-vNext/Polly/wiki/Circuit-Breaker))|When a system is seriously struggling, failing fast is better than making users/callers wait.

Protecting a faulting system from overload can help it recover. | "Stop doing it if it hurts"

"Give that system a break" | Breaks the circuit (blocks executions) for a period, when faults exceed some pre-configured threshold. | -|**Timeout**
([quickstart](#timeout) ; [deep](https://github.com/App-vNext/Polly/wiki/Timeout))|Beyond a certain wait, a success result is unlikely.| "Don't wait forever" |Guarantees the caller won't have to wait beyond the timeout. | -|**Bulkhead Isolation**
([quickstart](#bulkhead) ; [deep](https://github.com/App-vNext/Polly/wiki/Bulkhead))|When a process faults, multiple failing calls can stack up (if unbounded) and can easily swamp resource (threads/ CPU/ memory) in a host.

This can affect performance more widely by starving other operations of resource, bringing down the host, or causing cascading failures upstream. | "One fault shouldn't sink the whole ship" |Constrains the governed actions to a fixed-size resource pool, isolating their potential to affect others. | -|**Rate-limit**
([quickstart](#rate-limit) ; [deep](https://github.com/App-vNext/Polly/wiki/Rate-Limit))|Limiting the rate a system handles requests is another way to control load.

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. | -|**Cache**
([quickstart](#cache) ; [deep](https://github.com/App-vNext/Polly/wiki/Cache))|Some proportion of requests may be similar.| "You've asked that one before" |Provides a response from cache if known.

Stores responses automatically in cache, when first retrieved. | -|**Fallback**
([quickstart](#fallback) ; [deep](https://github.com/App-vNext/Polly/wiki/Fallback))|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. | -|**PolicyWrap**
([quickstart](#policywrap) ; [deep](https://github.com/App-vNext/Polly/wiki/PolicyWrap))|Different faults require different strategies; resilience means using a combination.| "Defence in depth" |Allows any of the above policies to be combined flexibly. | +// Define a resilience pipeline with the name "my-pipeline" +services.AddResiliencePipeline("my-pipeline", builder => +{ + builder + .AddRetry(new RetryStrategyOptions()) + .AddTimeout(TimeSpan.FromSeconds(10)); +}); -## Usage – fault-handling, reactive policies +// Build the service provider +IServiceProvider serviceProvider = services.BuildServiceProvider(); -Fault-handling policies handle specific exceptions thrown by, or results returned by, the delegates you execute through the policy. +// Retrieve ResiliencePipelineProvider that caches and dynamically creates the resilience pipelines +var pipelineProvider = serviceProvider.GetRequiredService>(); -### Step 1 : Specify the exceptions/faults you want the policy to handle +// Retrieve resilience pipeline using the name it was registered with +ResiliencePipeline pipeline = pipelineProvider.GetPipeline("my-pipeline"); -```csharp -// Single exception type -Policy - .Handle() +// Execute the pipeline +await pipeline.ExecuteAsync(static async token => +{ + // Your custom logic here +}); +``` + -// Single exception type with condition -Policy - .Handle(ex => ex.Number == 1205) +## Resilience strategies -// Multiple exception types -Policy - .Handle() - .Or() +Polly provides a variety of resilience strategies. Alongside the comprehensive guides for each strategy, the wiki also includes an [overview of the role each strategy plays in resilience engineering](https://github.com/App-vNext/Polly/wiki/Transient-fault-handling-and-proactive-resilience-engineering). -// Multiple exception types with condition -Policy - .Handle(ex => ex.Number == 1205) - .Or(ex => ex.ParamName == "example") +Polly categorizes resilience strategies into two main groups: -// Inner exceptions of ordinary exceptions or AggregateException, with or without conditions -// (HandleInner matches exceptions at both the top-level and inner exceptions) -Policy - .HandleInner() - .OrInner(ex => ex.CancellationToken != myToken) -``` +- **Reactive**: These strategies handle specific exceptions that are thrown, or results that are returned, by the callbacks executed through the strategy. +- **Proactive**: Unlike reactive strategies, proactive strategies do not focus on handling errors by the callbacks might throw or return. They can make pro-active decisions to cancel or reject the execution of callbacks (e.g., using a rate limiter or a timeout resilience strategy). -### Step 1b: (optionally) Specify return results you want to handle - -From Polly v4.3.0 onwards, policies wrapping calls returning a `TResult` can also handle `TResult` return values: - -```csharp -// Handle return value with condition -Policy - .HandleResult(r => r.StatusCode == HttpStatusCode.NotFound) - -// Handle multiple return values -Policy - .HandleResult(r => r.StatusCode == HttpStatusCode.InternalServerError) - .OrResult(r => r.StatusCode == HttpStatusCode.BadGateway) - -// Handle primitive return values (implied use of .Equals()) -Policy - .HandleResult(HttpStatusCode.InternalServerError) - .OrResult(HttpStatusCode.BadGateway) - -// Handle both exceptions and return values in one policy -HttpStatusCode[] httpStatusCodesWorthRetrying = { - HttpStatusCode.RequestTimeout, // 408 - HttpStatusCode.InternalServerError, // 500 - HttpStatusCode.BadGateway, // 502 - HttpStatusCode.ServiceUnavailable, // 503 - HttpStatusCode.GatewayTimeout // 504 -}; -HttpResponseMessage result = await Policy - .Handle() - .OrResult(r => httpStatusCodesWorthRetrying.Contains(r.StatusCode)) - .RetryAsync(...) - .ExecuteAsync( /* some Func> */ ) -``` +| Strategy | Reactive | Premise | AKA | How does the strategy mitigate?| +| ------------- | --- | ------------- |:-------------: |------------- | +|**Retry**
(strategy family)
([quickstart](#retry) ; [deep](https://www.pollydocs.org/strategies/retry)) |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**
(strategy family)
([quickstart](#circuit-breaker) ; [deep](https://www.pollydocs.org/strategies/circuit-breaker))|Yes|When a system is seriously struggling, failing fast is better than making users/callers wait.

Protecting a faulting system from overload can help it recover. | *Stop doing it if it hurts*

*Give that system a break* | Breaks the circuit (blocks executions) for a period, when faults exceed some pre-configured threshold. | +|**Timeout**
([quickstart](#timeout) ; [deep](https://www.pollydocs.org/strategies/timeout))|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**
([quickstart](#rate-limiter) ; [deep](https://www.pollydocs.org/strategies/rate-limiter))|No|Limiting the rate a system handles requests is another way to control load.

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**
([quickstart](#fallback) ; [deep](https://www.pollydocs.org/strategies/fallback))|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**
([quickstart](#hedging) ; [deep](https://www.pollydocs.org/strategies/hedging))|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. | -For more information, see [Handling Return Values](#handing-return-values-and-policytresult) at foot of this readme. +Visit [resilience strategies](https://www.pollydocs.org/strategies) docs to explore how to configure individual resilience strategies in more detail. -### Step 2 : Specify how the policy should handle those faults +### Retry -#### Retry + +```cs +// Add retry using the default options. +// See https://www.pollydocs.org/strategies/retry#defaults for defaults. +new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions()); -```csharp -// Retry once -Policy - .Handle() - .Retry() +// For instant retries with no delay +new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions +{ + Delay = TimeSpan.Zero +}); -// Retry multiple times -Policy - .Handle() - .Retry(3) +// For advanced control over the retry behavior, including the number of attempts, +// delay between retries, and the types of exceptions to handle. +new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions +{ + ShouldHandle = new PredicateBuilder().Handle(), + BackoffType = DelayBackoffType.Exponential, + UseJitter = true, // Adds a random factor to the delay + MaxRetryAttempts = 4, + Delay = TimeSpan.FromSeconds(3), +}); -// Retry multiple times, calling an action on each retry -// with the current exception and retry count -Policy - .Handle() - .Retry(3, onRetry: (exception, retryCount) => +// To use a custom function to generate the delay for retries +new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions +{ + MaxRetryAttempts = 2, + DelayGenerator = args => { - // Add logic to be executed before each retry, such as logging - }); + var delay = args.AttemptNumber switch + { + 0 => TimeSpan.Zero, + 1 => TimeSpan.FromSeconds(1), + _ => TimeSpan.FromSeconds(5) + }; + + // This example uses a synchronous delay generator, + // but the API also supports asynchronous implementations. + return new ValueTask(delay); + } +}); -// Retry multiple times, calling an action on each retry -// with the current exception, retry count and context -// provided to Execute() -Policy - .Handle() - .Retry(3, onRetry: (exception, retryCount, context) => +// To extract the delay from the result object +new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions +{ + DelayGenerator = args => { - // Add logic to be executed before each retry, such as logging - }); -``` + if (args.Outcome.Result is HttpResponseMessage responseMessage && + TryGetDelay(responseMessage, out TimeSpan delay)) + { + return new ValueTask(delay); + } + + // Returning null means the retry strategy will use its internal delay for this attempt. + return new ValueTask((TimeSpan?)null); + } +}); -#### Retry forever (until succeeds) - -```csharp - -// Retry forever -Policy - .Handle() - .RetryForever() - -// Retry forever, calling an action on each retry with the -// current exception -Policy - .Handle() - .RetryForever(onRetry: exception => - { - // Add logic to be executed before each retry, such as logging - }); - -// Retry forever, calling an action on each retry with the -// current exception and context provided to Execute() -Policy - .Handle() - .RetryForever(onRetry: (exception, context) => - { - // Add logic to be executed before each retry, such as logging - }); -``` +// To get notifications when a retry is performed +new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions +{ + MaxRetryAttempts = 2, + OnRetry = args => + { + Console.WriteLine("OnRetry, Attempt: {0}", args.AttemptNumber); -`RetryForever` does not actually retry forever; it will retry up to `int.MaxValue` (2147483647) times. Depending on what is done in the policy delegate this may take an exceedingly long time, but the policy will eventually hit `int.MaxValue` retries, get the last exception and stop retrying. - -#### Wait and retry - -```csharp -// Retry, waiting a specified duration between each retry. -// (The wait is imposed on catching the failure, before making the next try.) -Policy - .Handle() - .WaitAndRetry(new[] - { - TimeSpan.FromSeconds(1), - TimeSpan.FromSeconds(2), - TimeSpan.FromSeconds(3) - }); - -// Retry, waiting a specified duration between each retry, -// calling an action on each retry with the current exception -// and duration -Policy - .Handle() - .WaitAndRetry(new[] - { - TimeSpan.FromSeconds(1), - TimeSpan.FromSeconds(2), - TimeSpan.FromSeconds(3) - }, (exception, timeSpan) => { - // Add logic to be executed before each retry, such as logging - }); - -// Retry, waiting a specified duration between each retry, -// calling an action on each retry with the current exception, -// duration and context provided to Execute() -Policy - .Handle() - .WaitAndRetry(new[] - { - TimeSpan.FromSeconds(1), - TimeSpan.FromSeconds(2), - TimeSpan.FromSeconds(3) - }, (exception, timeSpan, context) => { - // Add logic to be executed before each retry, such as logging - }); - -// Retry, waiting a specified duration between each retry, -// calling an action on each retry with the current exception, -// duration, retry count, and context provided to Execute() -Policy - .Handle() - .WaitAndRetry(new[] - { - TimeSpan.FromSeconds(1), - TimeSpan.FromSeconds(2), - TimeSpan.FromSeconds(3) - }, (exception, timeSpan, retryCount, context) => { - // Add logic to be executed before each retry, such as logging - }); - -// Retry a specified number of times, using a function to -// calculate the duration to wait between retries based on -// the current retry attempt (allows for exponential back-off) -// In this case will wait for -// 2 ^ 1 = 2 seconds then -// 2 ^ 2 = 4 seconds then -// 2 ^ 3 = 8 seconds then -// 2 ^ 4 = 16 seconds then -// 2 ^ 5 = 32 seconds -Policy - .Handle() - .WaitAndRetry(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); - -// Retry a specified number of times, using a function to -// calculate the duration to wait between retries based on -// the current retry attempt, calling an action on each retry -// with the current exception, duration and context provided -// to Execute() -Policy - .Handle() - .WaitAndRetry( - 5, - retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), - (exception, timeSpan, context) => { - // Add logic to be executed before each retry, such as logging - } - ); - -// Retry a specified number of times, using a function to -// calculate the duration to wait between retries based on -// the current retry attempt, calling an action on each retry -// with the current exception, duration, retry count, and context -// provided to Execute() -Policy - .Handle() - .WaitAndRetry( - 5, - retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), - (exception, timeSpan, retryCount, context) => { - // Add logic to be executed before each retry, such as logging + // Event handlers can be asynchronous; here, we return an empty ValueTask. + return default; } - ); -``` - -The above code demonstrates how to build common wait-and-retry patterns from scratch, but our community also came up with an awesome contrib to wrap the common cases in helper methods: see [Polly.Contrib.WaitAndRetry](https://github.com/Polly-Contrib/Polly.Contrib.WaitAndRetry/). +}); -For `WaitAndRetry` policies handling Http Status Code 429 Retry-After, see [wiki documentation](https://github.com/App-vNext/Polly/wiki/Retry#retryafter-when-the-response-specifies-how-long-to-wait). +// To keep retrying indefinitely or until success use int.MaxValue. +new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions +{ + MaxRetryAttempts = int.MaxValue, +}); +``` + -#### Wait and retry forever (until succeeds) +If all retries fail, a retry strategy rethrows the final exception back to the calling code. For more details, visit the [retry strategy](https://www.pollydocs.org/strategies/retry) documentation. -```csharp +### Circuit Breaker -// Wait and retry forever -Policy - .Handle() - .WaitAndRetryForever(retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); + +```cs +// Add circuit breaker with default options. +// See https://www.pollydocs.org/strategies/circuit-breaker#defaults for defaults. +new ResiliencePipelineBuilder().AddCircuitBreaker(new CircuitBreakerStrategyOptions()); -// Wait and retry forever, calling an action on each retry with the -// current exception and the time to wait -Policy - .Handle() - .WaitAndRetryForever( - retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), - (exception, timespan) => - { - // Add logic to be executed before each retry, such as logging - }); +// 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() +}); -// Wait and retry forever, calling an action on each retry with the -// current exception, time to wait, and context provided to Execute() -Policy - .Handle() - .WaitAndRetryForever( - retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), - (exception, timespan, context) => +// Handle specific failed results for HttpResponseMessage: +new ResiliencePipelineBuilder() + .AddCircuitBreaker(new CircuitBreakerStrategyOptions { - // Add logic to be executed before each retry, such as logging + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(response => response.StatusCode == HttpStatusCode.InternalServerError) }); -``` - -Similarly to `RetryForever`, `WaitAndRetryForever` only actually retries `int.MaxValue` times. - -If all retries fail, a retry policy rethrows the final exception back to the calling code. - -For more depth see also: [Retry policy documentation on wiki](https://github.com/App-vNext/Polly/wiki/Retry). -#### Circuit Breaker +// Monitor the circuit state, useful for health reporting: +var stateProvider = new CircuitBreakerStateProvider(); -```csharp -// Break the circuit after the specified number of consecutive exceptions -// and keep circuit broken for the specified duration. -Policy - .Handle() - .CircuitBreaker(2, TimeSpan.FromMinutes(1)); - -// Break the circuit after the specified number of consecutive exceptions -// and keep circuit broken for the specified duration, -// calling an action on change of circuit state. -Action onBreak = (exception, timespan) => { ... }; -Action onReset = () => { ... }; -CircuitBreakerPolicy breaker = Policy - .Handle() - .CircuitBreaker(2, TimeSpan.FromMinutes(1), onBreak, onReset); - -// Break the circuit after the specified number of consecutive exceptions -// and keep circuit broken for the specified duration, -// calling an action on change of circuit state, -// passing a context provided to Execute(). -Action onBreak = (exception, timespan, context) => { ... }; -Action onReset = context => { ... }; -CircuitBreakerPolicy breaker = Policy - .Handle() - .CircuitBreaker(2, TimeSpan.FromMinutes(1), onBreak, onReset); - -// Monitor the circuit state, for example for health reporting. -CircuitState state = breaker.CircuitState; +new ResiliencePipelineBuilder() + .AddCircuitBreaker(new() { StateProvider = stateProvider }) + .Build(); /* -CircuitState.Closed - Normal operation. Execution of actions allowed. -CircuitState.Open - The automated controller has opened the circuit. Execution of actions blocked. -CircuitState.HalfOpen - Recovering from open state, after the automated break duration has expired. Execution of actions permitted. Success of subsequent action/s controls onward transition to Open or Closed state. -CircuitState.Isolated - Circuit held manually in an open state. Execution of actions blocked. +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 open (and hold open) a circuit breaker - for example to manually isolate a downstream service. -breaker.Isolate(); -// Reset the breaker to closed state, to start accepting actions again. -breaker.Reset(); -``` - -Circuit-breaker policies block exceptions by throwing `BrokenCircuitException` when the circuit is broken. See: [Circuit-Breaker documentation on wiki](https://github.com/App-vNext/Polly/wiki/Circuit-Breaker). - -Note that circuit-breaker policies [rethrow all exceptions](https://github.com/App-vNext/Polly/wiki/Circuit-Breaker#exception-handling), even handled ones. A circuit-breaker exists to measure faults and break the circuit when too many faults occur, but does not orchestrate retries. Combine a circuit-breaker with a retry policy as needed. - -#### Advanced Circuit Breaker +// Manually control the Circuit Breaker state: +var manualControl = new CircuitBreakerManualControl(); -```csharp -// Break the circuit if, within any period of duration samplingDuration, -// the proportion of actions resulting in a handled exception exceeds failureThreshold, -// provided also that the number of actions through the circuit in the period -// is at least minimumThroughput. +new ResiliencePipelineBuilder() + .AddCircuitBreaker(new() { ManualControl = manualControl }) + .Build(); -Policy - .Handle() - .AdvancedCircuitBreaker( - failureThreshold: 0.5, // Break on >=50% actions result in handled exceptions... - samplingDuration: TimeSpan.FromSeconds(10), // ... over any 10 second period - minimumThroughput: 8, // ... provided at least 8 actions in the 10 second period. - durationOfBreak: TimeSpan.FromSeconds(30) // Break for 30 seconds. - ); +// Manually isolate a circuit, e.g., to isolate a downstream service. +await manualControl.IsolateAsync(); -// Configuration overloads taking state-change delegates are -// available as described for CircuitBreaker above. - -// Circuit state monitoring and manual controls are -// available as described for CircuitBreaker above. +// Manually close the circuit to allow actions to be executed again. +await manualControl.CloseAsync(); ``` + -For more detail see: [Advanced Circuit-Breaker documentation](https://github.com/App-vNext/Polly/wiki/Advanced-Circuit-Breaker) on wiki. - -For more information on the Circuit Breaker pattern in general see: - -+ [Making the Netflix API More Resilient](http://techblog.netflix.com/2011/12/making-netflix-api-more-resilient.html) -+ [Circuit Breaker (Martin Fowler)](http://martinfowler.com/bliki/CircuitBreaker.html) -+ [Circuit Breaker Pattern (Microsoft)](https://msdn.microsoft.com/en-us/library/dn589784.aspx) -+ [Original Circuit Breaking Link](https://web.archive.org/web/20160106203951/http://thatextramile.be/blog/2008/05/the-circuit-breaker) - -#### Fallback +For more details, visit the [circuit breaker strategy](https://www.pollydocs.org/strategies/circuit-breaker) documentation. -```csharp -// Provide a substitute value, if an execution faults. -Policy - .Handle() - .OrResult(null) - .Fallback(UserAvatar.Blank); +### Fallback -// Specify a func to provide a substitute value, if execution faults. -Policy - .Handle() - .OrResult(null) - .Fallback(() => UserAvatar.GetRandomAvatar()); // where: public UserAvatar GetRandomAvatar() { ... } - -// Specify a substitute value or func, calling an action (eg for logging) if the fallback is invoked. -Policy - .Handle() - .Fallback(UserAvatar.Blank, onFallback: (exception, context) => + +```cs +// Add a fallback/substitute value if an operation fails. +new ResiliencePipelineBuilder() + .AddFallback(new FallbackStrategyOptions { - // Add extra logic to be called when the fallback is invoked, such as logging + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(r => r is null), + FallbackAction = args => Outcome.FromResultAsValueTask(UserAvatar.Blank) }); -``` - -For more detail see: [Fallback policy documentation](https://github.com/App-vNext/Polly/wiki/Fallback) on wiki. - -### Step 3 : Execute code through the policy -Execute an `Action`, `Func`, or lambda delegate equivalent, through the policy. The policy governs execution of the code passed to the `.Execute()` (or similar) method. - -> **Note** -> -> The code examples below show defining the policy and executing code through it in the same scope, for simplicity. See the notes after the code examples for other usage patterns. - -```csharp -// Execute an action -var policy = Policy - .Handle() - .Retry(); - -policy.Execute(() => DoSomething()); - -// Execute an action passing arbitrary context data -var policy = Policy - .Handle() - .Retry(3, (exception, retryCount, context) => +// Use a dynamically generated value if an operation fails. +new ResiliencePipelineBuilder() + .AddFallback(new FallbackStrategyOptions { - var methodThatRaisedException = context["methodName"]; - Log(exception, methodThatRaisedException); + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(r => r is null), + FallbackAction = args => + { + var avatar = UserAvatar.GetRandomAvatar(); + return Outcome.FromResultAsValueTask(avatar); + } }); -policy.Execute( - () => DoSomething(), - new Dictionary() {{ "methodName", "some method" }} -); - -// Execute a function returning a result -var policy = Policy - .Handle() - .Retry(); - -var result = policy.Execute(() => DoSomething()); - -// Execute a function returning a result passing arbitrary context data -var policy = Policy - .Handle() - .Retry(3, (exception, retryCount, context) => +// Use a default or dynamically generated value, and execute an additional action if the fallback is triggered. +new ResiliencePipelineBuilder() + .AddFallback(new FallbackStrategyOptions { - object methodThatRaisedException = context["methodName"]; - Log(exception, methodThatRaisedException) + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(r => r is null), + FallbackAction = args => + { + var avatar = UserAvatar.GetRandomAvatar(); + return Outcome.FromResultAsValueTask(UserAvatar.Blank); + }, + OnFallback = args => + { + // Add extra logic to be executed when the fallback is triggered, such as logging. + return default; // Returns an empty ValueTask + } }); - -var result = policy.Execute( - () => DoSomething(), - new Dictionary() {{ "methodName", "some method" }} -); - -// You can of course chain it all together -Policy - .Handle(ex => ex.Number == 1205) - .Or(ex => ex.ParamName == "example") - .Retry() - .Execute(() => DoSomething()); -``` - -#### Richer policy consumption patterns - -Defining and consuming the policy in the same scope, as shown above, is the most immediate way to use Polly. Consider also: - -+ Separate policy definition from policy consumption, and inject policies into the code which will consume them. This [enables many unit-testing scenarios](https://github.com/App-vNext/Polly/wiki/Unit-testing-with-Polly---with-examples). -+ If your application uses Polly in a number of locations, define all policies at start-up, and place them in a [`PolicyRegistry`](https://github.com/App-vNext/Polly/wiki/PolicyRegistry). This is a common pattern in .NET Core applications. For instance, you might define your own extension method on `IServiceCollection` to configure the policies you will consume elsewhere in the application. PolicyRegistry also [combines well with DI to support unit-testing](https://github.com/App-vNext/Polly/wiki/Unit-testing-with-Polly---with-examples#use-policyregistry-with-di-to-work-with-policy-collections). - -```csharp -public static ConfigurePollyPolicies(this IServiceCollection services) -{ - PolicyRegistry registry = new PolicyRegistry() - { - { "RepositoryResilienceStrategy", /* define Policy or PolicyWrap strategy */ }, - { "CollaboratingMicroserviceResilienceStrategy", /* define Policy or PolicyWrap strategy */ }, - { "ThirdPartyApiResilienceStrategy", /* define Policy or PolicyWrap strategy */ }, - /* etc */ - }; - - services.AddSingleton>(registry); -} ``` + -## Usage – proactive policies - -The proactive policies add resilience strategies that are not based on handling faults which the governed code may throw or return. - -### Step 1 : Configure - -#### Optimistic timeout +For more details, visit the [fallback strategy](https://www.pollydocs.org/strategies/fallback) documentation. -Optimistic timeout [operates via CancellationToken](https://github.com/App-vNext/Polly/wiki/Timeout#optimistic-timeout) and assumes delegates you execute support [co-operative cancellation](https://msdn.microsoft.com/en-us/library/dd997364.aspx). You must use `Execute/Async(...)` overloads taking a `CancellationToken`, and the executed delegate must honor that `CancellationToken`. +### Hedging -```csharp -// Timeout and return to the caller after 30 seconds, if the executed delegate has not completed. Optimistic timeout: Delegates should take and honour a CancellationToken. -Policy - .Timeout(30) + +```cs +// Add hedging with default options. +// See https://www.pollydocs.org/strategies/hedging#defaults for defaults. +new ResiliencePipelineBuilder() + .AddHedging(new HedgingStrategyOptions()); -// Configure timeout as timespan. -Policy - .Timeout(TimeSpan.FromMilliseconds(2500)) - -// Configure variable timeout via a func provider. -Policy - .Timeout(myTimeoutProvider) // Func myTimeoutProvider - -// Timeout, calling an action if the action times out -Policy - .Timeout(30, onTimeout: (context, timespan, task) => +// 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. +new ResiliencePipelineBuilder() + .AddHedging(new HedgingStrategyOptions { - // Add extra logic to be invoked when a timeout occurs, such as logging + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(response => response.StatusCode == HttpStatusCode.InternalServerError), + MaxHedgedAttempts = 3, + Delay = TimeSpan.FromSeconds(1), + ActionGenerator = args => + { + Console.WriteLine("Preparing to execute hedged action."); + + // Return a delegate function to invoke the original action with the action context. + // Optionally, you can also create a completely new action to be executed. + return () => args.Callback(args.ActionContext); + } }); -// Eg timeout, logging that the execution timed out: -Policy - .Timeout(30, onTimeout: (context, timespan, task) => +// Subscribe to hedging events. +new ResiliencePipelineBuilder() + .AddHedging(new HedgingStrategyOptions { - logger.Warn($"{context.PolicyKey} at {context.OperationKey}: execution timed out after {timespan.TotalSeconds} seconds."); + OnHedging = args => + { + Console.WriteLine($"OnHedging: Attempt number {args.AttemptNumber}"); + return default; + } }); - -// Eg timeout, capturing any exception from the timed-out task when it completes: -Policy - .Timeout(30, onTimeout: (context, timespan, task) => - { - task.ContinueWith(t => { - if (t.IsFaulted) logger.Error($"{context.PolicyKey} at {context.OperationKey}: execution timed out after {timespan.TotalSeconds} seconds, with: {t.Exception}."); - }); - }); -``` - -Example execution: - -```csharp -Policy timeoutPolicy = Policy.TimeoutAsync(30); -HttpResponseMessage httpResponse = await timeoutPolicy - .ExecuteAsync( - async ct => await httpClient.GetAsync(endpoint, ct), // Execute a delegate which responds to a CancellationToken input parameter. - CancellationToken.None // In this case, CancellationToken.None is passed into the execution, indicating you have no independent cancellation control you wish to add to the cancellation provided by TimeoutPolicy. Your own independent CancellationToken can also be passed - see wiki for examples. - ); -``` - -Timeout policies throw `TimeoutRejectedException` when a timeout occurs. - -For more detail see [Timeout policy documentation](https://github.com/App-vNext/Polly/wiki/Timeout) in the wiki. - -#### Pessimistic timeout - -Pessimistic timeout allows calling code to 'walk away' from waiting for an executed delegate to complete, even if it does not support cancellation. In synchronous executions this is at the expense of an extra thread; see [deep documentation on wiki](https://github.com/App-vNext/Polly/wiki/Timeout#pessimistic-timeout) for more detail. - -```csharp -// Timeout after 30 seconds, if the executed delegate has not completed. Enforces this timeout even if the executed code has no cancellation mechanism. -Policy - .Timeout(30, TimeoutStrategy.Pessimistic) - -// (All syntax variants outlined for optimistic timeout above also exist for pessimistic timeout.) -``` - -Example execution: - -```csharp -Policy timeoutPolicy = Policy.TimeoutAsync(30, TimeoutStrategy.Pessimistic); -var response = await timeoutPolicy - .ExecuteAsync( - async () => await FooNotHonoringCancellationAsync(), // Execute a delegate which takes no CancellationToken and does not respond to cancellation. - ); ``` + -Timeout policies throw `TimeoutRejectedException` when timeout occurs. +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 details, visit the [hedging strategy](https://www.pollydocs.org/strategies/hedging) documentation. -For more detail see: [Timeout policy documentation](https://github.com/App-vNext/Polly/wiki/Timeout) on wiki. +### Timeout -#### Bulkhead +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`. -```csharp -// Restrict executions through the policy to a maximum of twelve concurrent actions. -Policy - .Bulkhead(12) + +```cs +// Add timeout using the default options. +// See https://www.pollydocs.org/strategies/timeout#defaults for defaults. +new ResiliencePipelineBuilder() + .AddTimeout(new TimeoutStrategyOptions()); -// Restrict executions through the policy to a maximum of twelve concurrent actions, -// with up to two actions waiting for an execution slot in the bulkhead if all slots are taken. -Policy - .Bulkhead(12, 2) +// To add a timeout with a custom TimeSpan duration +new ResiliencePipelineBuilder() + .AddTimeout(TimeSpan.FromSeconds(3)); -// Restrict concurrent executions, calling an action if an execution is rejected -Policy - .Bulkhead(12, context => +// To add a timeout using a custom timeout generator function +new ResiliencePipelineBuilder() + .AddTimeout(new TimeoutStrategyOptions { - // Add callback logic for when the bulkhead rejects execution, such as logging + TimeoutGenerator = args => + { + // Note: the timeout generator supports asynchronous operations + return new ValueTask(TimeSpan.FromSeconds(123)); + } }); -// Monitor the bulkhead available capacity, for example for health/load reporting. -var bulkhead = Policy.Bulkhead(12, 2); -// ... -int freeExecutionSlots = bulkhead.BulkheadAvailableCount; -int freeQueueSlots = bulkhead.QueueAvailableCount; - -``` - -Bulkhead policies throw `BulkheadRejectedException` if items are queued to the bulkhead when the bulkhead execution and queue are both full. - -For more detail see: [Bulkhead policy documentation](https://github.com/App-vNext/Polly/wiki/Bulkhead) on wiki. - -#### Rate-Limit - -```csharp -// Allow up to 20 executions per second. -Policy.RateLimit(20, TimeSpan.FromSeconds(1)); - -// Allow up to 20 executions per second with a burst of 10 executions. -Policy.RateLimit(20, TimeSpan.FromSeconds(1), 10); - -// Allow up to 20 executions per second, with a delegate to return the -// retry-after value to use if the rate limit is exceeded. -Policy.RateLimit(20, TimeSpan.FromSeconds(1), (retryAfter, context) => -{ - return retryAfter.Add(TimeSpan.FromSeconds(2)); -}); - -// Allow up to 20 executions per second with a burst of 10 executions, -// with a delegate to return the retry-after value to use if the rate -// limit is exceeded. -Policy.RateLimit(20, TimeSpan.FromSeconds(1), 10, (retryAfter, context) => -{ - return retryAfter.Add(TimeSpan.FromSeconds(2)); -}); -``` - -Example execution: - -```csharp -public async Task SearchAsync(string query, HttpContext httpContext) -{ - var rateLimit = Policy.RateLimitAsync(20, TimeSpan.FromSeconds(1), 10); - - try +// To add a timeout and listen for timeout events +new ResiliencePipelineBuilder() + .AddTimeout(new TimeoutStrategyOptions { - var result = await rateLimit.ExecuteAsync(() => TextSearchAsync(query)); - - var json = JsonConvert.SerializeObject(result); - - httpContext.Response.ContentType = "application/json"; - await httpContext.Response.WriteAsync(json); - } - catch (RateLimitRejectedException ex) - { - string retryAfter = DateTimeOffset.UtcNow - .Add(ex.RetryAfter) - .ToUnixTimeSeconds() - .ToString(CultureInfo.InvariantCulture); - - httpContext.Response.StatusCode = 429; - httpContext.Response.Headers["Retry-After"] = retryAfter; - } -} -``` - -Rate-limit policies throw `RateLimitRejectedException` if too many requests are executed within the configured timespan. - -For more detail see: [Rate-limit policy documentation](https://github.com/App-vNext/Polly/wiki/Rate-Limit) in the wiki. - -#### Cache - -```csharp -var memoryCache = new MemoryCache(new MemoryCacheOptions()); -var memoryCacheProvider = new MemoryCacheProvider(memoryCache); -var cachePolicy = Policy.Cache(memoryCacheProvider, TimeSpan.FromMinutes(5)); - -// For .NET Core DI examples see the CacheProviders linked to from https://github.com/App-vNext/Polly/wiki/Cache#working-with-cacheproviders : -// - https://github.com/App-vNext/Polly.Caching.MemoryCache -// - https://github.com/App-vNext/Polly.Caching.IDistributedCache - -// Define a cache policy with absolute expiration at midnight tonight. -var cachePolicy = Policy.Cache(memoryCacheProvider, new AbsoluteTtl(DateTimeOffset.Now.Date.AddDays(1))); - -// Define a cache policy with sliding expiration: items remain valid for another 5 minutes each time the cache item is used. -var cachePolicy = Policy.Cache(memoryCacheProvider, new SlidingTtl(TimeSpan.FromMinutes(5))); - -// Define a cache Policy, and catch any cache provider errors for logging. -var cachePolicy = Policy.Cache(myCacheProvider, TimeSpan.FromMinutes(5), - (context, key, ex) => { - logger.Error($"Cache provider, for key {key}, threw exception: {ex}."); // (for example) - } -); - -// Execute through the cache as a read-through cache: check the cache first; if not found, execute underlying delegate and store the result in the cache. -// The key to use for caching, for a particular execution, is specified by setting the OperationKey (before v6: ExecutionKey) on a Context instance passed to the execution. Use an overload of the form shown below (or a richer overload including the same elements). -// Example: "FooKey" is the cache key that will be used in the below execution. -TResult result = cachePolicy.Execute(context => getFoo(), new Context("FooKey")); -``` - -For richer options and details of using further cache providers see: [Cache policy documentation](https://github.com/App-vNext/Polly/wiki/Cache) on wiki. - -#### PolicyWrap - -```csharp -// Define a combined policy strategy, built of previously-defined policies. -var policyWrap = Policy - .Wrap(fallback, cache, retry, breaker, timeout, bulkhead); -// (wraps the policies around any executed delegate: fallback outermost ... bulkhead innermost) -policyWrap.Execute(...) - -// Define a standard resilience strategy ... -PolicyWrap commonResilience = Policy.Wrap(retry, breaker, timeout); - -// ... then wrap in extra policies specific to a call site, at that call site: -Avatar avatar = Policy - .Handle() - .Fallback(Avatar.Blank) - .Wrap(commonResilience) - .Execute(() => { /* get avatar */ }); - -// Share commonResilience, but wrap different policies in at another call site: -Reputation reps = Policy - .Handle() - .Fallback(Reputation.NotAvailable) - .Wrap(commonResilience) - .Execute(() => { /* get reputation */ }); - -``` - -For more detail see: [PolicyWrap documentation](https://github.com/App-vNext/Polly/wiki/PolicyWrap) on wiki. - -#### NoOp - -```csharp -// Define a policy which will simply cause delegates passed for execution to be executed 'as is'. -// This is useful for stubbing-out Polly in unit tests, -// or in application situations where your code architecture might expect a policy -// but you simply want to pass the execution through without policy intervention. -NoOpPolicy noOp = Policy.NoOp(); -``` - -For more detail see: [NoOp documentation](https://github.com/App-vNext/Polly/wiki/NoOp) on wiki. - -### Step 2 : Execute the policy - -As for fault-handling policies [above](#step-3--execute-code-through-the-policy). - -## Usage – results and exceptions - -### Getting execution results as a PolicyResult - -Using the `ExecuteAndCapture(...)` methods you can capture the outcome of an execution: the methods return a `PolicyResult` instance which describes whether the outcome was a successful execution or a fault. - -```csharp -var policyResult = await Policy - .Handle() - .RetryAsync() - .ExecuteAndCaptureAsync(() => DoSomethingAsync()); -/* -policyResult.Outcome - whether the call succeeded or failed -policyResult.FinalException - the final exception captured, will be null if the call succeeded -policyResult.ExceptionType - was the final exception an exception the policy was defined to handle (like HttpRequestException above) or an unhandled one (say Exception). Will be null if the call succeeded. -policyResult.Result - if executing a func, the result if the call succeeded or the type's default value -*/ -``` - -### Getting execution results and return values with a HttpResponseMessage - -As described at step 1b, from Polly v4.3.0 onwards, policies can handle return values and exceptions in combination: - -```csharp -// Handle both exceptions and return values in one policy -HttpStatusCode[] httpStatusCodesWorthRetrying = { - HttpStatusCode.RequestTimeout, // 408 - HttpStatusCode.InternalServerError, // 500 - HttpStatusCode.BadGateway, // 502 - HttpStatusCode.ServiceUnavailable, // 503 - HttpStatusCode.GatewayTimeout // 504 -}; -HttpResponseMessage result = await Policy - .Handle() - .OrResult(r => httpStatusCodesWorthRetrying.Contains(r.StatusCode)) - .RetryAsync(...) - .ExecuteAsync( /* some Func> */ ) -``` - -The exceptions and return results to handle can be expressed fluently in any order. - -### Getting execution results and return values with a Policy<TResult> - -Configuring a policy with `.HandleResult(...)` or `.OrResult(...)` generates a strongly-typed `Policy` of the specific policy type, eg `Retry`, `AdvancedCircuitBreaker`. - -These policies must be used to execute delegates returning `TResult`, i.e.: - -+ `Execute(Func)` (and related overloads) -+ `ExecuteAsync(Func>)` (and related overloads) - -### Getting strongly-typed results with ExecuteAndCapture<TResult>() - -`.ExecuteAndCapture(...)` on non-generic policies returns a `PolicyResult` with properties: - -```txt -policyResult.Outcome - whether the call succeeded or failed -policyResult.FinalException - the final exception captured; will be null if the call succeeded -policyResult.ExceptionType - was the final exception an exception the policy was defined to handle (like HttpRequestException above) or an unhandled one (say Exception)? Will be null if the call succeeded. -policyResult.Result - if executing a func, the result if the call succeeded; otherwise, the type's default value -``` - -`.ExecuteAndCapture(Func)` on strongly-typed policies adds two properties: - -```txt -policyResult.FaultType - was the final fault handled an exception or a result handled by the policy? Will be null if the delegate execution succeeded. -policyResult.FinalHandledResult - the final fault result handled; will be null or the type's default value, if the call succeeded -``` - -### State-change delegates on Policy<TResult> policies - -In non-generic policies handling only exceptions, state-change delegates such as `onRetry` and `onBreak` take an `Exception` parameter. - -In generic-policies handling `TResult` return values, state-change delegates are identical except they take a `DelegateResult` parameter in place of `Exception.` `DelegateResult` has two properties: - -+ `Exception // The exception just thrown if policy is in process of handling an exception (otherwise null)` -+ `Result // The TResult just raised, if policy is in process of handling a result (otherwise default(TResult))` - -### BrokenCircuitException<TResult> - -Non-generic CircuitBreaker policies throw a `BrokenCircuitException` when the circuit is broken. This `BrokenCircuitException` contains the last exception (the one which caused the circuit to break) as the `InnerException`. - -For `CircuitBreakerPolicy` policies: - -+ A circuit broken due to an exception throws a `BrokenCircuitException` with `InnerException` set to the exception which triggered the break (as previously). -+ A circuit broken due to handling a result throws a `BrokenCircuitException` with the `Result` property set to the result which caused the circuit to break. - -## Policy Keys and Context data - -```csharp -// Identify policies with a PolicyKey, using the WithPolicyKey() extension method -// (for example, for correlation in logs or metrics) - -var policy = Policy - .Handle() - .Retry(3, onRetry: (exception, retryCount, context) => - { - logger.Error($"Retry {retryCount} of {context.PolicyKey} at {context.OperationKey}, due to: {exception}."); - }) - .WithPolicyKey("MyDataAccessPolicy"); - -// Identify call sites with an OperationKey, by passing in a Context -var customerDetails = policy.Execute(myDelegate, new Context("GetCustomerDetails")); - -// "MyDataAccessPolicy" -> context.PolicyKey -// "GetCustomerDetails -> context.OperationKey - - -// Pass additional custom information from call site into execution context -var policy = Policy - .Handle() - .Retry(3, onRetry: (exception, retryCount, context) => - { - logger.Error($"Retry {retryCount} of {context.PolicyKey} at {context.OperationKey}, getting {context["Type"]} of id {context["Id"]}, due to: {exception}."); - }) - .WithPolicyKey("MyDataAccessPolicy"); - -int id = ... // customer id from somewhere -var customerDetails = policy.Execute(context => GetCustomer(id), - new Context("GetCustomerDetails", new Dictionary() {{"Type","Customer"},{"Id",id}} - )); + TimeoutGenerator = args => + { + // Note: the timeout generator supports asynchronous operations + return new ValueTask(TimeSpan.FromSeconds(123)); + }, + OnTimeout = args => + { + Console.WriteLine($"{args.Context.OperationKey}: Execution timed out after {args.Timeout.TotalSeconds} seconds."); + return default; + } + }); ``` + -For more detail see: [Keys and Context Data](https://github.com/App-vNext/Polly/wiki/Keys-And-Context-Data) on wiki. +Timeout strategies throw `TimeoutRejectedException` when a timeout occurs. For more details, visit the [timeout strategy](https://www.pollydocs.org/strategies/timeout) documentation. -## PolicyRegistry +### Rate Limiter -```csharp -// Create a policy registry (for example on application start-up) -PolicyRegistry registry = new PolicyRegistry(); + +```cs +// Add rate limiter with default options. +// See https://www.pollydocs.org/strategies/rate-limiter#defaults for defaults. +new ResiliencePipelineBuilder() + .AddRateLimiter(new RateLimiterStrategyOptions()); -// Populate the registry with policies -registry.Add("StandardHttpResilience", myStandardHttpResiliencePolicy); -// Or: -registry["StandardHttpResilience"] = myStandardHttpResiliencePolicy; +// Create a rate limiter to allow a maximum of 100 concurrent executions and a queue of 50. +new ResiliencePipelineBuilder() + .AddConcurrencyLimiter(100, 50); -// Pass the registry instance to usage sites by DI, perhaps -public class MyServiceGateway -{ - public void MyServiceGateway(..., IReadOnlyPolicyRegistry registry, ...) +// Create a rate limiter that allows 100 executions per minute. +new ResiliencePipelineBuilder() + .AddRateLimiter(new SlidingWindowRateLimiter(new SlidingWindowRateLimiterOptions { - ... - } -} -// (Or if you prefer ambient-context pattern, use a thread-safe singleton) - -// Use a policy from the registry -registry.Get>("StandardHttpResilience") - .ExecuteAsync(...) + PermitLimit = 100, + Window = TimeSpan.FromMinutes(1) + })); ``` + -`PolicyRegistry` has a range of further dictionary-like semantics such as `.ContainsKey(...)`, `.TryGet(...)`, `.Count`, `.Clear()`, and `Remove(...)`. - -Available from v5.2.0. For more detail see: [PolicyRegistry](https://github.com/App-vNext/Polly/wiki/PolicyRegistry) on wiki. +Rate limiter strategy throws `RateLimiterRejectedException` if execution is rejected. For more details, visit the [rate limiter strategy](https://www.pollydocs.org/strategies/rate-limiter) documentation. -## Asynchronous Support +## Next steps -Polly fully supports asynchronous executions, using the asynchronous methods: +To learn more about Polly, visit [pollydocs.org][polly-docs]. -+ `RetryAsync` -+ `WaitAndRetryAsync` -+ `CircuitBreakerAsync` -+ (etc) -+ `ExecuteAsync` -+ `ExecuteAndCaptureAsync` - -In place of their synchronous counterparts: - -+ `Retry` -+ `WaitAndRetry` -+ `CircuitBreaker` -+ (etc) -+ `Execute` -+ `ExecuteAndCapture` - -Async overloads exist for all policy types and for all `Execute()` and `ExecuteAndCapture()` overloads. - -Usage example: - -```csharp -await Policy - .Handle(ex => ex.Number == 1205) - .Or(ex => ex.ParamName == "example") - .RetryAsync() - .ExecuteAsync(() => DoSomethingAsync()); - -``` +## Samples -### SynchronizationContext - -Async continuations and retries by default do not run on a captured synchronization context. To change this, use `.ExecuteAsync(...)` overloads taking a boolean `continueOnCapturedContext` parameter. - -### Cancellation support - -Async policy execution supports cancellation via `.ExecuteAsync(...)` overloads taking a `CancellationToken`. - -The token you pass as the `cancellationToken` parameter to the `ExecuteAsync(...)` call serves three purposes: - -+ It cancels Policy actions such as further retries, waits between retries or waits for a bulkhead execution slot. -+ It is passed by the policy as the `CancellationToken` input parameter to any delegate executed through the policy, to support cancellation during delegate execution. -+ In common with the Base Class Library implementation in `Task.Run(…)` and elsewhere, if the cancellation token is cancelled before execution begins, the user delegate is not executed at all. - -```csharp -// Try several times to retrieve from a URI, but support cancellation at any time. -CancellationToken cancellationToken = // ... -var policy = Policy - .Handle() - .WaitAndRetryAsync(new[] { - TimeSpan.FromSeconds(1), - TimeSpan.FromSeconds(2), - TimeSpan.FromSeconds(4) - }); -var response = await policy.ExecuteAsync(ct => httpClient.GetAsync(uri, ct), cancellationToken); -``` - -From Polly v5.0, synchronous executions also support cancellation via `CancellationToken`. - -## Thread safety - -All Polly policies are fully thread-safe. You can safely re-use policies at multiple call sites, and execute through policies concurrently on different threads. - -While the internal operation of the policy is thread-safe, this does not magically make delegates you execute through the policy thread-safe: if delegates you execute through the policy are not thread-safe, they remain not thread-safe. - -## Interfaces - -Polly v5.2.0 adds interfaces intended to support [`PolicyRegistry`](https://github.com/App-vNext/Polly/wiki/PolicyRegistry) and to group Policy functionality by the [interface segregation principle](https://en.wikipedia.org/wiki/Interface_segregation_principle). Polly's interfaces are not intended for coding your own policy implementations against. - -### Execution interfaces: `ISyncPolicy` etc - -Execution interfaces [`ISyncPolicy`](https://github.com/App-vNext/Polly/tree/main/src/Polly/ISyncPolicy.cs), [`IAsyncPolicy`](https://github.com/App-vNext/Polly/tree/main/src/Polly/IAsyncPolicy.cs), [`ISyncPolicy`](https://github.com/App-vNext/Polly/tree/main/src/Polly/ISyncPolicyTResult.cs) and [`IAsyncPolicy`](https://github.com/App-vNext/Polly/tree/main/src/Polly/IAsyncPolicyTResult.cs) define the execution overloads available to policies targeting sync/async, and non-generic / generic calls respectively. - -### Policy-kind interfaces: `ICircuitBreakerPolicy` etc - -Orthogonal to the execution interfaces, interfaces specific to the kind of Policy define properties and methods common to that type of policy. - -For example, [`ICircuitBreakerPolicy`](https://github.com/App-vNext/Polly/tree/main/src/Polly/CircuitBreaker/ICircuitBreakerPolicy.cs) defines - -+ `CircuitState CircuitState` -+ `Exception LastException` -+ `void Isolate()` -+ `void Reset()` - -with `ICircuitBreakerPolicy : ICircuitBreakerPolicy` adding: - -+ `TResult LastHandledResult`. - -This allows collections of similar kinds of policy to be treated as one - for example, for monitoring all your circuit-breakers as [described here](https://github.com/App-vNext/Polly/pull/205). - -For more detail see: [Polly and interfaces](https://github.com/App-vNext/Polly/wiki/Polly-and-interfaces) on wiki. - -## Sample Projects - -- [Polly-Samples](https://github.com/App-vNext/Polly-Samples) contains practical examples for using various implementations of Polly. Please feel free to contribute to the Polly-Samples repository in order to assist others who are either learning Polly for the first time, or are seeking advanced examples and novel approaches provided by our generous community. -- Microsoft's [eShopOnContainers project](https://github.com/dotnet-architecture/eShopOnContainers) is a sample project demonstrating a .NET Microservices architecture and using Polly for resilience +- [Samples](samples/README.md): Samples in this repository that serve as an introduction to Polly. +- [Polly-Samples](https://github.com/App-vNext/Polly-Samples): Contains practical examples for using various implementations of Polly. Please feel free to contribute to the Polly-Samples repository in order to assist others who are either learning Polly for the first time, or are seeking advanced examples and novel approaches provided by our generous community. +- Microsoft's [eShopOnContainers project](https://github.com/dotnet-architecture/eShopOnContainers): Sample project demonstrating a .NET Microservices architecture and using Polly for resilience. ## License Licensed under the terms of the [New BSD License](http://opensource.org/licenses/BSD-3-Clause) -## Resources - -Visit the [documentation][polly-docs] to explore more Polly-related resources. - [polly-docs]: https://www.pollydocs.org/ diff --git a/README_V8.md b/README_V8.md deleted file mode 100644 index 8eeb5db81b6..00000000000 --- a/README_V8.md +++ /dev/null @@ -1,439 +0,0 @@ -# Polly - -Polly is a .NET resilience and transient-fault-handling library that allows developers to express resilience strategies such as Retry, Circuit Breaker, Hedging, Timeout, Rate Limiter and Fallback in a fluent and thread-safe manner. - -[](https://www.dotnetfoundation.org/) -We are a member of the [.NET Foundation](https://www.dotnetfoundation.org/about)! - -**Keep up to date with new feature announcements, tips & tricks, and other news through [www.thepollyproject.org](https://www.thepollyproject.org)** - -[![Build status](https://github.com/App-vNext/Polly/workflows/build/badge.svg?branch=main&event=push)](https://github.com/App-vNext/Polly/actions?query=workflow%3Abuild+branch%3Amain+event%3Apush) [![Code coverage](https://codecov.io/gh/App-vNext/Polly/branch/main/graph/badge.svg)](https://codecov.io/gh/App-vNext/Polly) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/App-vNext/Polly/badge)](https://securityscorecards.dev/viewer/?uri=github.com/App-vNext/Polly) - -![Polly logo](https://raw.github.com/App-vNext/Polly/main/Polly-Logo.png) - -> [!IMPORTANT] -> This documentation describes the new Polly v8 API. If you are using the v7 API, please refer to the [previous version](https://github.com/App-vNext/Polly/tree/7.2.4) of the documentation. - -## NuGet Packages - -| **Package** | **Latest Version** | -|:--|:--| -| Polly | [![NuGet](https://buildstats.info/nuget/Polly?includePreReleases=true)](https://www.nuget.org/packages/Polly/ "Download Polly from NuGet.org") | -| Polly.Core | [![NuGet](https://buildstats.info/nuget/Polly.Core?includePreReleases=true)](https://www.nuget.org/packages/Polly.Core/ "Download Polly.Core from NuGet.org") | -| Polly.Extensions | [![NuGet](https://buildstats.info/nuget/Polly.Extensions?includePreReleases=true)](https://www.nuget.org/packages/Polly.Extensions/ "Download Polly.Extensions from NuGet.org") | -| Polly.RateLimiting | [![NuGet](https://buildstats.info/nuget/Polly.RateLimiting?includePreReleases=true)](https://www.nuget.org/packages/Polly.RateLimiting/ "Download Polly.RateLimiting from NuGet.org") | -| Polly.Testing | [![NuGet](https://buildstats.info/nuget/Polly.Testing?includePreReleases=true)](https://www.nuget.org/packages/Polly.Testing/ "Download Polly.Testing from NuGet.org") | - -## Documentation - -This README aims to give a quick overview of some Polly features - including enough to get you started with any resilience strategy. For deeper detail on any resilience strategy, and many other aspects of Polly, be sure also to check out the [documentation][polly-docs]. - -## Quick start - -To use Polly, you must provide a callback and execute it using [**resilience pipeline**](https://www.pollydocs.org/pipelines). A resilience pipeline is a combination of one or more [**resilience strategies**](https://www.pollydocs.org/strategies) such as retry, timeout, and rate limiter. Polly uses **builders** to integrate these strategies into a pipeline. - -To get started, first add the [Polly.Core](https://www.nuget.org/packages/Polly.Core/) package to your project by running the following command: - -```sh -dotnet add package Polly.Core -``` - -You can create a `ResiliencePipeline` using the `ResiliencePipelineBuilder` class as shown below: - - -```cs -// Create a instance of builder that exposes various extensions for adding resilience strategies -ResiliencePipeline pipeline = new ResiliencePipelineBuilder() - .AddRetry(new RetryStrategyOptions()) // Add retry using the default options - .AddTimeout(TimeSpan.FromSeconds(10)) // Add 10 second timeout - .Build(); // Builds the resilience pipeline - -// Execute the pipeline asynchronously -await pipeline.ExecuteAsync(static async cancellationToken => { /*Your custom logic here */ }, cancellationToken); -``` - - -### Dependency injection - -If you prefer to define resilience pipelines using [`IServiceCollection`](https://learn.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.iservicecollection), you'll need to install the [Polly.Extensions](https://www.nuget.org/packages/Polly.Extensions/) package: - -```sh -dotnet add package Polly.Extensions -``` - -You can then define your resilience pipeline using the `AddResiliencePipeline(...)` extension method as shown: - - -```cs -var services = new ServiceCollection(); - -// Define a resilience pipeline with the name "my-pipeline" -services.AddResiliencePipeline("my-pipeline", builder => -{ - builder - .AddRetry(new RetryStrategyOptions()) - .AddTimeout(TimeSpan.FromSeconds(10)); -}); - -// Build the service provider -IServiceProvider serviceProvider = services.BuildServiceProvider(); - -// Retrieve ResiliencePipelineProvider that caches and dynamically creates the resilience pipelines -var pipelineProvider = serviceProvider.GetRequiredService>(); - -// Retrieve resilience pipeline using the name it was registered with -ResiliencePipeline pipeline = pipelineProvider.GetPipeline("my-pipeline"); - -// Execute the pipeline -await pipeline.ExecuteAsync(static async token => -{ - // Your custom logic here -}); -``` - - -## Resilience strategies - -Polly provides a variety of resilience strategies. Alongside the comprehensive guides for each strategy, the wiki also includes an [overview of the role each strategy plays in resilience engineering](https://github.com/App-vNext/Polly/wiki/Transient-fault-handling-and-proactive-resilience-engineering). - -Polly categorizes resilience strategies into two main groups: - -- **Reactive**: These strategies handle specific exceptions that are thrown, or results that are returned, by the callbacks executed through the strategy. -- **Proactive**: Unlike reactive strategies, proactive strategies do not focus on handling errors by the callbacks might throw or return. They can make pro-active decisions to cancel or reject the execution of callbacks (e.g., using a rate limiter or a timeout resilience strategy). - -| Strategy | Reactive | Premise | AKA | How does the strategy mitigate?| -| ------------- | --- | ------------- |:-------------: |------------- | -|**Retry**
(strategy family)
([quickstart](#retry) ; [deep](https://www.pollydocs.org/strategies/retry)) |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**
(strategy family)
([quickstart](#circuit-breaker) ; [deep](https://www.pollydocs.org/strategies/circuit-breaker))|Yes|When a system is seriously struggling, failing fast is better than making users/callers wait.

Protecting a faulting system from overload can help it recover. | *Stop doing it if it hurts*

*Give that system a break* | Breaks the circuit (blocks executions) for a period, when faults exceed some pre-configured threshold. | -|**Timeout**
([quickstart](#timeout) ; [deep](https://www.pollydocs.org/strategies/timeout))|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**
([quickstart](#rate-limiter) ; [deep](https://www.pollydocs.org/strategies/rate-limiter))|No|Limiting the rate a system handles requests is another way to control load.

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**
([quickstart](#fallback) ; [deep](https://www.pollydocs.org/strategies/fallback))|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**
([quickstart](#hedging) ; [deep](https://www.pollydocs.org/strategies/hedging))|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](https://www.pollydocs.org/strategies) docs to explore how to configure individual resilience strategies in more detail. - -### Retry - - -```cs -// Add retry using the default options. -// See https://www.pollydocs.org/strategies/retry#defaults for defaults. -new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions()); - -// For instant retries with no delay -new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions -{ - Delay = TimeSpan.Zero -}); - -// For advanced control over the retry behavior, including the number of attempts, -// delay between retries, and the types of exceptions to handle. -new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions -{ - ShouldHandle = new PredicateBuilder().Handle(), - BackoffType = DelayBackoffType.Exponential, - UseJitter = true, // Adds a random factor to the delay - MaxRetryAttempts = 4, - Delay = TimeSpan.FromSeconds(3), -}); - -// To use a custom function to generate the delay for retries -new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions -{ - MaxRetryAttempts = 2, - DelayGenerator = args => - { - var delay = args.AttemptNumber switch - { - 0 => TimeSpan.Zero, - 1 => TimeSpan.FromSeconds(1), - _ => TimeSpan.FromSeconds(5) - }; - - // This example uses a synchronous delay generator, - // but the API also supports asynchronous implementations. - return new ValueTask(delay); - } -}); - -// To extract the delay from the result object -new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions -{ - DelayGenerator = args => - { - if (args.Outcome.Result is HttpResponseMessage responseMessage && - TryGetDelay(responseMessage, out TimeSpan delay)) - { - return new ValueTask(delay); - } - - // Returning null means the retry strategy will use its internal delay for this attempt. - return new ValueTask((TimeSpan?)null); - } -}); - -// To get notifications when a retry is performed -new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions -{ - MaxRetryAttempts = 2, - OnRetry = args => - { - Console.WriteLine("OnRetry, Attempt: {0}", args.AttemptNumber); - - // Event handlers can be asynchronous; here, we return an empty ValueTask. - return default; - } -}); - -// To keep retrying indefinitely or until success use int.MaxValue. -new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions -{ - MaxRetryAttempts = int.MaxValue, -}); -``` - - -If all retries fail, a retry strategy rethrows the final exception back to the calling code. For more details, visit the [retry strategy](https://www.pollydocs.org/strategies/retry) documentation. - -### Circuit Breaker - - -```cs -// Add circuit breaker with default options. -// See https://www.pollydocs.org/strategies/circuit-breaker#defaults for defaults. -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() -}); - -// Handle specific failed results for HttpResponseMessage: -new ResiliencePipelineBuilder() - .AddCircuitBreaker(new CircuitBreakerStrategyOptions - { - ShouldHandle = new PredicateBuilder() - .Handle() - .HandleResult(response => response.StatusCode == HttpStatusCode.InternalServerError) - }); - -// Monitor the circuit state, useful for health reporting: -var stateProvider = new CircuitBreakerStateProvider(); - -new ResiliencePipelineBuilder() - .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(); -``` - - -For more details, visit the [circuit breaker strategy](https://www.pollydocs.org/strategies/circuit-breaker) documentation. - -### Fallback - - -```cs -// Add a fallback/substitute value if an operation fails. -new ResiliencePipelineBuilder() - .AddFallback(new FallbackStrategyOptions - { - ShouldHandle = new PredicateBuilder() - .Handle() - .HandleResult(r => r is null), - FallbackAction = args => Outcome.FromResultAsValueTask(UserAvatar.Blank) - }); - -// Use a dynamically generated value if an operation fails. -new ResiliencePipelineBuilder() - .AddFallback(new FallbackStrategyOptions - { - ShouldHandle = new PredicateBuilder() - .Handle() - .HandleResult(r => r is null), - FallbackAction = args => - { - var avatar = UserAvatar.GetRandomAvatar(); - return Outcome.FromResultAsValueTask(avatar); - } - }); - -// Use a default or dynamically generated value, and execute an additional action if the fallback is triggered. -new ResiliencePipelineBuilder() - .AddFallback(new FallbackStrategyOptions - { - ShouldHandle = new PredicateBuilder() - .Handle() - .HandleResult(r => r is null), - FallbackAction = args => - { - var avatar = UserAvatar.GetRandomAvatar(); - return Outcome.FromResultAsValueTask(UserAvatar.Blank); - }, - OnFallback = args => - { - // Add extra logic to be executed when the fallback is triggered, such as logging. - return default; // Returns an empty ValueTask - } - }); -``` - - -For more details, visit the [fallback strategy](https://www.pollydocs.org/strategies/fallback) documentation. - -### Hedging - - -```cs -// Add hedging with default options. -// See https://www.pollydocs.org/strategies/hedging#defaults for defaults. -new ResiliencePipelineBuilder() - .AddHedging(new HedgingStrategyOptions()); - -// 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. -new ResiliencePipelineBuilder() - .AddHedging(new HedgingStrategyOptions - { - ShouldHandle = new PredicateBuilder() - .Handle() - .HandleResult(response => response.StatusCode == HttpStatusCode.InternalServerError), - MaxHedgedAttempts = 3, - Delay = TimeSpan.FromSeconds(1), - ActionGenerator = args => - { - Console.WriteLine("Preparing to execute hedged action."); - - // Return a delegate function to invoke the original action with the action context. - // Optionally, you can also create a completely new action to be executed. - return () => args.Callback(args.ActionContext); - } - }); - -// Subscribe to hedging events. -new ResiliencePipelineBuilder() - .AddHedging(new HedgingStrategyOptions - { - OnHedging = args => - { - Console.WriteLine($"OnHedging: Attempt number {args.AttemptNumber}"); - return default; - } - }); -``` - - -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 details, visit the [hedging strategy](https://www.pollydocs.org/strategies/hedging) documentation. - -### 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`. - - -```cs -// Add timeout using the default options. -// See https://www.pollydocs.org/strategies/timeout#defaults for defaults. -new ResiliencePipelineBuilder() - .AddTimeout(new TimeoutStrategyOptions()); - -// To add a timeout with a custom TimeSpan duration -new ResiliencePipelineBuilder() - .AddTimeout(TimeSpan.FromSeconds(3)); - -// To add a timeout using a custom timeout generator function -new ResiliencePipelineBuilder() - .AddTimeout(new TimeoutStrategyOptions - { - TimeoutGenerator = args => - { - // Note: the timeout generator supports asynchronous operations - return new ValueTask(TimeSpan.FromSeconds(123)); - } - }); - -// To add a timeout and listen for timeout events -new ResiliencePipelineBuilder() - .AddTimeout(new TimeoutStrategyOptions - { - TimeoutGenerator = args => - { - // Note: the timeout generator supports asynchronous operations - return new ValueTask(TimeSpan.FromSeconds(123)); - }, - OnTimeout = args => - { - Console.WriteLine($"{args.Context.OperationKey}: Execution timed out after {args.Timeout.TotalSeconds} seconds."); - return default; - } - }); -``` - - -Timeout strategies throw `TimeoutRejectedException` when a timeout occurs. For more details, visit the [timeout strategy](https://www.pollydocs.org/strategies/timeout) documentation. - -### Rate Limiter - - -```cs -// Add rate limiter with default options. -// See https://www.pollydocs.org/strategies/rate-limiter#defaults for defaults. -new ResiliencePipelineBuilder() - .AddRateLimiter(new RateLimiterStrategyOptions()); - -// Create a rate limiter to allow a maximum of 100 concurrent executions and a queue of 50. -new ResiliencePipelineBuilder() - .AddConcurrencyLimiter(100, 50); - -// Create a rate limiter that allows 100 executions per minute. -new ResiliencePipelineBuilder() - .AddRateLimiter(new SlidingWindowRateLimiter(new SlidingWindowRateLimiterOptions - { - PermitLimit = 100, - Window = TimeSpan.FromMinutes(1) - })); -``` - - -Rate limiter strategy throws `RateLimiterRejectedException` if execution is rejected. For more details, visit the [rate limiter strategy](https://www.pollydocs.org/strategies/rate-limiter) documentation. - -## Next steps - -To learn more about Polly, visit the [pollydocs.org][polly-docs]. - -## Samples - -- [Samples](samples/README.md): Samples in this repository that serve as an introduction to Polly. -- [Polly-Samples](https://github.com/App-vNext/Polly-Samples): Contains practical examples for using various implementations of Polly. Please feel free to contribute to the Polly-Samples repository in order to assist others who are either learning Polly for the first time, or are seeking advanced examples and novel approaches provided by our generous community. -- Microsoft's [eShopOnContainers project](https://github.com/dotnet-architecture/eShopOnContainers): Sample project demonstrating a .NET Microservices architecture and using Polly for resilience. - -## License - -Licensed under the terms of the [New BSD License](http://opensource.org/licenses/BSD-3-Clause) - -[polly-docs]: https://www.pollydocs.org/ diff --git a/docs/advanced/resilience-context.md b/docs/advanced/resilience-context.md index 7afec7744e7..e3f185543a6 100644 --- a/docs/advanced/resilience-context.md +++ b/docs/advanced/resilience-context.md @@ -1,8 +1,5 @@ # Resilience context -> [!NOTE] -> This is documentation for the upcoming Polly v8 release. - The `ResilienceContext` class in Polly provides an execution-scoped instance that accompanies each execution through a Polly resilience strategy. This class serves to share context and facilitate information exchange between the pre-execution, mid-execution, and post-execution phases. The resilience context exposes several properties: diff --git a/docs/general.md b/docs/general.md index 33e4f52aefa..f1d2f113218 100644 --- a/docs/general.md +++ b/docs/general.md @@ -1,8 +1,5 @@ # General -> [!NOTE] -> This is documentation for the upcoming Polly v8 release. - ## Supported targets Polly targets .NET Standard 2.0+ ([coverage](https://docs.microsoft.com/dotnet/standard/net-standard#net-implementation-support): .NET Core 2.0+, .NET Core 3.0, .NET 6.0+ and later Mono, Xamarin and UWP targets). The NuGet package also includes direct targets for .NET Framework 4.6.1 and 4.7.2. diff --git a/docs/index.md b/docs/index.md index 88787ba407b..c4918a7ee0a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,8 +1,5 @@ # Meet Polly: The .NET resilience library -> [!NOTE] -> This is documentation for the upcoming Polly v8 release. - ![Polly logo](https://raw.github.com/App-vNext/Polly/main/Polly-Logo.png) Polly is a powerful library for .NET that helps you handle transient faults and improve the resilience of your applications. With Polly, you can easily define and apply strategies such as Retry, Circuit Breaker, Hedging, Timeout, Rate Limiter and Fallback to handle failures and slowdowns in a fluent and thread-safe way. diff --git a/docs/pipelines/index.md b/docs/pipelines/index.md index d1d075b8192..3c4ccbe5ba2 100644 --- a/docs/pipelines/index.md +++ b/docs/pipelines/index.md @@ -1,8 +1,5 @@ # Resilience pipelines -> [!NOTE] -> This is documentation for the upcoming Polly v8 release. - The `ResiliencePipeline` allows executing arbitrary user-provided callbacks. It is a combination of one or more resilience strategies. ## Usage diff --git a/docs/strategies/index.md b/docs/strategies/index.md index a0c1253d661..0c847a00493 100644 --- a/docs/strategies/index.md +++ b/docs/strategies/index.md @@ -1,8 +1,5 @@ # Resilience strategies -> [!NOTE] -> This is documentation for the upcoming Polly v8 release. - Resilience strategies are essential components of Polly, designed to execute user-defined callbacks while adding an extra layer of resilience. These strategies can't be executed directly; they must be run through a **resilience pipeline**. Polly provides an API to construct resilience pipelines by incorporating one or more resilience strategies through the pipeline builders. Polly categorizes resilience strategies into two main groups: diff --git a/package-readme.md b/package-readme.md index f3a5f437dcf..6efe798b3f6 100644 --- a/package-readme.md +++ b/package-readme.md @@ -13,4 +13,4 @@ Polly is a .NET resilience and transient-fault-handling library that allows deve ## Documentation and Samples -Documentation, and samples, for using Polly can be found in the repository's [README](https://github.com/App-vNext/Polly#readme) and [wiki](https://github.com/App-vNext/Polly/wiki). +Documentation, and samples, for using Polly can be found in the repository's [README](https://github.com/App-vNext/Polly#readme) and [documentation](https://www.pollydocs.org/).