Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
26f75ea
Unify Timeout, Retry
peter-csala Apr 15, 2024
40bf86f
Merge branch 'App-vNext:main' into unify-strategy-descriptions
peter-csala Apr 15, 2024
547553e
Add runtime to the wordlist
peter-csala Apr 15, 2024
950a84c
Fix heading
peter-csala Apr 15, 2024
fa7b6ba
Apply suggestions from code review
peter-csala Apr 15, 2024
8f6cbc5
Apply suggestions
peter-csala Apr 15, 2024
4c46442
Unify Fallback
peter-csala Apr 16, 2024
ac1a2bb
Unify Hedging
peter-csala Apr 16, 2024
44b9937
Fix linting issue related to italic usage
peter-csala Apr 16, 2024
fe6f713
Unify Rate limiter
peter-csala Apr 17, 2024
5ffd9c8
Apply suggestions from code review
peter-csala Apr 17, 2024
1a2452a
Use note instead of important
peter-csala Apr 17, 2024
661b8d5
Unify Circuit Breaker
peter-csala Apr 17, 2024
67e8331
Fix table formatting
peter-csala Apr 17, 2024
1b2d1d5
Apply suggestions from code review
peter-csala Apr 17, 2024
a046c8f
Fix table format
peter-csala Apr 17, 2024
94ee246
Add telemetry section to timeout
peter-csala Apr 18, 2024
3b9cab6
Remove unused variable
peter-csala Apr 18, 2024
e64edad
Add telemetry section to retry
peter-csala Apr 18, 2024
ccae18c
Update docs/strategies/timeout.md
peter-csala Apr 18, 2024
6a9a59f
Add telemetry section to fallback
peter-csala Apr 18, 2024
1a40202
Update docs/strategies/retry.md
peter-csala Apr 18, 2024
d81f3e4
Fix note section for fallback telemetry
peter-csala Apr 18, 2024
552a0e2
Add telemetry to rate limiter
peter-csala Apr 19, 2024
d6e1334
Add telemetry section to hedging
peter-csala Apr 19, 2024
eed98a8
Remove extra whitespace
peter-csala Apr 19, 2024
20fe470
Add telemetry section to circuit breaker
peter-csala Apr 19, 2024
6f10f8d
Fix cb telemetry events' severity
peter-csala Apr 19, 2024
fc494c3
Apply suggestions from code review
peter-csala Apr 22, 2024
899a5c4
Apply suggested changes
peter-csala Apr 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ rethrow
rethrows
retryable
reusability
runtime
saas
sdk
serializers
Expand Down
27 changes: 17 additions & 10 deletions docs/strategies/fallback.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@

## About

- **Options**: [`FallbackStrategyOptions<T>`](xref:Polly.Fallback.FallbackStrategyOptions`1)
- **Extensions**: `AddFallback`
- **Strategy Type**: Reactive
- **Option(s)**:
- [`FallbackStrategyOptions<T>`](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

<!-- snippet: fallback -->
Expand Down Expand Up @@ -59,11 +66,11 @@ new ResiliencePipelineBuilder<UserAvatar>().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

Expand Down Expand Up @@ -298,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.
Expand All @@ -323,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

Expand Down
28 changes: 15 additions & 13 deletions docs/strategies/hedging.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@

## About

- **Options**: [`HedgingStrategyOptions<T>`](xref:Polly.Hedging.HedgingStrategyOptions`1)
- **Extensions**: `AddHedging`
- **Strategy Type**: Reactive
- **Option(s)**:
- [`HedgingStrategyOptions<T>`](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.
Expand Down Expand Up @@ -59,14 +61,14 @@ new ResiliencePipelineBuilder<HttpResponseMessage>().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`:

Expand Down
34 changes: 22 additions & 12 deletions docs/strategies/rate-limiter.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -107,6 +111,16 @@ catch (RateLimiterRejectedException)
```
<!-- endSnippet -->

## 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.
Expand All @@ -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

Expand Down
29 changes: 16 additions & 13 deletions docs/strategies/retry.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@

## About

- **Options**:
- **Option(s)**:
- [`RetryStrategyOptions`](xref:Polly.Retry.RetryStrategyOptions)
- [`RetryStrategyOptions<T>`](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 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

<!-- snippet: retry -->
Expand Down Expand Up @@ -97,16 +100,16 @@ new ResiliencePipelineBuilder<HttpResponseMessage>().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 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

Expand Down
Loading