From 1dcfc01a169290bf4f25d40fe063c10df81bbf7e Mon Sep 17 00:00:00 2001 From: peter-csala <57183693+peter-csala@users.noreply.github.com> Date: Thu, 25 Jan 2024 15:50:48 +0100 Subject: [PATCH] [Docs] Add delay calculation diagrams (#1922) Replace textual descriptions with diagrams. --- docs/strategies/retry.md | 188 +++++++++++++++++++++++++++------------ 1 file changed, 129 insertions(+), 59 deletions(-) diff --git a/docs/strategies/retry.md b/docs/strategies/retry.md index 262febffd62..a93b6f74546 100644 --- a/docs/strategies/retry.md +++ b/docs/strategies/retry.md @@ -98,7 +98,7 @@ 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. | @@ -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. @@ -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 <> + state if_state_step2 <> + state if_state_step3 <> + + 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 <> + state if_state_step2 <> + state if_state_step3 <> + + 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 <> + state if_state_step2 <> + state if_state_step3 <> + + 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 ] | ---