From 26f75eae252bda6ade564f71ca522c093b04e78a Mon Sep 17 00:00:00 2001 From: peter-csala Date: Mon, 15 Apr 2024 14:52:17 +0200 Subject: [PATCH 01/29] Unify Timeout, Retry --- docs/strategies/retry.md | 29 ++++++++++++++++------------- docs/strategies/timeout.md | 35 +++++++++++++++++++---------------- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/docs/strategies/retry.md b/docs/strategies/retry.md index 5c3be0a6d63..0fba4a07d4b 100644 --- a/docs/strategies/retry.md +++ b/docs/strategies/retry.md @@ -2,14 +2,17 @@ ## About -- **Options**: +- **Option(s)**: - [`RetryStrategyOptions`](xref:Polly.Retry.RetryStrategyOptions) - [`RetryStrategyOptions`](xref:Polly.Retry.RetryStrategyOptions`1) -- **Extensions**: `AddRetry` -- **Strategy Type**: Reactive +- **Extension(s)**: + - `AddRetry` +- **Exception(s)**: - --- +The retry **reactive** resilience strategy re-executes the same callback method if its execution fails. Failure can be an `Exception` or a result object indicating unsuccessful processing. Between the retry attempts the retry strategy waits a specified amount of time. You have a fine-grained control how to calculate the next delay. The retry strategy stops re-performing the same callback when it either runs out of the maximum allowed retry attempts or an unhandled exception is thrown / unhandled result object is returned. + ## Usage @@ -97,16 +100,16 @@ new ResiliencePipelineBuilder().AddRetry(optionsExtractDela ## Defaults -| Property | Default Value | Description | -|--------------------|----------------------------------------------------------------------------|------------------------------------------------------------------------------------------| -| `ShouldHandle` | Predicate that handles all exceptions except `OperationCanceledException`. | Predicate that determines what results and exceptions are handled by the retry strategy. | -| `MaxRetryAttempts` | 3 | The maximum number of retries to use, in addition to the original call. | -| `Delay` | 2 seconds | The base delay between retries. | -| `BackoffType` | Constant | The type of the back-off used to generate the retry delay. | -| `UseJitter` | False | Allows adding jitter to retry delays. | -| `DelayGenerator` | `null` | Used for generating custom delays for retries. | -| `OnRetry` | `null` | Action executed when retry occurs. | -| `MaxDelay` | `null` | Caps the calculated retry delay to a specified maximum duration. | +| Property | Default Value | Description | +|--------------------|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------------| +| `ShouldHandle` | Any exceptions except `OperationCanceledException`. | It defines a predicate to determine what results and/or exceptions are handled by the retry strategy. | +| `MaxRetryAttempts` | 3 | The maximum number of retry attempts to use, in addition to the original call. | +| `BackoffType` | Constant | The back-off algorithm type to generate the delay(s) between retry attempts. | +| `Delay` | 2 seconds | The *base* delay between retry attempts. See the next section for more details. | +| `MaxDelay` | `null` | If provided then the strategy caps the calculated retry delay to this value. | +| `UseJitter` | False | If set to `true` a jitter (random value) is added to retry delays. See the next section for more details. | +| `DelayGenerator` | `null` | This method allows you to **dynamically** calculate the retry delay based on runtime accessible values. | +| `OnRetry` | `null` | If provided then it will be invoked before the strategy delays the next attempt. | ## Calculation of the next delay diff --git a/docs/strategies/timeout.md b/docs/strategies/timeout.md index 66753fa71a2..b835f390c8c 100644 --- a/docs/strategies/timeout.md +++ b/docs/strategies/timeout.md @@ -2,15 +2,16 @@ ## About -- **Options**: [`TimeoutStrategyOptions`](xref:Polly.Timeout.TimeoutStrategyOptions) -- **Extensions**: `AddTimeout` -- **Strategy Type**: Proactive -- **Exceptions**: +- **Option(s)**: + - [`TimeoutStrategyOptions`](xref:Polly.Timeout.TimeoutStrategyOptions) +- **Extension(s)**: + - `AddTimeout` +- **Exception(s)**: - `TimeoutRejectedException`: Thrown when a delegate executed through a timeout strategy does not complete before the timeout. --- -The timeout resilience strategy cancels the execution if it does not complete within the specified timeout period. If the execution is canceled by the timeout strategy, it throws a `TimeoutRejectedException`. The timeout strategy operates by wrapping the incoming cancellation token with a new one. Should the original token be canceled, the timeout strategy will transparently honor the original cancellation token without throwing a `TimeoutRejectedException`. +The timeout **proactive** resilience strategy cancels the execution if it does not complete within the specified timeout period. If the execution is canceled by the timeout strategy, it throws a `TimeoutRejectedException`. The timeout strategy operates by wrapping the incoming cancellation token with a new one. Should the original token be canceled, the timeout strategy will transparently honor the original cancellation token without throwing a `TimeoutRejectedException`. > [!IMPORTANT] > It is crucial that the user's callback respects the cancellation token. If it does not, the callback will continue executing even after a cancellation request, thereby ignoring the cancellation. @@ -108,6 +109,19 @@ catch (TimeoutRejectedException) ``` +## Defaults + +| Property | Default Value | Description | +|--------------------|---------------|------------------------------------------------------------------------------------------------------------| +| `Timeout` | 30 seconds | It defines a **static** period within the delegate should complete otherwise it will be cancelled. | +| `TimeoutGenerator` | `null` | This method allows you to **dynamically** calculate the timeout period based on runtime accessible values. | +| `OnTimeout` | `null` | If provided then it will be invoked after the timeout occurred. | + +> [!NOTE] +> If both `Timeout` and `TimeoutGenerator` are specified then `Timeout` will be ignored. + +### OnTimeout versus catching TimeoutRejectedException + The `OnTimeout` user-provided delegate is called just before the strategy throws the `TimeoutRejectedException`. This delegate receives a parameter which allows you to access the `Context` object as well as the `Timeout`: - Accessing the `Context` is also possible via a different `Execute{Async}` overload. @@ -117,17 +131,6 @@ So, what is the purpose of the `OnTimeout` in case of static timeout settings? The `OnTimeout` delegate can be useful when you define a resilience pipeline which consists of multiple strategies. For example you have a timeout as the inner strategy and a retry as the outer strategy. If the retry is defined to handle `TimeoutRejectedException`, that means the `Execute{Async}` may or may not throw that exception depending on future attempts. So, if you want to get notification about the fact that a timeout has occurred, you have to provide a delegate to the `OnTimeout` property. -## Defaults - -| Property | Default Value | Description | -| ------------------ | ------------- | -------------------------------------------- | -| `Timeout` | 30 seconds | The default timeout used by the strategy. | -| `TimeoutGenerator` | `null` | Generates the timeout for a given execution. | -| `OnTimeout` | `null` | Event that is raised when timeout occurs. | - -> [!NOTE] -> If both `Timeout` and `TimeoutGenerator` are specified then `Timeout` will be ignored. - ## Diagrams ### Happy path sequence diagram From 547553e007df0d97db5bc25954737011a964b0e1 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Mon, 15 Apr 2024 15:03:14 +0200 Subject: [PATCH 02/29] Add runtime to the wordlist --- .github/wordlist.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/wordlist.txt b/.github/wordlist.txt index 0ddf7ccf1ce..794bc56c923 100644 --- a/.github/wordlist.txt +++ b/.github/wordlist.txt @@ -51,6 +51,7 @@ rethrow rethrows retryable reusability +runtime saas sdk serializers From 950a84c87ea8e8d1ddc176a6f06290b9542429f6 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Mon, 15 Apr 2024 15:11:44 +0200 Subject: [PATCH 03/29] Fix heading --- docs/strategies/timeout.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/strategies/timeout.md b/docs/strategies/timeout.md index b835f390c8c..9af79d1f9d9 100644 --- a/docs/strategies/timeout.md +++ b/docs/strategies/timeout.md @@ -120,7 +120,7 @@ catch (TimeoutRejectedException) > [!NOTE] > If both `Timeout` and `TimeoutGenerator` are specified then `Timeout` will be ignored. -### OnTimeout versus catching TimeoutRejectedException +### `OnTimeout` versus catching `TimeoutRejectedException` The `OnTimeout` user-provided delegate is called just before the strategy throws the `TimeoutRejectedException`. This delegate receives a parameter which allows you to access the `Context` object as well as the `Timeout`: From fa7b6ba65ed30359fec065af3abc1f6ef17214a3 Mon Sep 17 00:00:00 2001 From: peter-csala <57183693+peter-csala@users.noreply.github.com> Date: Mon, 15 Apr 2024 15:41:41 +0200 Subject: [PATCH 04/29] Apply suggestions from code review Co-authored-by: Martin Costello --- docs/strategies/retry.md | 6 +++--- docs/strategies/timeout.md | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/strategies/retry.md b/docs/strategies/retry.md index 0fba4a07d4b..0e8df14dc83 100644 --- a/docs/strategies/retry.md +++ b/docs/strategies/retry.md @@ -11,7 +11,7 @@ --- -The retry **reactive** resilience strategy re-executes the same callback method if its execution fails. Failure can be an `Exception` or a result object indicating unsuccessful processing. Between the retry attempts the retry strategy waits a specified amount of time. You have a fine-grained control how to calculate the next delay. The retry strategy stops re-performing the same callback when it either runs out of the maximum allowed retry attempts or an unhandled exception is thrown / unhandled result object is returned. +The retry **reactive** resilience strategy re-executes the same callback method if its execution fails. Failure can be an `Exception` or a result object indicating unsuccessful processing. Between the retry attempts the retry strategy waits a specified amount of time. You have fine-grained control over how to calculate the next delay. The retry strategy stops invoking the same callback when it reaches the maximum allowed number of retry attempts or an unhandled exception is thrown / result object indicating a failure is returned. ## Usage @@ -102,12 +102,12 @@ new ResiliencePipelineBuilder().AddRetry(optionsExtractDela | Property | Default Value | Description | |--------------------|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------------| -| `ShouldHandle` | Any exceptions except `OperationCanceledException`. | It defines a predicate to determine what results and/or exceptions are handled by the retry strategy. | +| `ShouldHandle` | Any exceptions other than `OperationCanceledException`. | Defines a predicate to determine what results and/or exceptions are handled by the retry strategy. | | `MaxRetryAttempts` | 3 | The maximum number of retry attempts to use, in addition to the original call. | | `BackoffType` | Constant | The back-off algorithm type to generate the delay(s) between retry attempts. | | `Delay` | 2 seconds | The *base* delay between retry attempts. See the next section for more details. | | `MaxDelay` | `null` | If provided then the strategy caps the calculated retry delay to this value. | -| `UseJitter` | False | If set to `true` a jitter (random value) is added to retry delays. See the next section for more details. | +| `UseJitter` | False | If set to `true`, a jitter (random value) is added to retry delays. See the next section for more details. | | `DelayGenerator` | `null` | This method allows you to **dynamically** calculate the retry delay based on runtime accessible values. | | `OnRetry` | `null` | If provided then it will be invoked before the strategy delays the next attempt. | diff --git a/docs/strategies/timeout.md b/docs/strategies/timeout.md index 9af79d1f9d9..6aa1353380c 100644 --- a/docs/strategies/timeout.md +++ b/docs/strategies/timeout.md @@ -113,8 +113,8 @@ catch (TimeoutRejectedException) | Property | Default Value | Description | |--------------------|---------------|------------------------------------------------------------------------------------------------------------| -| `Timeout` | 30 seconds | It defines a **static** period within the delegate should complete otherwise it will be cancelled. | -| `TimeoutGenerator` | `null` | This method allows you to **dynamically** calculate the timeout period based on runtime accessible values. | +| `Timeout` | 30 seconds | Defines a **static** period within which the delegate should complete, otherwise it will be cancelled. | +| `TimeoutGenerator` | `null` | This delegate allows you to **dynamically** calculate the timeout period based on runtime accessible values. | | `OnTimeout` | `null` | If provided then it will be invoked after the timeout occurred. | > [!NOTE] From 8f6cbc572dbf424ac0d89bd4f59003dfc642b433 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Mon, 15 Apr 2024 16:33:02 +0200 Subject: [PATCH 05/29] Apply suggestions --- docs/strategies/retry.md | 20 ++++++++++---------- docs/strategies/timeout.md | 10 +++++----- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/strategies/retry.md b/docs/strategies/retry.md index 0e8df14dc83..ea00f06e22f 100644 --- a/docs/strategies/retry.md +++ b/docs/strategies/retry.md @@ -100,16 +100,16 @@ new ResiliencePipelineBuilder().AddRetry(optionsExtractDela ## Defaults -| Property | Default Value | Description | -|--------------------|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------------| -| `ShouldHandle` | Any exceptions other than `OperationCanceledException`. | Defines a predicate to determine what results and/or exceptions are handled by the retry strategy. | -| `MaxRetryAttempts` | 3 | The maximum number of retry attempts to use, in addition to the original call. | -| `BackoffType` | Constant | The back-off algorithm type to generate the delay(s) between retry attempts. | -| `Delay` | 2 seconds | The *base* delay between retry attempts. See the next section for more details. | -| `MaxDelay` | `null` | If provided then the strategy caps the calculated retry delay to this value. | -| `UseJitter` | False | If set to `true`, a jitter (random value) is added to retry delays. See the next section for more details. | -| `DelayGenerator` | `null` | This method allows you to **dynamically** calculate the retry delay based on runtime accessible values. | -| `OnRetry` | `null` | If provided then it will be invoked before the strategy delays the next attempt. | +| Property | Default Value | Description | +|--------------------|---------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `ShouldHandle` | Any exceptions other than `OperationCanceledException`. | Defines a predicate to determine what results and/or exceptions are handled by the retry strategy. | +| `MaxRetryAttempts` | 3 | The maximum number of retry attempts to use, in addition to the original call. | +| `BackoffType` | Constant | The back-off algorithm type to generate the delay(s) between retry attempts. | +| `Delay` | 2 seconds | The *base* delay between retry attempts. See the next section for more details. | +| `MaxDelay` | `null` | If provided then the strategy caps the calculated retry delay to this value. | +| `UseJitter` | False | If set to `true`, a jitter (random value) is added to retry delays. See the next section for more details. | +| `DelayGenerator` | `null` | This delegate allows you to **dynamically** calculate the retry delay by utilizing information that is only available at runtime (like the attempt number). | +| `OnRetry` | `null` | If provided then it will be invoked before the strategy delays the next attempt. | ## Calculation of the next delay diff --git a/docs/strategies/timeout.md b/docs/strategies/timeout.md index 6aa1353380c..8b5fe822452 100644 --- a/docs/strategies/timeout.md +++ b/docs/strategies/timeout.md @@ -111,11 +111,11 @@ catch (TimeoutRejectedException) ## Defaults -| Property | Default Value | Description | -|--------------------|---------------|------------------------------------------------------------------------------------------------------------| -| `Timeout` | 30 seconds | Defines a **static** period within which the delegate should complete, otherwise it will be cancelled. | -| `TimeoutGenerator` | `null` | This delegate allows you to **dynamically** calculate the timeout period based on runtime accessible values. | -| `OnTimeout` | `null` | If provided then it will be invoked after the timeout occurred. | +| Property | Default Value | Description | +|--------------------|---------------|--------------------------------------------------------------------------------------------------------------------------------------| +| `Timeout` | 30 seconds | Defines a **static** period within which the delegate should complete, otherwise it will be cancelled. | +| `TimeoutGenerator` | `null` | This delegate allows you to **dynamically** calculate the timeout period by utilizing information that is only available at runtime. | +| `OnTimeout` | `null` | If provided then it will be invoked after the timeout occurred. | > [!NOTE] > If both `Timeout` and `TimeoutGenerator` are specified then `Timeout` will be ignored. From 4c464424cecca32137550628c2c5e5a461c2d1c4 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Tue, 16 Apr 2024 10:41:22 +0200 Subject: [PATCH 06/29] Unify Fallback --- docs/strategies/fallback.md | 24 ++++++++++++++++-------- docs/strategies/retry.md | 2 +- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/docs/strategies/fallback.md b/docs/strategies/fallback.md index 61b7eb18196..ecdf1f6323e 100644 --- a/docs/strategies/fallback.md +++ b/docs/strategies/fallback.md @@ -2,12 +2,19 @@ ## About -- **Options**: [`FallbackStrategyOptions`](xref:Polly.Fallback.FallbackStrategyOptions`1) -- **Extensions**: `AddFallback` -- **Strategy Type**: Reactive +- **Option(s)**: + - [`FallbackStrategyOptions`](xref:Polly.Fallback.FallbackStrategyOptions`1) +- **Extension(s)**: + - `AddFallback` +- **Exception(s)**: - --- +The fallback **reactive** resilience strategy provides a substitute if the execution of the callback fails. Failure can be either an `Exception` or a result object indicating unsuccessful processing. Typically this strategy is used as a last resort, meaning that if all other strategies failed to overcome the transient failure you could still provide a fallback value to the caller. + +> [!NOTE] +> In this document the *fallback*, *substitute*, and *surrogate* terms are used interchangeably. + ## Usage @@ -59,11 +66,12 @@ new ResiliencePipelineBuilder().AddFallback(optionsOnFallback); ## Defaults -| Property | Default Value | Description | -| ---------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | -| `ShouldHandle` | Predicate that handles all exceptions except `OperationCanceledException`. | Predicate that determines what results and exceptions are handled by the fallback strategy. | -| `FallbackAction` | `Null`, **Required** | Fallback action to be executed. | -| `OnFallback` | `null` | Event that is raised when fallback happens. | +| Property | Default Value | Description | +|----------------|---------------------------------------------------------|-------------------------------------------------------------------------------------------------------| +| `ShouldHandle` | Any exceptions other than `OperationCanceledException`. | Defines a predicate to determine what results and/or exceptions are handled by the fallback strategy. | + +| `FallbackAction` | `Null`, **Required** | This delegate allows you to **dynamically** calculate the surrogate value by utilizing information that is only available at runtime (like the outcome). | +| `OnFallback` | `null` | If provided then it will be invoked before the strategy calculates the fallback value. | ## Diagrams diff --git a/docs/strategies/retry.md b/docs/strategies/retry.md index ea00f06e22f..b24aa1fa96e 100644 --- a/docs/strategies/retry.md +++ b/docs/strategies/retry.md @@ -11,7 +11,7 @@ --- -The retry **reactive** resilience strategy re-executes the same callback method if its execution fails. Failure can be an `Exception` or a result object indicating unsuccessful processing. Between the retry attempts the retry strategy waits a specified amount of time. You have fine-grained control over how to calculate the next delay. The retry strategy stops invoking the same callback when it reaches the maximum allowed number of retry attempts or an unhandled exception is thrown / result object indicating a failure is returned. +The retry **reactive** resilience strategy re-executes the same callback method if its execution fails. Failure can be either an `Exception` or a result object indicating unsuccessful processing. Between the retry attempts the retry strategy waits a specified amount of time. You have fine-grained control over how to calculate the next delay. The retry strategy stops invoking the same callback when it reaches the maximum allowed number of retry attempts or an unhandled exception is thrown / result object indicating a failure is returned. ## Usage From ac1a2bb97de8b6ac518f9353d090fdeb9751ac82 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Tue, 16 Apr 2024 11:02:28 +0200 Subject: [PATCH 07/29] Unify Hedging --- docs/strategies/fallback.md | 1 - docs/strategies/hedging.md | 28 +++++++++++++++------------- docs/strategies/retry.md | 20 ++++++++++---------- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/docs/strategies/fallback.md b/docs/strategies/fallback.md index ecdf1f6323e..ba35f862f1d 100644 --- a/docs/strategies/fallback.md +++ b/docs/strategies/fallback.md @@ -69,7 +69,6 @@ new ResiliencePipelineBuilder().AddFallback(optionsOnFallback); | Property | Default Value | Description | |----------------|---------------------------------------------------------|-------------------------------------------------------------------------------------------------------| | `ShouldHandle` | Any exceptions other than `OperationCanceledException`. | Defines a predicate to determine what results and/or exceptions are handled by the fallback strategy. | - | `FallbackAction` | `Null`, **Required** | This delegate allows you to **dynamically** calculate the surrogate value by utilizing information that is only available at runtime (like the outcome). | | `OnFallback` | `null` | If provided then it will be invoked before the strategy calculates the fallback value. | diff --git a/docs/strategies/hedging.md b/docs/strategies/hedging.md index 515ca0dabf1..389e77b3c08 100644 --- a/docs/strategies/hedging.md +++ b/docs/strategies/hedging.md @@ -2,15 +2,17 @@ ## About -- **Options**: [`HedgingStrategyOptions`](xref:Polly.Hedging.HedgingStrategyOptions`1) -- **Extensions**: `AddHedging` -- **Strategy Type**: Reactive +- **Option(s)**: + - [`HedgingStrategyOptions`](xref:Polly.Hedging.HedgingStrategyOptions`1) +- **Extension(s)**: + - `AddHedging` +- **Exception(s)**: - --- -The hedging strategy enables the re-execution of a user-defined callback if the previous execution takes too long. This approach gives you the option to either run the original callback again or specify a new callback for subsequent hedged attempts. Implementing a hedging strategy can boost the overall responsiveness of the system. However, it's essential to note that this improvement comes at the cost of increased resource utilization. If low latency is not a critical requirement, you may find the [retry strategy](retry.md) is more appropriate. +The hedging **reactive** strategy enables the re-execution of the callback if the previous execution takes too long. This approach gives you the option to either run the original callback again or specify a new callback for subsequent *hedged* attempts. Implementing a hedging strategy can boost the overall responsiveness of the system. However, it's essential to note that this improvement comes at the cost of increased resource utilization. If low latency is not a critical requirement, you may find the [retry strategy](retry.md) more appropriate. -This strategy also supports multiple [concurrency modes](#concurrency-modes) for added flexibility. +This strategy also supports multiple [concurrency modes](#concurrency-modes) to flexibly tailor the behavior for your own needs. > [!NOTE] > Please do not start any background work when executing actions using the hedging strategy. This strategy can spawn multiple parallel tasks, and as a result multiple background tasks can be started. @@ -59,14 +61,14 @@ new ResiliencePipelineBuilder().AddHedging(optionsDefaults) ## Defaults -| Property | Default Value | Description | -|---------------------|----------------------------------------------------------------------------|------------------------------------------------------------------------------------------| -| `ShouldHandle` | Predicate that handles all exceptions except `OperationCanceledException`. | Predicate that determines what results and exceptions are handled by the retry strategy. | -| `MaxHedgedAttempts` | 1 | The maximum number of hedged actions to use, in addition to the original action. | -| `Delay` | 2 seconds | The maximum waiting time before spawning a new hedged action. | -| `ActionGenerator` | Returns the original callback that was passed to the hedging strategy. | Generator that creates hedged actions. | -| `DelayGenerator` | `null` | Used for generating custom delays for hedging. | -| `OnHedging` | `null` | Event that is raised when a hedging is performed. | +| Property | Default Value | Description | +|---------------------|--------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `ShouldHandle` | Any exceptions other than `OperationCanceledException`. | Defines a predicate to determine what results and/or exceptions are handled by the hedging strategy. | +| `MaxHedgedAttempts` | 1 | The maximum number of hedged actions to use, in addition to the original action. | +| `Delay` | 2 seconds | The maximum waiting time before spawning a new hedged action. | +| `ActionGenerator` | It returns the original callback that was passed to this strategy. | This delegate allows you to **dynamically** calculate the hedged action by utilizing information that is only available at runtime (like the attempt number). | +| `DelayGenerator` | `null` | This optional delegate allows you to **dynamically** calculate the delay by utilizing information that is only available at runtime (like the attempt number). | +| `OnHedging` | `null` | If provided then it will be invoked before the strategy performs the hedged action. | You can use the following special values for `Delay` or in `DelayGenerator`: diff --git a/docs/strategies/retry.md b/docs/strategies/retry.md index b24aa1fa96e..61ee41b62ab 100644 --- a/docs/strategies/retry.md +++ b/docs/strategies/retry.md @@ -100,16 +100,16 @@ new ResiliencePipelineBuilder().AddRetry(optionsExtractDela ## Defaults -| Property | Default Value | Description | -|--------------------|---------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `ShouldHandle` | Any exceptions other than `OperationCanceledException`. | Defines a predicate to determine what results and/or exceptions are handled by the retry strategy. | -| `MaxRetryAttempts` | 3 | The maximum number of retry attempts to use, in addition to the original call. | -| `BackoffType` | Constant | The back-off algorithm type to generate the delay(s) between retry attempts. | -| `Delay` | 2 seconds | The *base* delay between retry attempts. See the next section for more details. | -| `MaxDelay` | `null` | If provided then the strategy caps the calculated retry delay to this value. | -| `UseJitter` | False | If set to `true`, a jitter (random value) is added to retry delays. See the next section for more details. | -| `DelayGenerator` | `null` | This delegate allows you to **dynamically** calculate the retry delay by utilizing information that is only available at runtime (like the attempt number). | -| `OnRetry` | `null` | If provided then it will be invoked before the strategy delays the next attempt. | +| Property | Default Value | Description | +|--------------------|---------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `ShouldHandle` | Any exceptions other than `OperationCanceledException`. | Defines a predicate to determine what results and/or exceptions are handled by the retry strategy. | +| `MaxRetryAttempts` | 3 | The maximum number of retry attempts to use, in addition to the original call. | +| `BackoffType` | Constant | The back-off algorithm type to generate the delay(s) between retry attempts. | +| `Delay` | 2 seconds | The *base* delay between retry attempts. See the next section for more details. | +| `MaxDelay` | `null` | If provided then the strategy caps the calculated retry delay to this value. | +| `UseJitter` | False | If set to `true`, a jitter (random value) is added to retry delays. See the next section for more details. | +| `DelayGenerator` | `null` | This optional delegate allows you to **dynamically** calculate the retry delay by utilizing information that is only available at runtime (like the attempt number). | +| `OnRetry` | `null` | If provided then it will be invoked before the strategy delays the next attempt. | ## Calculation of the next delay From 44b9937c1e13f35d222462fb0c48712778f120f3 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Tue, 16 Apr 2024 11:07:13 +0200 Subject: [PATCH 08/29] Fix linting issue related to italic usage --- docs/strategies/fallback.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/strategies/fallback.md b/docs/strategies/fallback.md index ba35f862f1d..566c1592806 100644 --- a/docs/strategies/fallback.md +++ b/docs/strategies/fallback.md @@ -305,7 +305,7 @@ return await fallback.ExecuteAsync(CallPrimary, CancellationToken.None); ### Nesting `ExecuteAsync` calls -Combining multiple strategies can be achieved in various ways. However, deeply nesting `ExecuteAsync` calls can lead to what's commonly referred to as _`Execute` Hell_. +Combining multiple strategies can be achieved in various ways. However, deeply nesting `ExecuteAsync` calls can lead to what's commonly referred to as *`Execute` Hell*. > [!NOTE] > While this isn't strictly tied to the Fallback mechanism, it's frequently observed when Fallback is the outermost layer. @@ -330,7 +330,7 @@ return result; **Reasoning**: -This is akin to JavaScript's [callback hell](http://callbackhell.com/) or _[the pyramid of doom](https://en.wikipedia.org/wiki/Pyramid_of_doom_(programming))_. It's easy to mistakenly reference the wrong `CancellationToken` parameter. +This is akin to JavaScript's [callback hell](http://callbackhell.com/) or *[the pyramid of doom](https://en.wikipedia.org/wiki/Pyramid_of_doom_(programming))*. It's easy to mistakenly reference the wrong `CancellationToken` parameter. ✅ DO From fe6f713574afdf00d2cdac35d6f786bef7ee7955 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Wed, 17 Apr 2024 10:09:39 +0200 Subject: [PATCH 09/29] Unify Rate limiter --- docs/strategies/rate-limiter.md | 34 +++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/docs/strategies/rate-limiter.md b/docs/strategies/rate-limiter.md index f268f8234cb..f3a091a5ecb 100644 --- a/docs/strategies/rate-limiter.md +++ b/docs/strategies/rate-limiter.md @@ -2,16 +2,20 @@ ## About -- **Options**: [`RateLimiterStrategyOptions`](xref:Polly.RateLimiting.RateLimiterStrategyOptions) -- **Extensions**: `AddRateLimiter`, `AddConcurrencyLimiter` -- **Strategy Type**: Proactive +- **Option(s)**: + - [`RateLimiterStrategyOptions`](xref:Polly.RateLimiting.RateLimiterStrategyOptions) +- **Extension(s)**: + - `AddRateLimiter`, + - `AddConcurrencyLimiter` - **Exceptions**: - `RateLimiterRejectedException`: Thrown when a rate limiter rejects an execution. -- **Package**: [Polly.RateLimiting](https://www.nuget.org/packages/Polly.RateLimiting) + +> [!IMPORTANT] +> The rate limiter strategy resides inside the [Polly.RateLimiting](https://www.nuget.org/packages/Polly.RateLimiting) package, not like the others ([Polly.Core](https://www.nuget.org/packages/Polly.Core)). --- -The rate limiter resilience strategy controls the number of operations that can pass through it. This strategy is a thin layer over the API provided by the [`System.Threading.RateLimiting`](https://www.nuget.org/packages/System.Threading.RateLimiting) package. +The rate limiter **proactive** resilience strategy controls the number of operations that can pass through it. This strategy is a thin layer over the API provided by the [`System.Threading.RateLimiting`](https://www.nuget.org/packages/System.Threading.RateLimiting) package. This strategy can be used in two flavors: control inbound load via rate limiter, control outbound load via concurrency limiter. Further reading: @@ -107,6 +111,16 @@ catch (RateLimiterRejectedException) ``` +## Defaults + +| Property | Default Value | Description | +|-----------------------------|------------------------------------------------------|-------------------------------------------------------------------------------------------------------------| +| `RateLimiter` | `null` | **Dynamically** creates a `RateLimitLease` for executions. | +| `DefaultRateLimiterOptions` | `PermitLimit` set to 1000 and `QueueLimit` set to 0. | If `RateLimiter` is not provided then this options object will be used for the default concurrency limiter. | +| `OnRejected` | `null` | If provided then it will be invoked after the limiter rejected an execution. | + +### `OnRejected` versus catching `RateLimiterRejectedException` + The `OnRejected` user-provided delegate is called just before the strategy throws the `RateLimiterRejectedException`. This delegate receives a parameter which allows you to access the `Context` object as well as the `Lease`: - Accessing the `Context` is also possible via a different `Execute{Async}` overload. @@ -116,13 +130,9 @@ So, what is the purpose of the `OnRejected`? The `OnRejected` delegate can be useful when you define a resilience pipeline which consists of multiple strategies. For example, you have a rate limiter as the inner strategy and a retry as the outer strategy. If the retry is defined to handle `RateLimiterRejectedException`, that means the `Execute{Async}` may or may not throw that exception depending on future attempts. So, if you want to get notification about the fact that the rate limit has been exceeded, you have to provide a delegate to the `OnRejected` property. -## Defaults - -| Property | Default Value | Description | -| --------------------------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------- | -| `RateLimiter` | `null` | Generator that creates a `RateLimitLease` for executions. | -| `DefaultRateLimiterOptions` | `PermitLimit` set to 1000 and `QueueLimit` set to 0. | The options for the default concurrency limiter that will be used when `RateLimiter` is `null`. | -| `OnRejected` | `null` | Event that is raised when the execution is rejected by the rate limiter. | +> [!IMPORTANT] +> The [`RateLimiterRejectedException`](xref:Polly.RateLimiting.RateLimiterRejectedException) has a property called `RetryAfter`. If this optional `TimeSpan` is provided then that means your requests are throttled and you should retry them no sooner than it indicates. +> Please note that this information is not available inside the `OnRejected`. ## Diagrams From 5ffd9c870b1f2beca0da344677e54622e9de0aed Mon Sep 17 00:00:00 2001 From: peter-csala <57183693+peter-csala@users.noreply.github.com> Date: Wed, 17 Apr 2024 12:06:19 +0200 Subject: [PATCH 10/29] Apply suggestions from code review Co-authored-by: Martin Costello --- docs/strategies/rate-limiter.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/strategies/rate-limiter.md b/docs/strategies/rate-limiter.md index f3a091a5ecb..28b8061ef2b 100644 --- a/docs/strategies/rate-limiter.md +++ b/docs/strategies/rate-limiter.md @@ -11,11 +11,11 @@ - `RateLimiterRejectedException`: Thrown when a rate limiter rejects an execution. > [!IMPORTANT] -> The rate limiter strategy resides inside the [Polly.RateLimiting](https://www.nuget.org/packages/Polly.RateLimiting) package, not like the others ([Polly.Core](https://www.nuget.org/packages/Polly.Core)). +> The rate limiter strategy resides inside the [Polly.RateLimiting](https://www.nuget.org/packages/Polly.RateLimiting) package, not in ([Polly.Core](https://www.nuget.org/packages/Polly.Core)) like other strategies. --- -The rate limiter **proactive** resilience strategy controls the number of operations that can pass through it. This strategy is a thin layer over the API provided by the [`System.Threading.RateLimiting`](https://www.nuget.org/packages/System.Threading.RateLimiting) package. This strategy can be used in two flavors: control inbound load via rate limiter, control outbound load via concurrency limiter. +The rate limiter **proactive** resilience strategy controls the number of operations that can pass through it. This strategy is a thin layer over the API provided by the [`System.Threading.RateLimiting`](https://www.nuget.org/packages/System.Threading.RateLimiting) package. This strategy can be used in two flavors: to control inbound load via a rate limiter and to control outbound load via a concurrency limiter. Further reading: @@ -131,8 +131,8 @@ So, what is the purpose of the `OnRejected`? The `OnRejected` delegate can be useful when you define a resilience pipeline which consists of multiple strategies. For example, you have a rate limiter as the inner strategy and a retry as the outer strategy. If the retry is defined to handle `RateLimiterRejectedException`, that means the `Execute{Async}` may or may not throw that exception depending on future attempts. So, if you want to get notification about the fact that the rate limit has been exceeded, you have to provide a delegate to the `OnRejected` property. > [!IMPORTANT] -> The [`RateLimiterRejectedException`](xref:Polly.RateLimiting.RateLimiterRejectedException) has a property called `RetryAfter`. If this optional `TimeSpan` is provided then that means your requests are throttled and you should retry them no sooner than it indicates. -> Please note that this information is not available inside the `OnRejected`. +> The [`RateLimiterRejectedException`](xref:Polly.RateLimiting.RateLimiterRejectedException) has a `RetryAfter` property. If this optional `TimeSpan` is provided then this indicates that your requests are throttled and you should retry them no sooner than the value given. +> Please note that this information is not available inside the `OnRejected` callback. ## Diagrams From 1a2452a95de035809298fbb455b84426e5f36647 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Wed, 17 Apr 2024 12:07:52 +0200 Subject: [PATCH 11/29] Use note instead of important --- docs/strategies/rate-limiter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/strategies/rate-limiter.md b/docs/strategies/rate-limiter.md index 28b8061ef2b..204eed1e39c 100644 --- a/docs/strategies/rate-limiter.md +++ b/docs/strategies/rate-limiter.md @@ -10,7 +10,7 @@ - **Exceptions**: - `RateLimiterRejectedException`: Thrown when a rate limiter rejects an execution. -> [!IMPORTANT] +> [!NOTE] > The rate limiter strategy resides inside the [Polly.RateLimiting](https://www.nuget.org/packages/Polly.RateLimiting) package, not in ([Polly.Core](https://www.nuget.org/packages/Polly.Core)) like other strategies. --- From 661b8d5e82e7263fe18ba2e2feabe6bf59a826d4 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Wed, 17 Apr 2024 15:41:17 +0200 Subject: [PATCH 12/29] Unify Circuit Breaker --- docs/strategies/circuit-breaker.md | 41 ++++++++++++++++++------------ docs/strategies/rate-limiter.md | 2 +- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/docs/strategies/circuit-breaker.md b/docs/strategies/circuit-breaker.md index fdd744c7f8c..f2419c207c1 100644 --- a/docs/strategies/circuit-breaker.md +++ b/docs/strategies/circuit-breaker.md @@ -2,17 +2,20 @@ ## About -- **Options**: +- **Option(s)**: - [`CircuitBreakerStrategyOptions`](xref:Polly.CircuitBreaker.CircuitBreakerStrategyOptions) - [`CircuitBreakerStrategyOptions`](xref:Polly.CircuitBreaker.CircuitBreakerStrategyOptions`1) -- **Extensions**: `AddCircuitBreaker` +- **Extension(s)**: + - `AddCircuitBreaker` - **Strategy Type**: Reactive -- **Exceptions**: +- **Exception(s)**: - `BrokenCircuitException`: Thrown when a circuit is broken and the action could not be executed. - `IsolatedCircuitException`: Thrown when a circuit is isolated (held open) by manual override. --- +The circuit breaker **reactive** resilience strategy shortcuts the execution if the underlying resource is detected as unhealthy. The detection process is done via sampling. If the sampled executions' failure-success ratio exceeds a predefined threshold then circuit breaker will prevent any new executions by throwing a `BrokenCircuitException`. After a preset duration the circuit breaker performs a probe, because the assumption is that this period was enough for the resource to self-heal. Depending on the outcome of the probe the circuit will either allow new executions or still block them. + > [!NOTE] > Be aware that the Circuit Breaker strategy [rethrows all exceptions](https://github.com/App-vNext/Polly/wiki/Circuit-Breaker#exception-handling), including those that are handled. A Circuit Breaker's role is to monitor faults and break the circuit when a certain threshold is reached; it does not manage retries. Combine the Circuit Breaker with a Retry strategy if needed. @@ -91,23 +94,29 @@ new ResiliencePipelineBuilder().AddCircuitBreaker(optionsSt ## Defaults -| Property | Default Value | Description | -| ----------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | -| `ShouldHandle` | Predicate that handles all exceptions except `OperationCanceledException`. | Specifies which results and exceptions are managed by the circuit breaker strategy. | -| `FailureRatio` | 0.1 | The ratio of failures to successes that will cause the circuit to break/open. | -| `MinimumThroughput` | 100 | The minimum number of actions that must occur in the circuit within a specific time slice. | -| `SamplingDuration` | 30 seconds | The time period over which failure ratios are calculated. | -| `BreakDuration` | 5 seconds | The time period for which the circuit will remain broken/open before attempting to reset. | -| `BreakDurationGenerator` | `null` | Enables adaptive adjustment of break duration based on the current state of the circuit. | -| `OnClosed` | `null` | Event triggered when the circuit transitions to the `Closed` state. | -| `OnOpened` | `null` | Event triggered when the circuit transitions to the `Opened` state. | -| `OnHalfOpened` | `null` | Event triggered when the circuit transitions to the `HalfOpened` state. | -| `ManualControl` | `null` | Allows for manual control to isolate or close the circuit. | -| `StateProvider` | `null` | Enables the retrieval of the current state of the circuit. | +| Property | Default Value | Description | +|--------------------------|---------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| +| `ShouldHandle` | Any exceptions other than `OperationCanceledException`. | Defines a predicate to determine what results and/or exceptions are handled by the circuit breaker strategy. | +| `FailureRatio` | 0.1 | The failure-success ratio that will cause the circuit to break/open. `0.1` means 10% failed of all sampled executions. | +| `MinimumThroughput` | 100 | The minimum number of actions that must occur within the specified sampling duration. | +| `SamplingDuration` | 30 seconds | The time period over which the failure-success ratio is calculated. | +| `BreakDuration` | 5 seconds | Defines a **static** time period for which the circuit will remain broken/open before attempting to reset. | +| `BreakDurationGenerator` | `null` | This delegate allows you to **dynamically** calculate the break duration by utilizing information that is only available at runtime (like failure count). | +| `ManualControl` | `null` | If provided then the circuit's state can be manual controlled via a `CircuitBreakerManualControl` object. | +| `StateProvider` | `null` | If provided then the circuit's current state can be retrieved via a `CircuitBreakerStateProvider` object. | +| `OnClosed` | `null` | If provided then it will be invoked after the circuit transitioned either to the `Closed` or to the `Isolated` state. | +| `OnOpened` | `null` | If provided then it will be invoked after the circuit transitioned to the `Opened` state. | +| `OnHalfOpened` | `null` | If provided then it will be invoked after the circuit transitioned to the `HalfOpened` state. | > [!NOTE] > If both `BreakDuration` and `BreakDurationGenerator` are specified then `BreakDuration` will be ignored. +--- + +> [!IMPORTANT] +> If the `MinimumThroughput` is not reached during the `SamplingDuration` then the `FailureRatio` is ignored. +> In other words, the circuit will not break even if all of the executions failed but their quantity is bellow the minimum throughput. + ## Diagrams ### State diagram diff --git a/docs/strategies/rate-limiter.md b/docs/strategies/rate-limiter.md index 204eed1e39c..c654914dc26 100644 --- a/docs/strategies/rate-limiter.md +++ b/docs/strategies/rate-limiter.md @@ -7,7 +7,7 @@ - **Extension(s)**: - `AddRateLimiter`, - `AddConcurrencyLimiter` -- **Exceptions**: +- **Exception(s)**: - `RateLimiterRejectedException`: Thrown when a rate limiter rejects an execution. > [!NOTE] From 67e8331ccabb54a4874df473f095a729b30e4062 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Wed, 17 Apr 2024 15:45:57 +0200 Subject: [PATCH 13/29] Fix table formatting --- docs/strategies/fallback.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/strategies/fallback.md b/docs/strategies/fallback.md index 566c1592806..162311f96be 100644 --- a/docs/strategies/fallback.md +++ b/docs/strategies/fallback.md @@ -66,11 +66,11 @@ new ResiliencePipelineBuilder().AddFallback(optionsOnFallback); ## Defaults -| Property | Default Value | Description | -|----------------|---------------------------------------------------------|-------------------------------------------------------------------------------------------------------| -| `ShouldHandle` | Any exceptions other than `OperationCanceledException`. | Defines a predicate to determine what results and/or exceptions are handled by the fallback strategy. | -| `FallbackAction` | `Null`, **Required** | This delegate allows you to **dynamically** calculate the surrogate value by utilizing information that is only available at runtime (like the outcome). | -| `OnFallback` | `null` | If provided then it will be invoked before the strategy calculates the fallback value. | +| Property | Default Value | Description | +|------------------|---------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| +| `ShouldHandle` | Any exceptions other than `OperationCanceledException`. | Defines a predicate to determine what results and/or exceptions are handled by the fallback strategy. | +| `FallbackAction` | `Null`, **Required** | This delegate allows you to **dynamically** calculate the surrogate value by utilizing information that is only available at runtime (like the outcome). | +| `OnFallback` | `null` | If provided then it will be invoked before the strategy calculates the fallback value. | ## Diagrams From 1b2d1d53b284f478ac062855284634a1b64e8c30 Mon Sep 17 00:00:00 2001 From: peter-csala <57183693+peter-csala@users.noreply.github.com> Date: Wed, 17 Apr 2024 16:11:24 +0200 Subject: [PATCH 14/29] Apply suggestions from code review Co-authored-by: Martin Costello --- docs/strategies/circuit-breaker.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/strategies/circuit-breaker.md b/docs/strategies/circuit-breaker.md index f2419c207c1..8958b8fa415 100644 --- a/docs/strategies/circuit-breaker.md +++ b/docs/strategies/circuit-breaker.md @@ -14,7 +14,7 @@ --- -The circuit breaker **reactive** resilience strategy shortcuts the execution if the underlying resource is detected as unhealthy. The detection process is done via sampling. If the sampled executions' failure-success ratio exceeds a predefined threshold then circuit breaker will prevent any new executions by throwing a `BrokenCircuitException`. After a preset duration the circuit breaker performs a probe, because the assumption is that this period was enough for the resource to self-heal. Depending on the outcome of the probe the circuit will either allow new executions or still block them. +The circuit breaker **reactive** resilience strategy shortcuts the execution if the underlying resource is detected as unhealthy. The detection process is done via sampling. If the sampled executions' failure-success ratio exceeds a predefined threshold then a circuit breaker will prevent any new executions by throwing a `BrokenCircuitException`. After a preset duration the circuit breaker performs a probe, because the assumption is that this period was enough for the resource to self-heal. Depending on the outcome of the probe, the circuit will either allow new executions or continue to block them. > [!NOTE] > Be aware that the Circuit Breaker strategy [rethrows all exceptions](https://github.com/App-vNext/Polly/wiki/Circuit-Breaker#exception-handling), including those that are handled. A Circuit Breaker's role is to monitor faults and break the circuit when a certain threshold is reached; it does not manage retries. Combine the Circuit Breaker with a Retry strategy if needed. @@ -98,15 +98,15 @@ new ResiliencePipelineBuilder().AddCircuitBreaker(optionsSt |--------------------------|---------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| | `ShouldHandle` | Any exceptions other than `OperationCanceledException`. | Defines a predicate to determine what results and/or exceptions are handled by the circuit breaker strategy. | | `FailureRatio` | 0.1 | The failure-success ratio that will cause the circuit to break/open. `0.1` means 10% failed of all sampled executions. | -| `MinimumThroughput` | 100 | The minimum number of actions that must occur within the specified sampling duration. | +| `MinimumThroughput` | 100 | The minimum number of executions that must occur within the specified sampling duration. | | `SamplingDuration` | 30 seconds | The time period over which the failure-success ratio is calculated. | | `BreakDuration` | 5 seconds | Defines a **static** time period for which the circuit will remain broken/open before attempting to reset. | | `BreakDurationGenerator` | `null` | This delegate allows you to **dynamically** calculate the break duration by utilizing information that is only available at runtime (like failure count). | -| `ManualControl` | `null` | If provided then the circuit's state can be manual controlled via a `CircuitBreakerManualControl` object. | +| `ManualControl` | `null` | If provided then the circuit's state can be manually controlled via a `CircuitBreakerManualControl` object. | | `StateProvider` | `null` | If provided then the circuit's current state can be retrieved via a `CircuitBreakerStateProvider` object. | -| `OnClosed` | `null` | If provided then it will be invoked after the circuit transitioned either to the `Closed` or to the `Isolated` state. | -| `OnOpened` | `null` | If provided then it will be invoked after the circuit transitioned to the `Opened` state. | -| `OnHalfOpened` | `null` | If provided then it will be invoked after the circuit transitioned to the `HalfOpened` state. | +| `OnClosed` | `null` | If provided then it will be invoked after the circuit transitions to either the `Closed` or `Isolated` states. | +| `OnOpened` | `null` | If provided then it will be invoked after the circuit transitions to the `Opened` state. | +| `OnHalfOpened` | `null` | If provided then it will be invoked after the circuit transitions to the `HalfOpened` state. | > [!NOTE] > If both `BreakDuration` and `BreakDurationGenerator` are specified then `BreakDuration` will be ignored. @@ -115,7 +115,7 @@ new ResiliencePipelineBuilder().AddCircuitBreaker(optionsSt > [!IMPORTANT] > If the `MinimumThroughput` is not reached during the `SamplingDuration` then the `FailureRatio` is ignored. -> In other words, the circuit will not break even if all of the executions failed but their quantity is bellow the minimum throughput. +> In other words, the circuit will not break even if all of the executions failed when their quantity is below the minimum throughput. ## Diagrams From a046c8f0094b724cab1725e4384c20f4c60c6c88 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Wed, 17 Apr 2024 16:12:00 +0200 Subject: [PATCH 15/29] Fix table format --- docs/strategies/circuit-breaker.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/strategies/circuit-breaker.md b/docs/strategies/circuit-breaker.md index 8958b8fa415..c1db3c90c3b 100644 --- a/docs/strategies/circuit-breaker.md +++ b/docs/strategies/circuit-breaker.md @@ -98,15 +98,15 @@ new ResiliencePipelineBuilder().AddCircuitBreaker(optionsSt |--------------------------|---------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| | `ShouldHandle` | Any exceptions other than `OperationCanceledException`. | Defines a predicate to determine what results and/or exceptions are handled by the circuit breaker strategy. | | `FailureRatio` | 0.1 | The failure-success ratio that will cause the circuit to break/open. `0.1` means 10% failed of all sampled executions. | -| `MinimumThroughput` | 100 | The minimum number of executions that must occur within the specified sampling duration. | +| `MinimumThroughput` | 100 | The minimum number of executions that must occur within the specified sampling duration. | | `SamplingDuration` | 30 seconds | The time period over which the failure-success ratio is calculated. | | `BreakDuration` | 5 seconds | Defines a **static** time period for which the circuit will remain broken/open before attempting to reset. | | `BreakDurationGenerator` | `null` | This delegate allows you to **dynamically** calculate the break duration by utilizing information that is only available at runtime (like failure count). | -| `ManualControl` | `null` | If provided then the circuit's state can be manually controlled via a `CircuitBreakerManualControl` object. | +| `ManualControl` | `null` | If provided then the circuit's state can be manually controlled via a `CircuitBreakerManualControl` object. | | `StateProvider` | `null` | If provided then the circuit's current state can be retrieved via a `CircuitBreakerStateProvider` object. | -| `OnClosed` | `null` | If provided then it will be invoked after the circuit transitions to either the `Closed` or `Isolated` states. | -| `OnOpened` | `null` | If provided then it will be invoked after the circuit transitions to the `Opened` state. | -| `OnHalfOpened` | `null` | If provided then it will be invoked after the circuit transitions to the `HalfOpened` state. | +| `OnClosed` | `null` | If provided then it will be invoked after the circuit transitions to either the `Closed` or `Isolated` states. | +| `OnOpened` | `null` | If provided then it will be invoked after the circuit transitions to the `Opened` state. | +| `OnHalfOpened` | `null` | If provided then it will be invoked after the circuit transitions to the `HalfOpened` state. | > [!NOTE] > If both `BreakDuration` and `BreakDurationGenerator` are specified then `BreakDuration` will be ignored. From 94ee246b516f69cca5624abd7187d9d02ec02a6c Mon Sep 17 00:00:00 2001 From: peter-csala Date: Thu, 18 Apr 2024 10:54:38 +0200 Subject: [PATCH 16/29] Add telemetry section to timeout --- docs/strategies/timeout.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/strategies/timeout.md b/docs/strategies/timeout.md index 8b5fe822452..cd3789a3974 100644 --- a/docs/strategies/timeout.md +++ b/docs/strategies/timeout.md @@ -131,6 +131,30 @@ So, what is the purpose of the `OnTimeout` in case of static timeout settings? The `OnTimeout` delegate can be useful when you define a resilience pipeline which consists of multiple strategies. For example you have a timeout as the inner strategy and a retry as the outer strategy. If the retry is defined to handle `TimeoutRejectedException`, that means the `Execute{Async}` may or may not throw that exception depending on future attempts. So, if you want to get notification about the fact that a timeout has occurred, you have to provide a delegate to the `OnTimeout` property. +## Telemetry + +The timeout strategy reports the following telemetry events: + +| Event Name | Event Severity | When? | +|-------------|----------------|---------------------------------------------------------| +| `OnTimeout` | `Error` | Just before the strategy calls the `OnTimeout` delegate | + +Here are some sample events: + +```none +Resilience event occurred. EventName: 'OnTimeout', Source: '(null)/(null)/Timeout', Operation Key: '', Result: '' +Resilience event occurred. EventName: 'OnTimeout', Source: 'MyApplication/MyTestPipeline/MyTimeoutStrategy', Operation Key: 'MyTimeoutGuardedOperation', Result: '' +``` + +> [!NOTE] +> Please note that the `OnTimeout` telemetry event will be reported **only if** the timeout strategy cancels the provided callback execution. +> +> So, if the callback either finishes on time or throws an exception then there will be no telemetry log. +> +> Also remember that the `Result` will be **always** empty for the `OnTimeout` telemetry event. + +For further information please check out the [telemetry page](../advanced/telemetry.html). + ## Diagrams ### Happy path sequence diagram From 3b9cab60be8e532a45e0a4e8c3e08b2832f6b00f Mon Sep 17 00:00:00 2001 From: peter-csala Date: Thu, 18 Apr 2024 11:00:58 +0200 Subject: [PATCH 17/29] Remove unused variable --- .../CircuitBreaker/Controller/CircuitStateController.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs b/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs index 70645725cd1..e8b37c22401 100644 --- a/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs +++ b/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs @@ -333,8 +333,6 @@ private void OpenCircuitFor_NeedsLock(Outcome outcome, TimeSpan breakDuration } _blockedUntil = IsDateTimeOverflow(utcNow, breakDuration) ? DateTimeOffset.MaxValue : utcNow + breakDuration; - - var transitionedState = _circuitState; _circuitState = CircuitState.Open; var args = new OnCircuitOpenedArguments(context, outcome, breakDuration, manual); From e64edad567ded72a98c5bd5933aea793258dcacb Mon Sep 17 00:00:00 2001 From: peter-csala Date: Thu, 18 Apr 2024 11:39:27 +0200 Subject: [PATCH 18/29] Add telemetry section to retry --- docs/strategies/retry.md | 59 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/docs/strategies/retry.md b/docs/strategies/retry.md index 61ee41b62ab..dbbfceb598b 100644 --- a/docs/strategies/retry.md +++ b/docs/strategies/retry.md @@ -111,6 +111,65 @@ new ResiliencePipelineBuilder().AddRetry(optionsExtractDela | `DelayGenerator` | `null` | This optional delegate allows you to **dynamically** calculate the retry delay by utilizing information that is only available at runtime (like the attempt number). | | `OnRetry` | `null` | If provided then it will be invoked before the strategy delays the next attempt. | +## Telemetry + +The retry strategy reports the following telemetry events: + +| Event Name | Event Severity | When? | +|---------------------|---------------------------|-------------------------------------------------------| +| `Execution Attempt` | `Information` / `Warning` | Just before the strategy calculates the next delay | +| `OnRetry` | `Warning` | Just before the strategy calls the `OnRetry` delegate | + +Here are some sample events: + +### Unhandled case + +If the retry strategy does not perform any retries then the reported telemetry events' severity will be `Information`: + +```none +Execution attempt. Source: 'MyApplication/MyTestPipeline/MyRetryStrategy', Operation Key: 'MyRetryableOperation', Result: '1', Handled: 'False', Attempt: '0', Execution Time: '110.952' + +Execution attempt. Source: 'MyApplication/MyTestPipeline/MyRetryStrategy', Operation Key: 'MyRetryableOperation', Result: 'Failed', Handled: 'False', Attempt: '0', Execution Time: '5.2194' + System.Exception: Failed + at Program.<>c.
b__0_1(ResilienceContext ctx) + ... + at Polly.ResiliencePipeline.<>c.<b__1_0>d.MoveNext() in /_/src/Polly.Core/ResiliencePipeline.Async.cs:line 67 +``` + +### Handled case + +If the retry strategy performs some retries then the reported telemetry events' severity will be `Warning`: + +```none +Execution attempt. Source: 'MyApplication/MyTestPipeline/MyRetryStrategy', Operation Key: 'MyRetryableOperation', Result: 'Failed', Handled: 'True', Attempt: '0', Execution Time: '5.0397' + System.Exception: Failed + at Program.<>c.
b__0_1(ResilienceContext ctx) + ... + at Polly.ResiliencePipeline.<>c.<b__1_0>d.MoveNext() in /_/src/Polly.Core/ResiliencePipeline.Async.cs:line 67 + +Resilience event occurred. EventName: 'OnRetry', Source: 'MyApplication/MyTestPipeline/MyRetryStrategy', Operation Key: 'MyRetryableOperation', Result: 'Failed' + System.Exception: Failed + at Program.<>c.
b__0_1(ResilienceContext ctx) + ... + at Polly.ResiliencePipeline.<>c.<b__1_0>d.MoveNext() in /_/src/Polly.Core/ResiliencePipeline.Async.cs:line 67 + + +Execution attempt. Source: 'MyApplication/MyTestPipeline/MyRetryStrategy', Operation Key: 'MyRetryableOperation', Result: 'Failed', Handled: 'True', Attempt: '1', Execution Time: '0.1159' + System.Exception: Failed + at Program.<>c.
b__0_1(ResilienceContext ctx) + ... + at Polly.ResiliencePipeline.<>c.<b__1_0>d.MoveNext() in /_/src/Polly.Core/ResiliencePipeline.Async.cs:line 67 +``` + +> [!NOTE] +> Please note that the `OnRetry` telemetry event will be reported **only if** the retry strategy performs any retry attempts. +> +> On the other hand the `Execution attempt` event will be **always** reported regardless whether the strategy has to perform any retries. +> +> Also remember that the `Attempt: '0'` means the original attempt. + +For further information please check out the [telemetry page](../advanced/telemetry.html). + ## Calculation of the next delay If the `ShouldHandle` predicate returns `true` and the next attempt number is not greater than `MaxRetryAttempts` then the retry strategy calculates the next delay. From ccae18c6751334aa55dab342b54be6824fee4d06 Mon Sep 17 00:00:00 2001 From: peter-csala <57183693+peter-csala@users.noreply.github.com> Date: Thu, 18 Apr 2024 11:40:49 +0200 Subject: [PATCH 19/29] Update docs/strategies/timeout.md Co-authored-by: Martin Costello --- docs/strategies/timeout.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/strategies/timeout.md b/docs/strategies/timeout.md index cd3789a3974..07855ac0d10 100644 --- a/docs/strategies/timeout.md +++ b/docs/strategies/timeout.md @@ -149,7 +149,7 @@ Resilience event occurred. EventName: 'OnTimeout', Source: 'MyApplication/MyTest > [!NOTE] > Please note that the `OnTimeout` telemetry event will be reported **only if** the timeout strategy cancels the provided callback execution. > -> So, if the callback either finishes on time or throws an exception then there will be no telemetry log. +> So, if the callback either finishes on time or throws an exception then there will be no telemetry emitted. > > Also remember that the `Result` will be **always** empty for the `OnTimeout` telemetry event. From 6a9a59f6e24c296f636cd114d7a4172fec93b7d7 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Thu, 18 Apr 2024 12:10:17 +0200 Subject: [PATCH 20/29] Add telemetry section to fallback --- docs/strategies/fallback.md | 31 +++++++++++++++++++++++++++++++ docs/strategies/timeout.md | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/docs/strategies/fallback.md b/docs/strategies/fallback.md index 162311f96be..3f12f8c66e9 100644 --- a/docs/strategies/fallback.md +++ b/docs/strategies/fallback.md @@ -72,6 +72,37 @@ new ResiliencePipelineBuilder().AddFallback(optionsOnFallback); | `FallbackAction` | `Null`, **Required** | This delegate allows you to **dynamically** calculate the surrogate value by utilizing information that is only available at runtime (like the outcome). | | `OnFallback` | `null` | If provided then it will be invoked before the strategy calculates the fallback value. | +## Telemetry + +The fallback strategy reports the following telemetry events: + +| Event Name | Event Severity | When? | +|--------------|----------------|----------------------------------------------------------| +| `OnFallback` | `Warning` | Just before the strategy calls the `OnFallback` delegate | + +Here are some sample events: + +```none +Resilience event occurred. EventName: 'OnFallback', Source: 'MyApplication/MyTestPipeline/MyFallbackStrategy', Operation Key: 'MyFallbackGuardedOperation', Result: '-1' + +Resilience event occurred. EventName: 'OnFallback', Source: '(null)/(null)/Fallback', Operation Key: '', Result: 'Exception of type 'CustomException' was thrown.' + CustomException: Exception of type 'CustomException' was thrown. + at Program.<>c.
b__0_3(ResilienceContext ctx) + ... + at Polly.ResiliencePipeline.<>c__8`1.<b__8_0>d.MoveNext() in /_/src/Polly.Core/ResiliencePipeline.AsyncT.cs:line 95 +``` + +> [!NOTE] +> Please note that the `OnFallback` telemetry event will be reported either if the fallback strategy provides a surrogate value or the original callback throws an exception. +> +> If an exception was thrown then the telemetry event will be reported regardless the exception was handled or unhandled from the fallback strategy perspective. +> +> So, **only if** the callback returns an acceptable result there will be no telemetry emitted. +> +> Also remember that the `Result` will be **always populated** for the `OnFallback` telemetry event. + +For further information please check out the [telemetry page](../advanced/telemetry.html). + ## Diagrams ### Happy path sequence diagram diff --git a/docs/strategies/timeout.md b/docs/strategies/timeout.md index 07855ac0d10..0728773bdf1 100644 --- a/docs/strategies/timeout.md +++ b/docs/strategies/timeout.md @@ -151,7 +151,7 @@ Resilience event occurred. EventName: 'OnTimeout', Source: 'MyApplication/MyTest > > So, if the callback either finishes on time or throws an exception then there will be no telemetry emitted. > -> Also remember that the `Result` will be **always** empty for the `OnTimeout` telemetry event. +> Also remember that the `Result` will be **always empty** for the `OnTimeout` telemetry event. For further information please check out the [telemetry page](../advanced/telemetry.html). From 1a402026a422cd26341baaf51a72978b0774f776 Mon Sep 17 00:00:00 2001 From: peter-csala <57183693+peter-csala@users.noreply.github.com> Date: Thu, 18 Apr 2024 12:10:37 +0200 Subject: [PATCH 21/29] Update docs/strategies/retry.md Co-authored-by: Martin Costello --- docs/strategies/retry.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/strategies/retry.md b/docs/strategies/retry.md index dbbfceb598b..5c903285713 100644 --- a/docs/strategies/retry.md +++ b/docs/strategies/retry.md @@ -166,7 +166,7 @@ Execution attempt. Source: 'MyApplication/MyTestPipeline/MyRetryStrategy', Opera > > On the other hand the `Execution attempt` event will be **always** reported regardless whether the strategy has to perform any retries. > -> Also remember that the `Attempt: '0'` means the original attempt. +> Also remember that `Attempt: '0'` relates to the original execution attempt. For further information please check out the [telemetry page](../advanced/telemetry.html). From d81f3e4ba6aece1b0d9f9697e766be4e7525b619 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Thu, 18 Apr 2024 12:19:10 +0200 Subject: [PATCH 22/29] Fix note section for fallback telemetry --- docs/strategies/fallback.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/strategies/fallback.md b/docs/strategies/fallback.md index 3f12f8c66e9..0e9bb6bde5d 100644 --- a/docs/strategies/fallback.md +++ b/docs/strategies/fallback.md @@ -93,11 +93,9 @@ Resilience event occurred. EventName: 'OnFallback', Source: '(null)/(null)/Fallb ``` > [!NOTE] -> Please note that the `OnFallback` telemetry event will be reported either if the fallback strategy provides a surrogate value or the original callback throws an exception. +> Please note that the `OnFallback` telemetry event will be reported **only if** the fallback strategy provides a surrogate value. > -> If an exception was thrown then the telemetry event will be reported regardless the exception was handled or unhandled from the fallback strategy perspective. -> -> So, **only if** the callback returns an acceptable result there will be no telemetry emitted. +> So, if the callback either returns an acceptable result or throws an unhandled exception then there will be no telemetry emitted. > > Also remember that the `Result` will be **always populated** for the `OnFallback` telemetry event. From 552a0e2fd6b1a5b141a690b2e360b2a6a893cc47 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Fri, 19 Apr 2024 09:40:57 +0200 Subject: [PATCH 23/29] Add telemetry to rate limiter --- docs/strategies/fallback.md | 2 +- docs/strategies/rate-limiter.md | 22 ++++++++++++++++++++++ docs/strategies/retry.md | 2 +- docs/strategies/timeout.md | 2 +- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/docs/strategies/fallback.md b/docs/strategies/fallback.md index 0e9bb6bde5d..ed5403c64f5 100644 --- a/docs/strategies/fallback.md +++ b/docs/strategies/fallback.md @@ -99,7 +99,7 @@ Resilience event occurred. EventName: 'OnFallback', Source: '(null)/(null)/Fallb > > Also remember that the `Result` will be **always populated** for the `OnFallback` telemetry event. -For further information please check out the [telemetry page](../advanced/telemetry.html). +For further information please check out the [telemetry page](https://www.pollydocs.org/advanced/telemetry). ## Diagrams diff --git a/docs/strategies/rate-limiter.md b/docs/strategies/rate-limiter.md index c654914dc26..4ee8d6569d5 100644 --- a/docs/strategies/rate-limiter.md +++ b/docs/strategies/rate-limiter.md @@ -134,6 +134,28 @@ The `OnRejected` delegate can be useful when you define a resilience pipeline wh > The [`RateLimiterRejectedException`](xref:Polly.RateLimiting.RateLimiterRejectedException) has a `RetryAfter` property. If this optional `TimeSpan` is provided then this indicates that your requests are throttled and you should retry them no sooner than the value given. > Please note that this information is not available inside the `OnRejected` callback. +## Telemetry + +The rate limiter strategy reports the following telemetry events: + +| Event Name | Event Severity | When? | +|-------------------------|----------------|----------------------------------------------------------| +| `OnRateLimiterRejected` | `Error` | Just before the strategy calls the `OnRejected` delegate | + +Here are some sample events: + +```none +Resilience event occurred. EventName: 'OnRateLimiterRejected', Source: '(null)/(null)/RateLimiter', Operation Key: '', Result: '' +Resilience event occurred. EventName: 'OnRateLimiterRejected', Source: 'MyApplication/MyTestPipeline/MyRateLimiterStrategy', Operation Key: 'MyRateLimitedOperation', Result: '' +``` + +> [!NOTE] +> Please note that the `OnRateLimiterRejected` telemetry event will be reported **only if** the rate limiter strategy rejects the provided callback execution. +> +> Also remember that the `Result` will be **always empty** for the `OnRateLimiterRejected` telemetry event. + +For further information please check out the [telemetry page](https://www.pollydocs.org/advanced/telemetry). + ## Diagrams ### Rate Limiter diff --git a/docs/strategies/retry.md b/docs/strategies/retry.md index 5c903285713..82adf40985c 100644 --- a/docs/strategies/retry.md +++ b/docs/strategies/retry.md @@ -168,7 +168,7 @@ Execution attempt. Source: 'MyApplication/MyTestPipeline/MyRetryStrategy', Opera > > Also remember that `Attempt: '0'` relates to the original execution attempt. -For further information please check out the [telemetry page](../advanced/telemetry.html). +For further information please check out the [telemetry page](https://www.pollydocs.org/advanced/telemetry). ## Calculation of the next delay diff --git a/docs/strategies/timeout.md b/docs/strategies/timeout.md index 0728773bdf1..2e4071ec333 100644 --- a/docs/strategies/timeout.md +++ b/docs/strategies/timeout.md @@ -153,7 +153,7 @@ Resilience event occurred. EventName: 'OnTimeout', Source: 'MyApplication/MyTest > > Also remember that the `Result` will be **always empty** for the `OnTimeout` telemetry event. -For further information please check out the [telemetry page](../advanced/telemetry.html). +For further information please check out the [telemetry page](https://www.pollydocs.org/advanced/telemetry). ## Diagrams From d6e13347dff77e66e391d680c194cecfd67a5b55 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Fri, 19 Apr 2024 10:18:35 +0200 Subject: [PATCH 24/29] Add telemetry section to hedging --- docs/strategies/hedging.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/docs/strategies/hedging.md b/docs/strategies/hedging.md index 389e77b3c08..39234af6e65 100644 --- a/docs/strategies/hedging.md +++ b/docs/strategies/hedging.md @@ -78,6 +78,44 @@ You can use the following special values for `Delay` or in `DelayGenerator`: > [!NOTE] > If both `Delay` and `DelayGenerator` are specified then `Delay` will be ignored. +## Telemetry + +The hedging strategy reports the following telemetry events: + +| Event Name | Event Severity | When? | +|---------------------|---------------------------|----------------------------------------------------------------------| +| `Execution Attempt` | `Information` / `Warning` | Just after the original/hedged action completes with success/failure | +| `OnHedging` | `Warning` | Just before the strategy calls the `OnHedging` delegate | + +Here are some sample events: + +The reported `Execution Attempt` telemetry events' severity depends on the action's outcome: + +- If it succeeded then the severity is `Information` +- It it failed then the severity is `Warning` + +```none +Resilience event occurred. EventName: 'OnHedging', Source: 'MyApplication/MyTestPipeline/Hedging', Operation Key: 'MyHedgingOperation', Result: '' + +Execution attempt. Source: 'MyApplication/MyTestPipeline/Hedging', Operation Key: 'MyHedgingOperation', Result: '1', Handled: 'False', Attempt: '0', Execution Time: '1505.3839' + +Execution attempt. Source: 'MyApplication/MyTestPipeline/Hedging', Operation Key: 'MyHedgingOperation', Result: 'Exception of type 'CustomException' was thrown.', Handled: 'True', Attempt: '1', Execution Time: '1525.2899' + CustomException: Exception of type 'CustomException' was thrown. + at Program.<>c.<
b__0_2>d.MoveNext() + ... + at Polly.ResiliencePipeline.<>c__8`1.<b__8_0>d.MoveNext() in /_/src/Polly.Core/ResiliencePipeline.AsyncT.cs:line 95 +``` + +> [!NOTE] +> Please note that the `OnHedging` telemetry event will be reported **only if** the hedging strategy performs any hedged actions. +> +> On the other hand the `Execution attempt` event will be **always** reported regardless whether the strategy has to perform hedging. +> +> Also remember that `Attempt: '0'` relates to the original execution attempt. + +For further information please check out the [telemetry page](https://www.pollydocs.org/advanced/telemetry). + + ## Concurrency modes In the sections below, explore the different concurrency modes available in the hedging strategy. The behavior is primarily controlled by the `Delay` property value. From eed98a8e7c4afce6e47ce97f63e040c7ad5e7707 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Fri, 19 Apr 2024 10:19:49 +0200 Subject: [PATCH 25/29] Remove extra whitespace --- docs/strategies/hedging.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/strategies/hedging.md b/docs/strategies/hedging.md index 39234af6e65..8cdf5be1880 100644 --- a/docs/strategies/hedging.md +++ b/docs/strategies/hedging.md @@ -115,7 +115,6 @@ Execution attempt. Source: 'MyApplication/MyTestPipeline/Hedging', Operation Key For further information please check out the [telemetry page](https://www.pollydocs.org/advanced/telemetry). - ## Concurrency modes In the sections below, explore the different concurrency modes available in the hedging strategy. The behavior is primarily controlled by the `Delay` property value. From 20fe47010179bb5327753c899fc6da7d7942b52f Mon Sep 17 00:00:00 2001 From: peter-csala Date: Fri, 19 Apr 2024 12:12:50 +0200 Subject: [PATCH 26/29] Add telemetry section to circuit breaker --- docs/strategies/circuit-breaker.md | 33 ++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/docs/strategies/circuit-breaker.md b/docs/strategies/circuit-breaker.md index c1db3c90c3b..4168248169c 100644 --- a/docs/strategies/circuit-breaker.md +++ b/docs/strategies/circuit-breaker.md @@ -117,6 +117,39 @@ new ResiliencePipelineBuilder().AddCircuitBreaker(optionsSt > If the `MinimumThroughput` is not reached during the `SamplingDuration` then the `FailureRatio` is ignored. > In other words, the circuit will not break even if all of the executions failed when their quantity is below the minimum throughput. +## Telemetry + +The circuit breaker strategy reports the following telemetry events: + +| Event Name | Event Severity | When? | +|-----------------------|----------------|------------------------------------------------------------| +| `OnCircuitClosed` | `Error` | Just before the strategy calls the `OnClosed` delegate | +| `OnCircuitOpened` | `Error` | Just before the strategy calls the `OnOpened` delegate | +| `OnCircuitHalfOpened` | `Error` | Just before the strategy calls the `OnHalfOpened` delegate | + +Here are some sample events: + +```none +Resilience event occurred. EventName: 'OnCircuitOpened', Source: 'MyApplication/MyTestPipeline/MyCircuitBreakerStrategy', Operation Key: 'MyCircuitedOperation', Result: 'Exception of type 'CustomException' was thrown.' + CustomException: Exception of type 'CustomException' was thrown. + at Program.<>c.<
b__0_1>d.MoveNext() + ... + at Polly.ResiliencePipeline.<>c__8`1.<b__8_0>d.MoveNext() in /_/src/Polly.Core/ResiliencePipeline.AsyncT.cs:line 95 + +Resilience event occurred. EventName: 'OnCircuitHalfOpened', Source: 'MyApplication/MyTestPipeline/MyCircuitBreakerStrategy', Operation Key: 'MyCircuitedOperation', Result: '' + +Resilience event occurred. EventName: 'OnCircuitClosed', Source: 'MyApplication/MyTestPipeline/MyCircuitBreakerStrategy', Operation Key: 'MyCircuitedOperation', Result: '42' +``` + +> [!NOTE] +> Please note that the `OnCircuitXYZ` telemetry events will be reported **only if** the circuit breaker strategy transitions from one state into another. +> +> Remember in case of `ManualControl` the `OnCircuitHalfOpened` telemetry event will not be emitted. +> +> Also the `Result` will be **always empty** for the `OnCircuitHalfOpened` telemetry event. + +For further information please check out the [telemetry page](https://www.pollydocs.org/advanced/telemetry). + ## Diagrams ### State diagram From 6f10f8d9a430d90bdfeed881485d544434293c84 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Fri, 19 Apr 2024 12:17:53 +0200 Subject: [PATCH 27/29] Fix cb telemetry events' severity --- docs/strategies/circuit-breaker.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/strategies/circuit-breaker.md b/docs/strategies/circuit-breaker.md index 4168248169c..7b25006e85c 100644 --- a/docs/strategies/circuit-breaker.md +++ b/docs/strategies/circuit-breaker.md @@ -123,9 +123,9 @@ The circuit breaker strategy reports the following telemetry events: | Event Name | Event Severity | When? | |-----------------------|----------------|------------------------------------------------------------| -| `OnCircuitClosed` | `Error` | Just before the strategy calls the `OnClosed` delegate | +| `OnCircuitClosed` | `Information` | Just before the strategy calls the `OnClosed` delegate | | `OnCircuitOpened` | `Error` | Just before the strategy calls the `OnOpened` delegate | -| `OnCircuitHalfOpened` | `Error` | Just before the strategy calls the `OnHalfOpened` delegate | +| `OnCircuitHalfOpened` | `Warning` | Just before the strategy calls the `OnHalfOpened` delegate | Here are some sample events: From fc494c32224bc602cdb6ba6952c4db4dbb566f79 Mon Sep 17 00:00:00 2001 From: peter-csala <57183693+peter-csala@users.noreply.github.com> Date: Mon, 22 Apr 2024 10:46:39 +0200 Subject: [PATCH 28/29] Apply suggestions from code review Co-authored-by: martintmk <103487740+martintmk@users.noreply.github.com> --- docs/strategies/hedging.md | 2 +- docs/strategies/retry.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/strategies/hedging.md b/docs/strategies/hedging.md index 8cdf5be1880..9c21a2ea601 100644 --- a/docs/strategies/hedging.md +++ b/docs/strategies/hedging.md @@ -84,7 +84,7 @@ The hedging strategy reports the following telemetry events: | Event Name | Event Severity | When? | |---------------------|---------------------------|----------------------------------------------------------------------| -| `Execution Attempt` | `Information` / `Warning` | Just after the original/hedged action completes with success/failure | +| `ExecutionAttempt` | `Information` / `Warning` | Just after the original/hedged action completes with success/failure | | `OnHedging` | `Warning` | Just before the strategy calls the `OnHedging` delegate | Here are some sample events: diff --git a/docs/strategies/retry.md b/docs/strategies/retry.md index 82adf40985c..bb4899d1f01 100644 --- a/docs/strategies/retry.md +++ b/docs/strategies/retry.md @@ -117,7 +117,7 @@ The retry strategy reports the following telemetry events: | Event Name | Event Severity | When? | |---------------------|---------------------------|-------------------------------------------------------| -| `Execution Attempt` | `Information` / `Warning` | Just before the strategy calculates the next delay | +| `ExecutionAttempt` | `Information` / `Warning` | Just before the strategy calculates the next delay | | `OnRetry` | `Warning` | Just before the strategy calls the `OnRetry` delegate | Here are some sample events: From 899a5c47290d273f0c02f96aa33bc7f279f5d7b9 Mon Sep 17 00:00:00 2001 From: peter-csala Date: Mon, 22 Apr 2024 10:52:00 +0200 Subject: [PATCH 29/29] Apply suggested changes --- docs/strategies/circuit-breaker.md | 6 +++--- docs/strategies/fallback.md | 2 +- docs/strategies/hedging.md | 12 ++++++------ docs/strategies/rate-limiter.md | 2 +- docs/strategies/retry.md | 16 ++++++++-------- docs/strategies/timeout.md | 2 +- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/strategies/circuit-breaker.md b/docs/strategies/circuit-breaker.md index 7b25006e85c..fdd85fe78dc 100644 --- a/docs/strategies/circuit-breaker.md +++ b/docs/strategies/circuit-breaker.md @@ -130,15 +130,15 @@ The circuit breaker strategy reports the following telemetry events: Here are some sample events: ```none -Resilience event occurred. EventName: 'OnCircuitOpened', Source: 'MyApplication/MyTestPipeline/MyCircuitBreakerStrategy', Operation Key: 'MyCircuitedOperation', Result: 'Exception of type 'CustomException' was thrown.' +Resilience event occurred. EventName: 'OnCircuitOpened', Source: 'MyPipeline/MyPipelineInstance/MyCircuitBreakerStrategy', Operation Key: 'MyCircuitedOperation', Result: 'Exception of type 'CustomException' was thrown.' CustomException: Exception of type 'CustomException' was thrown. at Program.<>c.<
b__0_1>d.MoveNext() ... at Polly.ResiliencePipeline.<>c__8`1.<b__8_0>d.MoveNext() in /_/src/Polly.Core/ResiliencePipeline.AsyncT.cs:line 95 -Resilience event occurred. EventName: 'OnCircuitHalfOpened', Source: 'MyApplication/MyTestPipeline/MyCircuitBreakerStrategy', Operation Key: 'MyCircuitedOperation', Result: '' +Resilience event occurred. EventName: 'OnCircuitHalfOpened', Source: 'MyPipeline/MyPipelineInstance/MyCircuitBreakerStrategy', Operation Key: 'MyCircuitedOperation', Result: '' -Resilience event occurred. EventName: 'OnCircuitClosed', Source: 'MyApplication/MyTestPipeline/MyCircuitBreakerStrategy', Operation Key: 'MyCircuitedOperation', Result: '42' +Resilience event occurred. EventName: 'OnCircuitClosed', Source: 'MyPipeline/MyPipelineInstance/MyCircuitBreakerStrategy', Operation Key: 'MyCircuitedOperation', Result: '42' ``` > [!NOTE] diff --git a/docs/strategies/fallback.md b/docs/strategies/fallback.md index ed5403c64f5..c77bd0ad173 100644 --- a/docs/strategies/fallback.md +++ b/docs/strategies/fallback.md @@ -83,7 +83,7 @@ The fallback strategy reports the following telemetry events: Here are some sample events: ```none -Resilience event occurred. EventName: 'OnFallback', Source: 'MyApplication/MyTestPipeline/MyFallbackStrategy', Operation Key: 'MyFallbackGuardedOperation', Result: '-1' +Resilience event occurred. EventName: 'OnFallback', Source: 'MyPipeline/MyPipelineInstance/MyFallbackStrategy', Operation Key: 'MyFallbackGuardedOperation', Result: '-1' Resilience event occurred. EventName: 'OnFallback', Source: '(null)/(null)/Fallback', Operation Key: '', Result: 'Exception of type 'CustomException' was thrown.' CustomException: Exception of type 'CustomException' was thrown. diff --git a/docs/strategies/hedging.md b/docs/strategies/hedging.md index 9c21a2ea601..c5af808899f 100644 --- a/docs/strategies/hedging.md +++ b/docs/strategies/hedging.md @@ -82,10 +82,10 @@ You can use the following special values for `Delay` or in `DelayGenerator`: The hedging strategy reports the following telemetry events: -| Event Name | Event Severity | When? | -|---------------------|---------------------------|----------------------------------------------------------------------| +| Event Name | Event Severity | When? | +|--------------------|---------------------------|----------------------------------------------------------------------| | `ExecutionAttempt` | `Information` / `Warning` | Just after the original/hedged action completes with success/failure | -| `OnHedging` | `Warning` | Just before the strategy calls the `OnHedging` delegate | +| `OnHedging` | `Warning` | Just before the strategy calls the `OnHedging` delegate | Here are some sample events: @@ -95,11 +95,11 @@ The reported `Execution Attempt` telemetry events' severity depends on the actio - It it failed then the severity is `Warning` ```none -Resilience event occurred. EventName: 'OnHedging', Source: 'MyApplication/MyTestPipeline/Hedging', Operation Key: 'MyHedgingOperation', Result: '' +Resilience event occurred. EventName: 'OnHedging', Source: 'MyPipeline/MyPipelineInstance/Hedging', Operation Key: 'MyHedgingOperation', Result: '' -Execution attempt. Source: 'MyApplication/MyTestPipeline/Hedging', Operation Key: 'MyHedgingOperation', Result: '1', Handled: 'False', Attempt: '0', Execution Time: '1505.3839' +Execution attempt. Source: 'MyPipeline/MyPipelineInstance/Hedging', Operation Key: 'MyHedgingOperation', Result: '1', Handled: 'False', Attempt: '0', Execution Time: '1505.3839' -Execution attempt. Source: 'MyApplication/MyTestPipeline/Hedging', Operation Key: 'MyHedgingOperation', Result: 'Exception of type 'CustomException' was thrown.', Handled: 'True', Attempt: '1', Execution Time: '1525.2899' +Execution attempt. Source: 'MyPipeline/MyPipelineInstance/Hedging', Operation Key: 'MyHedgingOperation', Result: 'Exception of type 'CustomException' was thrown.', Handled: 'True', Attempt: '1', Execution Time: '1525.2899' CustomException: Exception of type 'CustomException' was thrown. at Program.<>c.<
b__0_2>d.MoveNext() ... diff --git a/docs/strategies/rate-limiter.md b/docs/strategies/rate-limiter.md index 4ee8d6569d5..9d720c970e1 100644 --- a/docs/strategies/rate-limiter.md +++ b/docs/strategies/rate-limiter.md @@ -146,7 +146,7 @@ Here are some sample events: ```none Resilience event occurred. EventName: 'OnRateLimiterRejected', Source: '(null)/(null)/RateLimiter', Operation Key: '', Result: '' -Resilience event occurred. EventName: 'OnRateLimiterRejected', Source: 'MyApplication/MyTestPipeline/MyRateLimiterStrategy', Operation Key: 'MyRateLimitedOperation', Result: '' +Resilience event occurred. EventName: 'OnRateLimiterRejected', Source: 'MyPipeline/MyPipelineInstance/MyRateLimiterStrategy', Operation Key: 'MyRateLimitedOperation', Result: '' ``` > [!NOTE] diff --git a/docs/strategies/retry.md b/docs/strategies/retry.md index bb4899d1f01..7cfddc9e5b7 100644 --- a/docs/strategies/retry.md +++ b/docs/strategies/retry.md @@ -115,10 +115,10 @@ new ResiliencePipelineBuilder().AddRetry(optionsExtractDela The retry strategy reports the following telemetry events: -| Event Name | Event Severity | When? | -|---------------------|---------------------------|-------------------------------------------------------| +| Event Name | Event Severity | When? | +|--------------------|---------------------------|-------------------------------------------------------| | `ExecutionAttempt` | `Information` / `Warning` | Just before the strategy calculates the next delay | -| `OnRetry` | `Warning` | Just before the strategy calls the `OnRetry` delegate | +| `OnRetry` | `Warning` | Just before the strategy calls the `OnRetry` delegate | Here are some sample events: @@ -127,9 +127,9 @@ Here are some sample events: If the retry strategy does not perform any retries then the reported telemetry events' severity will be `Information`: ```none -Execution attempt. Source: 'MyApplication/MyTestPipeline/MyRetryStrategy', Operation Key: 'MyRetryableOperation', Result: '1', Handled: 'False', Attempt: '0', Execution Time: '110.952' +Execution attempt. Source: 'MyPipeline/MyPipelineInstance/MyRetryStrategy', Operation Key: 'MyRetryableOperation', Result: '1', Handled: 'False', Attempt: '0', Execution Time: '110.952' -Execution attempt. Source: 'MyApplication/MyTestPipeline/MyRetryStrategy', Operation Key: 'MyRetryableOperation', Result: 'Failed', Handled: 'False', Attempt: '0', Execution Time: '5.2194' +Execution attempt. Source: 'MyPipeline/MyPipelineInstance/MyRetryStrategy', Operation Key: 'MyRetryableOperation', Result: 'Failed', Handled: 'False', Attempt: '0', Execution Time: '5.2194' System.Exception: Failed at Program.<>c.
b__0_1(ResilienceContext ctx) ... @@ -141,20 +141,20 @@ Execution attempt. Source: 'MyApplication/MyTestPipeline/MyRetryStrategy', Opera If the retry strategy performs some retries then the reported telemetry events' severity will be `Warning`: ```none -Execution attempt. Source: 'MyApplication/MyTestPipeline/MyRetryStrategy', Operation Key: 'MyRetryableOperation', Result: 'Failed', Handled: 'True', Attempt: '0', Execution Time: '5.0397' +Execution attempt. Source: 'MyPipeline/MyPipelineInstance/MyRetryStrategy', Operation Key: 'MyRetryableOperation', Result: 'Failed', Handled: 'True', Attempt: '0', Execution Time: '5.0397' System.Exception: Failed at Program.<>c.
b__0_1(ResilienceContext ctx) ... at Polly.ResiliencePipeline.<>c.<b__1_0>d.MoveNext() in /_/src/Polly.Core/ResiliencePipeline.Async.cs:line 67 -Resilience event occurred. EventName: 'OnRetry', Source: 'MyApplication/MyTestPipeline/MyRetryStrategy', Operation Key: 'MyRetryableOperation', Result: 'Failed' +Resilience event occurred. EventName: 'OnRetry', Source: 'MyPipeline/MyPipelineInstance/MyRetryStrategy', Operation Key: 'MyRetryableOperation', Result: 'Failed' System.Exception: Failed at Program.<>c.
b__0_1(ResilienceContext ctx) ... at Polly.ResiliencePipeline.<>c.<b__1_0>d.MoveNext() in /_/src/Polly.Core/ResiliencePipeline.Async.cs:line 67 -Execution attempt. Source: 'MyApplication/MyTestPipeline/MyRetryStrategy', Operation Key: 'MyRetryableOperation', Result: 'Failed', Handled: 'True', Attempt: '1', Execution Time: '0.1159' +Execution attempt. Source: 'MyPipeline/MyPipelineInstance/MyRetryStrategy', Operation Key: 'MyRetryableOperation', Result: 'Failed', Handled: 'True', Attempt: '1', Execution Time: '0.1159' System.Exception: Failed at Program.<>c.
b__0_1(ResilienceContext ctx) ... diff --git a/docs/strategies/timeout.md b/docs/strategies/timeout.md index 2e4071ec333..a39f49c2a6a 100644 --- a/docs/strategies/timeout.md +++ b/docs/strategies/timeout.md @@ -143,7 +143,7 @@ Here are some sample events: ```none Resilience event occurred. EventName: 'OnTimeout', Source: '(null)/(null)/Timeout', Operation Key: '', Result: '' -Resilience event occurred. EventName: 'OnTimeout', Source: 'MyApplication/MyTestPipeline/MyTimeoutStrategy', Operation Key: 'MyTimeoutGuardedOperation', Result: '' +Resilience event occurred. EventName: 'OnTimeout', Source: 'MyPipeline/MyPipelineInstance/MyTimeoutStrategy', Operation Key: 'MyTimeoutGuardedOperation', Result: '' ``` > [!NOTE]