From b2364dc8bb9808a533d3ffda7354a65255c6ceb0 Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Tue, 3 Oct 2023 20:14:31 +0200 Subject: [PATCH 1/8] [Docs] Expand fault handling docs --- README.md | 2 +- docs/index.md | 2 +- docs/strategies/index.md | 65 ++++++++++++++--------- src/Snippets/Docs/ResilienceStrategies.cs | 44 ++++++++------- 4 files changed, 67 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 1b442f2d0cb..391357ed03d 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ We are a member of the [.NET Foundation](https://www.dotnetfoundation.org/about) ## 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]. +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 [pollydocs.org][polly-docs]. ## Quick start diff --git a/docs/index.md b/docs/index.md index c4918a7ee0a..7cb49af3b7a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -21,7 +21,7 @@ You can learn more about each strategy and how to use them [resilience strategie ## How to get started with Polly? -Polly is easy to install and use. You can follow the [getting started](getting-started.md) guide to add Polly to your project and use it for your requests. +Polly is easy to install and use. You can follow the [getting started](getting-started.md) guide to add and start using Polly in your projects. ## Where to find more information? diff --git a/docs/strategies/index.md b/docs/strategies/index.md index 0c847a00493..825bee03102 100644 --- a/docs/strategies/index.md +++ b/docs/strategies/index.md @@ -43,39 +43,52 @@ ResiliencePipeline pipeline = new ResiliencePipelineBuilder() > [!NOTE] > The configuration options are automatically validated by Polly and come with sensible defaults. Therefore, you don't have to specify all the properties unless needed. -## Fault-handling in reactive strategies +## Fault handling using predicates -Each reactive strategy exposes the `ShouldHandle` predicate property. This property represents a predicate to determine whether the fault or the result returned after executing the resilience strategy should be managed or not. +Each reactive strategy provides access to the `ShouldHandle` predicate property. This property offers a mechanism to decide whether the resilience strategy should manage the fault or result returned after execution. -This is demonstrated below: +Setting up the predicate can be accomplished in the following ways: - +- **Manually setting the predicate**: Directly configure the predicate. The advised approach involves using [switch expressions](https://learn.microsoft.com/dotnet/csharp/language-reference/operators/switch-expression) for maximum flexibility, and also allows the incorporation of asynchronous predicates. +- **Employing `PredicateBuilder`**: The `PredicateBuilder` provides a more straightforward method to configure the predicates, akin to predicate setups in earlier Polly versions. + +The examples below illustrate both methods: + +### Configure predicates manually + + ```cs -// Create an instance of options for a retry strategy. In this example, -// we use RetryStrategyOptions. You could also use other options like -// CircuitBreakerStrategyOptions or FallbackStrategyOptions. -var options = new RetryStrategyOptions(); - -// PredicateBuilder can simplify the setup of the ShouldHandle predicate. -options.ShouldHandle = new PredicateBuilder() - .HandleResult(response => !response.IsSuccessStatusCode) - .Handle(); - -// For greater flexibility, you can directly use the ShouldHandle delegate with switch expressions. -options.ShouldHandle = args => args.Outcome switch +var options = new RetryStrategyOptions { - // Strategies may offer additional context for result handling. - // For instance, the retry strategy exposes the number of attempts made. - _ when args.AttemptNumber > 3 => PredicateResult.False(), - { Exception: HttpRequestException } => PredicateResult.True(), - { Result: HttpResponseMessage response } when !response.IsSuccessStatusCode => PredicateResult.True(), - _ => PredicateResult.False() + // For greater flexibility, you can directly use the ShouldHandle delegate with switch expressions. + ShouldHandle = args => args.Outcome switch + { + // Strategies may offer rich arguments for result handling. + // For instance, the retry strategy exposes the number of attempts made. + _ when args.AttemptNumber > 3 => PredicateResult.False(), + { Exception: HttpRequestException } => PredicateResult.True(), + { Result: HttpResponseMessage response } when !response.IsSuccessStatusCode => PredicateResult.True(), + _ => PredicateResult.False() + } }; ``` -Some additional notes from the preceding example: +- `PredicateResult.True()` is a shorthand for `new ValueTask(true)`. +- All `ShouldHandle` predicates are asynchronous and use the type `Func, ValueTask>`. The `Args` acts as a placeholder, and each strategy defines its own arguments. + +### Configure predicates using `PredicateBuilder` + +, or , is a utility API aimed at simplifying the configuration of predicates. -- `PredicateBuilder` is a utility API designed to make configuring predicates easier. -- `PredicateResult.True()` is shorthand for `new ValueTask(true)`. -- All `ShouldHandle` predicates are asynchronous and have the type `Func, ValueTask>`. The `Args` serves as a placeholder, and each strategy defines its own arguments. + +```cs +// Use PredicateBuilder to simplify the setup of the ShouldHandle predicate. +var options = new RetryStrategyOptions +{ + ShouldHandle = new PredicateBuilder() + .HandleResult(response => !response.IsSuccessStatusCode) // Handle results + .Handle() // Or handle exceptions, chaining is supported +}; +``` + diff --git a/src/Snippets/Docs/ResilienceStrategies.cs b/src/Snippets/Docs/ResilienceStrategies.cs index 4af04756d32..c0f0dd65507 100644 --- a/src/Snippets/Docs/ResilienceStrategies.cs +++ b/src/Snippets/Docs/ResilienceStrategies.cs @@ -20,29 +20,37 @@ public static async Task Usage() #endregion } - public static void ShouldHandle() + public static void ShouldHandleManual() { - #region should-handle + #region should-handle-manual - // Create an instance of options for a retry strategy. In this example, - // we use RetryStrategyOptions. You could also use other options like - // CircuitBreakerStrategyOptions or FallbackStrategyOptions. - var options = new RetryStrategyOptions(); + var options = new RetryStrategyOptions + { + // For greater flexibility, you can directly use the ShouldHandle delegate with switch expressions. + ShouldHandle = args => args.Outcome switch + { + // Strategies may offer rich arguments for result handling. + // For instance, the retry strategy exposes the number of attempts made. + _ when args.AttemptNumber > 3 => PredicateResult.False(), + { Exception: HttpRequestException } => PredicateResult.True(), + { Result: HttpResponseMessage response } when !response.IsSuccessStatusCode => PredicateResult.True(), + _ => PredicateResult.False() + } + }; - // PredicateBuilder can simplify the setup of the ShouldHandle predicate. - options.ShouldHandle = new PredicateBuilder() - .HandleResult(response => !response.IsSuccessStatusCode) - .Handle(); + #endregion + } + + public static void ShouldHandlePredicateBuilder() + { + #region should-handle-predicate-builder - // For greater flexibility, you can directly use the ShouldHandle delegate with switch expressions. - options.ShouldHandle = args => args.Outcome switch + // Use PredicateBuilder to simplify the setup of the ShouldHandle predicate. + var options = new RetryStrategyOptions { - // Strategies may offer additional context for result handling. - // For instance, the retry strategy exposes the number of attempts made. - _ when args.AttemptNumber > 3 => PredicateResult.False(), - { Exception: HttpRequestException } => PredicateResult.True(), - { Result: HttpResponseMessage response } when !response.IsSuccessStatusCode => PredicateResult.True(), - _ => PredicateResult.False() + ShouldHandle = new PredicateBuilder() + .HandleResult(response => !response.IsSuccessStatusCode) // Handle results + .Handle() // Or handle exceptions, chaining is supported }; #endregion From f5c371d4cee2f173bc040a74b5f9cfaa53fa0d06 Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Tue, 3 Oct 2023 20:16:04 +0200 Subject: [PATCH 2/8] fixes --- src/Snippets/Docs/ResilienceStrategies.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Snippets/Docs/ResilienceStrategies.cs b/src/Snippets/Docs/ResilienceStrategies.cs index c0f0dd65507..1358afec1e2 100644 --- a/src/Snippets/Docs/ResilienceStrategies.cs +++ b/src/Snippets/Docs/ResilienceStrategies.cs @@ -50,7 +50,7 @@ public static void ShouldHandlePredicateBuilder() { ShouldHandle = new PredicateBuilder() .HandleResult(response => !response.IsSuccessStatusCode) // Handle results - .Handle() // Or handle exceptions, chaining is supported + .Handle() // Or handle exceptions, chaining is supported }; #endregion From 6b24407592f9793c8cc832978f8e08317dbd0840 Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Tue, 3 Oct 2023 20:18:28 +0200 Subject: [PATCH 3/8] Cleanup section names --- docs/strategies/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/strategies/index.md b/docs/strategies/index.md index 825bee03102..d8e4edbbc0f 100644 --- a/docs/strategies/index.md +++ b/docs/strategies/index.md @@ -43,7 +43,7 @@ ResiliencePipeline pipeline = new ResiliencePipelineBuilder() > [!NOTE] > The configuration options are automatically validated by Polly and come with sensible defaults. Therefore, you don't have to specify all the properties unless needed. -## Fault handling using predicates +## Fault handling Each reactive strategy provides access to the `ShouldHandle` predicate property. This property offers a mechanism to decide whether the resilience strategy should manage the fault or result returned after execution. @@ -54,7 +54,7 @@ Setting up the predicate can be accomplished in the following ways: The examples below illustrate both methods: -### Configure predicates manually +### Fault handling using switch expressions ```cs @@ -77,7 +77,7 @@ var options = new RetryStrategyOptions - `PredicateResult.True()` is a shorthand for `new ValueTask(true)`. - All `ShouldHandle` predicates are asynchronous and use the type `Func, ValueTask>`. The `Args` acts as a placeholder, and each strategy defines its own arguments. -### Configure predicates using `PredicateBuilder` +### Fault handling using `PredicateBuilder` , or , is a utility API aimed at simplifying the configuration of predicates. From ffa41944c85987404da0337fe92e2a2fb18c0d9b Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Tue, 3 Oct 2023 20:28:31 +0200 Subject: [PATCH 4/8] Cleanup --- docs/strategies/index.md | 21 +++++++++++++++++---- src/Snippets/Docs/ResilienceStrategies.cs | 4 +++- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/docs/strategies/index.md b/docs/strategies/index.md index d8e4edbbc0f..95873499b69 100644 --- a/docs/strategies/index.md +++ b/docs/strategies/index.md @@ -67,6 +67,7 @@ var options = new RetryStrategyOptions // For instance, the retry strategy exposes the number of attempts made. _ when args.AttemptNumber > 3 => PredicateResult.False(), { Exception: HttpRequestException } => PredicateResult.True(), + { Exception: TimeoutRejectedException } => PredicateResult.True(), // You can handle multiple exceptions { Result: HttpResponseMessage response } when !response.IsSuccessStatusCode => PredicateResult.True(), _ => PredicateResult.False() } @@ -74,13 +75,14 @@ var options = new RetryStrategyOptions ``` +Notes from the preceding example: + - `PredicateResult.True()` is a shorthand for `new ValueTask(true)`. -- All `ShouldHandle` predicates are asynchronous and use the type `Func, ValueTask>`. The `Args` acts as a placeholder, and each strategy defines its own arguments. +- `ShouldHandle` predicates are asynchronous and use the type `Func, ValueTask>`. The `Args` acts as a placeholder, and each strategy defines its own arguments. +- Multiple exceptions can be handled using switch expressions. ### Fault handling using `PredicateBuilder` -, or , is a utility API aimed at simplifying the configuration of predicates. - ```cs // Use PredicateBuilder to simplify the setup of the ShouldHandle predicate. @@ -88,7 +90,18 @@ var options = new RetryStrategyOptions { ShouldHandle = new PredicateBuilder() .HandleResult(response => !response.IsSuccessStatusCode) // Handle results - .Handle() // Or handle exceptions, chaining is supported + .Handle() // Or handle exception + .Handle() // Chaining is supported }; ``` + +- , or , is a utility API aimed at simplifying the configuration of predicates. + +The preceding sample: + +- Uses `HandleResult` to register predicate that determines whether the result should be handled or not. +- Uses `Handle` to handle multiple exceptions types. + +> [!NOTE] +> Compared to configuring the predicate manually, the approach using `PredicateBuilder` is slightly slower because each method call on this type registers a new predicate that needs to be invoked when evaluating the outcome of the execution. diff --git a/src/Snippets/Docs/ResilienceStrategies.cs b/src/Snippets/Docs/ResilienceStrategies.cs index 1358afec1e2..4a0d13b1173 100644 --- a/src/Snippets/Docs/ResilienceStrategies.cs +++ b/src/Snippets/Docs/ResilienceStrategies.cs @@ -33,6 +33,7 @@ public static void ShouldHandleManual() // For instance, the retry strategy exposes the number of attempts made. _ when args.AttemptNumber > 3 => PredicateResult.False(), { Exception: HttpRequestException } => PredicateResult.True(), + { Exception: TimeoutRejectedException } => PredicateResult.True(), // You can handle multiple exceptions { Result: HttpResponseMessage response } when !response.IsSuccessStatusCode => PredicateResult.True(), _ => PredicateResult.False() } @@ -50,7 +51,8 @@ public static void ShouldHandlePredicateBuilder() { ShouldHandle = new PredicateBuilder() .HandleResult(response => !response.IsSuccessStatusCode) // Handle results - .Handle() // Or handle exceptions, chaining is supported + .Handle() // Or handle exception + .Handle() // Chaining is supported }; #endregion From f4fbf63830b53b6441cefde8ccb703eaab8db7e0 Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Tue, 3 Oct 2023 21:32:21 +0200 Subject: [PATCH 5/8] Cleanup --- docs/strategies/index.md | 32 +++++++++++++++++++++-- src/Snippets/Docs/ResilienceStrategies.cs | 28 ++++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/docs/strategies/index.md b/docs/strategies/index.md index 95873499b69..bfd2011b188 100644 --- a/docs/strategies/index.md +++ b/docs/strategies/index.md @@ -54,7 +54,7 @@ Setting up the predicate can be accomplished in the following ways: The examples below illustrate both methods: -### Fault handling using switch expressions +### Fault handling: Predicates ```cs @@ -77,11 +77,39 @@ var options = new RetryStrategyOptions Notes from the preceding example: +- Switch expressions are used to determine whether to retry on not. - `PredicateResult.True()` is a shorthand for `new ValueTask(true)`. - `ShouldHandle` predicates are asynchronous and use the type `Func, ValueTask>`. The `Args` acts as a placeholder, and each strategy defines its own arguments. - Multiple exceptions can be handled using switch expressions. -### Fault handling using `PredicateBuilder` +### Fault handling: Asynchronous predicates + +You can also use asynchronous delegates for more advanced scenarios, such as retrying based on the response body. + + +```cs +var options = new RetryStrategyOptions +{ + ShouldHandle = async args => + { + if (args.Outcome.Exception is not null) + { + return args.Outcome.Exception switch + { + HttpRequestException => true, + TimeoutRejectedException => true, + _ => false + }; + } + + // Determine whether to retry asynchronously or not based on the result. + return await ShouldRetryAsync(args.Outcome.Result!, args.Context.CancellationToken); + } +}; +``` + + +### Fault handling: `PredicateBuilder` ```cs diff --git a/src/Snippets/Docs/ResilienceStrategies.cs b/src/Snippets/Docs/ResilienceStrategies.cs index 4a0d13b1173..d8eba1068c7 100644 --- a/src/Snippets/Docs/ResilienceStrategies.cs +++ b/src/Snippets/Docs/ResilienceStrategies.cs @@ -42,6 +42,34 @@ public static void ShouldHandleManual() #endregion } + public static void ShouldHandleManualAsync() + { + #region should-handle-manual-async + + var options = new RetryStrategyOptions + { + ShouldHandle = async args => + { + if (args.Outcome.Exception is not null) + { + return args.Outcome.Exception switch + { + HttpRequestException => true, + TimeoutRejectedException => true, + _ => false + }; + } + + // Determine whether to retry asynchronously or not based on the result. + return await ShouldRetryAsync(args.Outcome.Result!, args.Context.CancellationToken); + } + }; + + #endregion + } + + private static Task ShouldRetryAsync(HttpResponseMessage response, CancellationToken cancellationToken) => Task.FromResult(true); + public static void ShouldHandlePredicateBuilder() { #region should-handle-predicate-builder From cce012883f79b720d42965707ea12b8bb91a2d6b Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Tue, 3 Oct 2023 21:34:11 +0200 Subject: [PATCH 6/8] cleanup --- docs/strategies/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/strategies/index.md b/docs/strategies/index.md index bfd2011b188..1f65e669653 100644 --- a/docs/strategies/index.md +++ b/docs/strategies/index.md @@ -54,7 +54,7 @@ Setting up the predicate can be accomplished in the following ways: The examples below illustrate both methods: -### Fault handling: Predicates +### Predicates ```cs @@ -82,7 +82,7 @@ Notes from the preceding example: - `ShouldHandle` predicates are asynchronous and use the type `Func, ValueTask>`. The `Args` acts as a placeholder, and each strategy defines its own arguments. - Multiple exceptions can be handled using switch expressions. -### Fault handling: Asynchronous predicates +### Asynchronous predicates You can also use asynchronous delegates for more advanced scenarios, such as retrying based on the response body. @@ -109,7 +109,7 @@ var options = new RetryStrategyOptions ``` -### Fault handling: `PredicateBuilder` +### Predicate builder ```cs From 1d882be6fee51513376b2723c1e5872bdfc8d33b Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Tue, 3 Oct 2023 21:45:45 +0200 Subject: [PATCH 7/8] cleanup --- docs/strategies/index.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/strategies/index.md b/docs/strategies/index.md index 1f65e669653..64aa258c360 100644 --- a/docs/strategies/index.md +++ b/docs/strategies/index.md @@ -52,7 +52,7 @@ Setting up the predicate can be accomplished in the following ways: - **Manually setting the predicate**: Directly configure the predicate. The advised approach involves using [switch expressions](https://learn.microsoft.com/dotnet/csharp/language-reference/operators/switch-expression) for maximum flexibility, and also allows the incorporation of asynchronous predicates. - **Employing `PredicateBuilder`**: The `PredicateBuilder` provides a more straightforward method to configure the predicates, akin to predicate setups in earlier Polly versions. -The examples below illustrate both methods: +The examples below illustrate these: ### Predicates @@ -84,7 +84,7 @@ Notes from the preceding example: ### Asynchronous predicates -You can also use asynchronous delegates for more advanced scenarios, such as retrying based on the response body. +You can also use asynchronous delegates for more advanced scenarios, such as retrying based on the response body: ```cs @@ -111,6 +111,8 @@ var options = new RetryStrategyOptions ### Predicate builder +, or , is a utility class aimed at simplifying the configuration of predicates: + ```cs // Use PredicateBuilder to simplify the setup of the ShouldHandle predicate. @@ -124,12 +126,10 @@ var options = new RetryStrategyOptions ``` -- , or , is a utility API aimed at simplifying the configuration of predicates. - The preceding sample: - Uses `HandleResult` to register predicate that determines whether the result should be handled or not. - Uses `Handle` to handle multiple exceptions types. > [!NOTE] -> Compared to configuring the predicate manually, the approach using `PredicateBuilder` is slightly slower because each method call on this type registers a new predicate that needs to be invoked when evaluating the outcome of the execution. +> When using `PredicateBuilder` instead of manually configuring the predicate, there is a minor performance impact. Each method call on `PredicateBuilder` registers a new predicate, which must be invoked when evaluating the outcome. From c761e92d5a4412fe00c77de252473a9130cd519a Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Wed, 4 Oct 2023 09:08:11 +0200 Subject: [PATCH 8/8] PR comments --- README.md | 2 +- docs/strategies/index.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 391357ed03d..aa5a49c5447 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ We are a member of the [.NET Foundation](https://www.dotnetfoundation.org/about) ## 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 [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 [pollydocs.org][polly-docs]. ## Quick start diff --git a/docs/strategies/index.md b/docs/strategies/index.md index 64aa258c360..a20cc6059e1 100644 --- a/docs/strategies/index.md +++ b/docs/strategies/index.md @@ -50,7 +50,7 @@ Each reactive strategy provides access to the `ShouldHandle` predicate property. Setting up the predicate can be accomplished in the following ways: - **Manually setting the predicate**: Directly configure the predicate. The advised approach involves using [switch expressions](https://learn.microsoft.com/dotnet/csharp/language-reference/operators/switch-expression) for maximum flexibility, and also allows the incorporation of asynchronous predicates. -- **Employing `PredicateBuilder`**: The `PredicateBuilder` provides a more straightforward method to configure the predicates, akin to predicate setups in earlier Polly versions. +- **Employing `PredicateBuilder`**: The `PredicateBuilder` class provides a more straight-forward method to configure the predicates, akin to predicate setups in earlier Polly versions. The examples below illustrate these: @@ -128,7 +128,7 @@ var options = new RetryStrategyOptions The preceding sample: -- Uses `HandleResult` to register predicate that determines whether the result should be handled or not. +- Uses `HandleResult` to register a predicate that determines whether the result should be handled or not. - Uses `Handle` to handle multiple exceptions types. > [!NOTE]