< Summary

Information
Class: ArturRios.Util.FlowControl.Waiter.JitteredWaiter
Assembly: ArturRios.Util
File(s): /home/runner/work/dotnet-util/dotnet-util/src/FlowControl/Waiter/JitteredWaiter.cs
Line coverage
100%
Covered lines: 19
Uncovered lines: 0
Coverable lines: 19
Total lines: 82
Line coverage: 100%
Branch coverage
100%
Covered branches: 2
Total branches: 2
Branch coverage: 100%
Method coverage
100%
Covered methods: 6
Fully covered methods: 6
Total methods: 6
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%44100%
get_MaxRetryCount()100%11100%
get_MaxWaitMilliseconds()100%11100%
get_Count()100%11100%
get_CanRetry()100%11100%
WaitAsync()100%22100%

File(s)

/home/runner/work/dotnet-util/dotnet-util/src/FlowControl/Waiter/JitteredWaiter.cs

#LineLine coverage
 1namespace ArturRios.Util.FlowControl.Waiter;
 2
 3/// <summary>
 4/// Implements an exponential backoff waiting strategy with jitter to reduce contention.
 5/// </summary>
 6/// <remarks>
 7/// Wait times grow exponentially (2^n seconds base minus a fixed delay) and a random jitter is added.
 8/// Call <see cref="WaitAsync"/> before each retry attempt until <see cref="CanRetry"/> is false.
 9/// Every wait is clamped to <see cref="MaxWaitMilliseconds"/>, so a large
 10/// <paramref name="maxRetryCount"/> produces a long series of capped waits rather than an arithmetic
 11/// overflow or a multi-day sleep.
 12/// </remarks>
 13/// <param name="maxRetryCount">Maximum number of retries allowed; must not be negative.</param>
 14/// <param name="maxWaitMilliseconds">
 15/// Upper bound applied to a single wait, in milliseconds. Defaults to
 16/// <see cref="DefaultMaxWaitMilliseconds"/> (30 seconds); must be greater than zero.
 17/// </param>
 18/// <exception cref="ArgumentOutOfRangeException">
 19/// <paramref name="maxRetryCount"/> is negative, or <paramref name="maxWaitMilliseconds"/> is not positive.
 20/// </exception>
 1021public class JitteredWaiter(int maxRetryCount, int maxWaitMilliseconds = JitteredWaiter.DefaultMaxWaitMilliseconds)
 22{
 23    /// <summary>
 24    /// Default ceiling for a single wait: 30 seconds.
 25    /// </summary>
 26    public const int DefaultMaxWaitMilliseconds = 30_000;
 27
 1428    private readonly Lock _lock = new();
 29
 30    /// <summary>
 31    /// Maximum number of retries permitted.
 32    /// </summary>
 9933    public int MaxRetryCount { get; } = maxRetryCount >= 0
 1434        ? maxRetryCount
 1435        : throw new ArgumentOutOfRangeException(nameof(maxRetryCount), maxRetryCount, "Value must not be negative.");
 36
 37    /// <summary>
 38    /// Ceiling applied to a single wait, in milliseconds.
 39    /// </summary>
 9040    public int MaxWaitMilliseconds { get; } = maxWaitMilliseconds > 0
 1241        ? maxWaitMilliseconds
 1242        : throw new ArgumentOutOfRangeException(nameof(maxWaitMilliseconds), maxWaitMilliseconds, "Value must be greater
 43
 24144    private int Count { get; set; }
 45
 46    /// <summary>
 47    /// Indicates whether another retry attempt can be performed.
 48    /// </summary>
 49    public bool CanRetry
 50    {
 51        get
 552        {
 53            lock (_lock)
 54            {
 555                return Count < MaxRetryCount;
 56            }
 557        }
 58    }
 59
 60    /// <summary>
 61    /// Asynchronously waits based on the current retry attempt using exponential backoff with jitter.
 62    /// </summary>
 63    /// <param name="cancellationToken">Cancels the wait.</param>
 64    /// <exception cref="MaxRetriesReachedException">Thrown when called more times than <see cref="MaxRetryCount"/>.</ex
 65    /// <exception cref="OperationCanceledException"><paramref name="cancellationToken"/> was canceled.</exception>
 66    public async Task WaitAsync(CancellationToken cancellationToken = default)
 8067    {
 68        int currentRetryAttempt;
 69
 70        lock (_lock)
 71        {
 8072            if (Count >= MaxRetryCount)
 73            {
 274                throw new MaxRetriesReachedException();
 75            }
 76
 7877            currentRetryAttempt = Count++;
 7878        }
 79
 7880        await Task.Delay(Backoff.DelayFor(currentRetryAttempt, MaxWaitMilliseconds), cancellationToken).ConfigureAwait(f
 7681    }
 82}