| | | 1 | | namespace ProjectTemplate.Web.Services; |
| | | 2 | | |
| | | 3 | | /// <summary> |
| | | 4 | | /// Calculates the delay before a background service retries after consecutive failures. |
| | | 5 | | /// </summary> |
| | | 6 | | /// <remarks> |
| | | 7 | | /// A fixed poll interval keeps a failing dependency under constant load and fills the log with one entry per cycle. |
| | | 8 | | /// After a failure the delay doubles for each additional consecutive failure, up to a configured maximum, and returns |
| | | 9 | | /// to the normal interval as soon as a cycle succeeds. |
| | | 10 | | /// </remarks> |
| | | 11 | | internal static class BackgroundServiceRetryDelay |
| | | 12 | | { |
| | | 13 | | /// <summary> |
| | | 14 | | /// Calculates the delay before the next cycle. |
| | | 15 | | /// </summary> |
| | | 16 | | /// <param name="interval">The configured interval between successful cycles.</param> |
| | | 17 | | /// <param name="maximumRetryDelay">The maximum delay after consecutive failures.</param> |
| | | 18 | | /// <param name="consecutiveFailureCount">The number of consecutive failed cycles; zero after a success.</param> |
| | | 19 | | /// <returns> |
| | | 20 | | /// <paramref name="interval"/> when the previous cycle succeeded; otherwise the interval doubled once per |
| | | 21 | | /// additional consecutive failure, capped at <paramref name="maximumRetryDelay"/>. |
| | | 22 | | /// </returns> |
| | | 23 | | internal static TimeSpan Calculate( |
| | | 24 | | TimeSpan interval, |
| | | 25 | | TimeSpan maximumRetryDelay, |
| | | 26 | | int consecutiveFailureCount) |
| | | 27 | | { |
| | 10 | 28 | | ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(interval, TimeSpan.Zero); |
| | 9 | 29 | | ArgumentOutOfRangeException.ThrowIfNegative(consecutiveFailureCount); |
| | | 30 | | |
| | 8 | 31 | | if (consecutiveFailureCount == 0) |
| | | 32 | | { |
| | 1 | 33 | | return interval; |
| | | 34 | | } |
| | | 35 | | |
| | 7 | 36 | | TimeSpan cap = maximumRetryDelay > interval ? maximumRetryDelay : interval; |
| | | 37 | | |
| | | 38 | | // Cap the exponent before multiplying so a long-running outage cannot overflow the calculation. |
| | 7 | 39 | | int exponent = Math.Min(consecutiveFailureCount - 1, 16); |
| | 7 | 40 | | double delayTicks = interval.Ticks * Math.Pow(2, exponent); |
| | | 41 | | |
| | 7 | 42 | | return delayTicks >= cap.Ticks ? cap : TimeSpan.FromTicks((long)delayTicks); |
| | | 43 | | } |
| | | 44 | | } |