Skip to content

Commit

Permalink
[Docs] Add delay calculation diagrams (#1922)
Browse files Browse the repository at this point in the history
Replace textual descriptions with diagrams.
  • Loading branch information
peter-csala authored Jan 25, 2024
1 parent 51044c5 commit 1dcfc01
Showing 1 changed file with 129 additions and 59 deletions.
188 changes: 129 additions & 59 deletions docs/strategies/retry.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ 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. |
Expand All @@ -116,9 +116,10 @@ There are many properties that may contribute to this calculation:

- `BackoffType`: Specifies which calculation algorithm should run.
- `Delay`: If only this property is specified then it will be used as-is. If others are also specified then this will be used as a *base delay*.
- `DelayGenerator`: If specified, will override other property-based calculations, **except** if it returns `null` or a negative `TimeSpan`, in which case the other property-based calculations are used.
- `DelayGenerator`: If specified, overrides other property-based calculations, **except** if it returns `null` or a negative `TimeSpan`, in which case the other property-based calculations are used.
- `MaxDelay`: If specified, caps the delay if the calculated delay is greater than this value, **except** if `DelayGenerator` is used, where no capping is applied.
- `UseJitter`: If enabled, then adds a random value between between -25% of `Delay` and +25% of `Delay`, **except** if `BackoffType` is `Exponential`, where a bit more complex jitter calculation is used.
- `UseJitter`: If enabled, adds a random value between -25% and +25% of the calculated `Delay`, **except** if `BackoffType` is `Exponential`, where a `DecorrelatedJitterBackoffV2` formula is used for jitter calculation.
- That formula is based on [Polly.Contrib.WaitAndRetry](https://github.com/Polly-Contrib/Polly.Contrib.WaitAndRetry).

> [!IMPORTANT]
> The summarized description below is an implementation detail. It may change in the future without notice.
Expand All @@ -127,84 +128,153 @@ The `BackoffType` property's data type is the [`DelayBackoffType`](xref:Polly.De

### Constant

Even though the `Constant` name could imply that only the `Delay` property is used, in reality all the above listed properties are used.

Step 1: Calculating the base delay:

- If `UseJitter` is set to `false` and `Delay` is specified then `Delay` will be used.
- If `UseJitter` is set to `true` and `Delay` is specified then a random value is added to the `Delay`.
- The random value is between -25% and +25% of `Delay`.

Step 2: Capping the delay if needed:

- If `MaxDelay` is not set then the previously calculated delay will be used.
- If `MaxDelay` is set and the previously calculated delay is greater than `MaxDelay` then `MaxDelay` will be used.

Step 3: Using the generator if supplied

- If the returned `TimeSpan` of the `DelayGenerator` method call is positive then it will be used.
- If the returned `TimeSpan` of the `DelayGenerator` method call is negative then it will use the step 2's result.
- If the `DelayGenerator` method call is `null` then it will use the step 2's result.

> [!NOTE]
> The `DelayGenerator`'s returned value is not capped with the `MaxDelay`.
```mermaid
stateDiagram-v2
state if_state_step1 <<choice>>
state if_state_step2 <<choice>>
state if_state_step3 <<choice>>
constant: Delay
constantWJitter: Delay + Random
compare: MaxDelay < BaseDelay
setBase: Set BaseDelay
setNormalized: Set NormalizedDelay
setNext: Set NextDelay
UseJitter --> if_state_step1
if_state_step1 --> constantWJitter:true
if_state_step1 --> constant: false
constantWJitter --> setBase
constant --> setBase
setBase --> compare
compare --> if_state_step2
if_state_step2 --> MaxDelay: true
if_state_step2 --> BaseDelay: false
MaxDelay --> setNormalized
BaseDelay --> setNormalized
setNormalized --> DelayGenerator
DelayGenerator --> if_state_step3
if_state_step3 --> GeneratedDelay: positive
if_state_step3 --> NormalizedDelay: null or negative
GeneratedDelay --> setNext
NormalizedDelay --> setNext
setNext --> [*]
```

#### Constant examples

The delays column contains an example series of five values to depict the patterns.

| Settings | Delays in milliseconds |
|--|--|
| `Delay`: `1sec` | [1000,1000,1000,1000,1000] |
| `Delay`: `1sec`, `UseJitter`: `true` | [986,912,842,972,1007] |
| `Delay`: `1sec`, `UseJitter`: `true`, `MaxDelay`: `1100ms` | [1100,978,1100,1041,916] |
| Settings | Delays in milliseconds |
|------------------------------------------------------------|----------------------------------|
| `Delay`: `1sec` | [ 1000, 1000, 1000, 1000, 1000 ] |
| `Delay`: `1sec`, `UseJitter`: `true` | [ 986, 912, 842, 972, 1007 ] |
| `Delay`: `1sec`, `UseJitter`: `true`, `MaxDelay`: `1100ms` | [ 1100, 978, 1100, 1041, 916 ] |

### Linear

This algorithm increases the delays for every attempt in a linear fashion if no jitter is used.
```mermaid
stateDiagram-v2
state if_state_step1 <<choice>>
state if_state_step2 <<choice>>
state if_state_step3 <<choice>>
linear: Delay * AttemptNumber
linearWJitter: (Delay * AttemptNumber) + Random
compare: MaxDelay < BaseDelay
setBase: Set BaseDelay
setNormalized: Set NormalizedDelay
setNext: Set NextDelay
UseJitter --> if_state_step1
if_state_step1 --> linearWJitter:true
if_state_step1 --> linear: false
linearWJitter --> setBase
linear --> setBase
setBase --> compare
compare --> if_state_step2
if_state_step2 --> MaxDelay: true
if_state_step2 --> BaseDelay: false
MaxDelay --> setNormalized
BaseDelay --> setNormalized
setNormalized --> DelayGenerator
DelayGenerator --> if_state_step3
if_state_step3 --> GeneratedDelay: positive
if_state_step3 --> NormalizedDelay: null or negative
GeneratedDelay --> setNext
NormalizedDelay --> setNext
setNext --> [*]
```

Step 1: Calculating the base delay:
#### Linear examples

- If `UseJitter` is set to `false` and `Delay` is specified then `Delay` multiplied by the actual attempt number will be used.
- If `UseJitter` is set to `true` and `Delay` is specified then a random value is added to the `Delay` multiplied by the actual attempt number.
- The random value is between -25% and +25% of the newly calculated `Delay`.
The delays column contains an example series of five values to depict the patterns.

> [!NOTE]
> Because the jitter calculation is based on the newly calculated delay, the new delay could be less than the previous value.
Step 2 and 3 are the same as for the Constant algorithm.

#### Linear examples

The delays column contains an example series of five values to depict the patterns.

| Settings | Delays in milliseconds |
|--|--|
| `Delay`: `1sec` | [1000,2000,3000,4000,5000] |
| `Delay`: `1sec`, `UseJitter`: `true` | [1129,2147,2334,4894,4102] |
| `Delay`: `1sec`, `UseJitter`: `true`, `MaxDelay`: `4500ms` | [907,2199,2869,4500,4500] |
| Settings | Delays in milliseconds |
|------------------------------------------------------------|----------------------------------|
| `Delay`: `1sec` | [ 1000, 2000, 3000, 4000, 5000 ] |
| `Delay`: `1sec`, `UseJitter`: `true` | [ 1129, 2147, 2334, 4894, 4102 ] |
| `Delay`: `1sec`, `UseJitter`: `true`, `MaxDelay`: `4500ms` | [ 907, 2199, 2869, 4500, 4500 ] |

### Exponential

This algorithm increases the delays for every attempt in an exponential fashion if no jitter is used.

- If `UseJitter` is set to `false` and `Delay` is specified then squaring actual attempt number multiplied by the `Delay` will be used (*`attempt^2 * delay`*).
- If `UseJitter` is set to `true` and the `Delay` is specified then a `DecorrelatedJitterBackoffV2` formula (based on [Polly.Contrib.WaitAndRetry](https://github.com/Polly-Contrib/Polly.Contrib.WaitAndRetry)) will be used.

> [!NOTE]
> Because the jitter calculation is based on the newly calculated delay, the new delay could be less than the previous value.
Step 2 and 3 are the same as for the Constant algorithm.
```mermaid
stateDiagram-v2
state if_state_step1 <<choice>>
state if_state_step2 <<choice>>
state if_state_step3 <<choice>>
exponential: Delay * AttemptNumber^2
exponentialWJitter: Decorrelated Jitter Backoff V2
compare: MaxDelay < BaseDelay
setBase: Set BaseDelay
setNormalized: Set NormalizedDelay
setNext: Set NextDelay
UseJitter --> if_state_step1
if_state_step1 --> exponentialWJitter:true
if_state_step1 --> exponential: false
exponentialWJitter --> setBase
exponential --> setBase
setBase --> compare
compare --> if_state_step2
if_state_step2 --> MaxDelay: true
if_state_step2 --> BaseDelay: false
MaxDelay --> setNormalized
BaseDelay --> setNormalized
setNormalized --> DelayGenerator
DelayGenerator --> if_state_step3
if_state_step3 --> GeneratedDelay: positive
if_state_step3 --> NormalizedDelay: null or negative
GeneratedDelay --> setNext
NormalizedDelay --> setNext
setNext --> [*]
```

#### Exponential examples

The delays column contains an example series of five values to depict the patterns.

| Settings | Delays in milliseconds |
|--|--|
| `Delay`: `1sec` | [1000,2000,4000,8000,16000] |
| `Delay`: `1sec`, `UseJitter`: `true` | [393,1453,4235,5369,16849] |
| `Delay`: `1sec`, `UseJitter`: `true`, `MaxDelay`: `15000ms` | [477,793,2227,5651,15000] |
> [!NOTE]
> Because the jitter calculation is based on the newly calculated delay, the new delay could be less than the previous value.
| Settings | Delays in milliseconds |
|-------------------------------------------------------------|-----------------------------------|
| `Delay`: `1sec` | [ 1000, 2000, 4000, 8000, 16000 ] |
| `Delay`: `1sec`, `UseJitter`: `true` | [ 393, 1453, 4235, 5369, 16849 ] |
| `Delay`: `1sec`, `UseJitter`: `true`, `MaxDelay`: `15000ms` | [ 477, 793, 2227, 5651, 15000 ] |

---

Expand Down

0 comments on commit 1dcfc01

Please sign in to comment.