< Summary

Information
Class: AsiBackbone.Signing.ManagedKey.SharedRandomManagedKeyRetryJitterSource
Assembly: AsiBackbone.Signing.ManagedKey
File(s): /home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Signing.ManagedKey/ManagedKeyRetryBackoff.cs
Line coverage
100%
Covered lines: 4
Uncovered lines: 0
Coverable lines: 4
Total lines: 119
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Instance()100%11100%
.ctor()100%11100%
NextDouble()100%11100%

File(s)

/home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Signing.ManagedKey/ManagedKeyRetryBackoff.cs

#LineLine coverage
 1namespace AsiBackbone.Signing.ManagedKey;
 2
 3/// <summary>
 4/// Calculates bounded exponential retry delays with jitter.
 5/// </summary>
 6internal static class ManagedKeyRetryBackoff
 7{
 8    internal const string StrategyName = "exponential-jitter";
 9
 10    /// <summary>
 11    /// Calculates a monotonically increasing retry delay for the one-based retry attempt.
 12    /// </summary>
 13    internal static TimeSpan CalculateDelay(
 14        TimeSpan baseDelay,
 15        TimeSpan maxDelay,
 16        int retryAttempt,
 17        double jitterSample,
 18        TimeSpan previousDelay)
 19    {
 20        ArgumentOutOfRangeException.ThrowIfLessThan(baseDelay, TimeSpan.Zero);
 21
 22        if (maxDelay < TimeSpan.Zero || maxDelay < baseDelay)
 23        {
 24            throw new ArgumentOutOfRangeException(nameof(maxDelay));
 25        }
 26
 27        ArgumentOutOfRangeException.ThrowIfNegativeOrZero(retryAttempt);
 28
 29        if (double.IsNaN(jitterSample) || jitterSample < 0d || jitterSample > 1d)
 30        {
 31            throw new ArgumentOutOfRangeException(nameof(jitterSample));
 32        }
 33
 34        if (previousDelay < TimeSpan.Zero || previousDelay > maxDelay)
 35        {
 36            throw new ArgumentOutOfRangeException(nameof(previousDelay));
 37        }
 38
 39        if (baseDelay == TimeSpan.Zero)
 40        {
 41            return TimeSpan.Zero;
 42        }
 43
 44        long upperBoundTicks = CalculateUpperBoundTicks(baseDelay.Ticks, maxDelay.Ticks, retryAttempt);
 45        long exponentialLowerBoundTicks = upperBoundTicks / 2;
 46        long monotonicLowerBoundTicks = previousDelay.Ticks < upperBoundTicks
 47            ? previousDelay.Ticks + 1
 48            : upperBoundTicks;
 49        long lowerBoundTicks = Math.Min(
 50            upperBoundTicks,
 51            Math.Max(exponentialLowerBoundTicks, monotonicLowerBoundTicks));
 52        long jitterRangeTicks = upperBoundTicks - lowerBoundTicks;
 53        long jitteredTicks = jitterSample >= 1d
 54            ? upperBoundTicks
 55            : lowerBoundTicks + (long)(jitterRangeTicks * jitterSample);
 56
 57        return TimeSpan.FromTicks(Math.Min(jitteredTicks, maxDelay.Ticks));
 58    }
 59
 60    private static long CalculateUpperBoundTicks(long baseDelayTicks, long maxDelayTicks, int retryAttempt)
 61    {
 62        long upperBoundTicks = Math.Min(baseDelayTicks, maxDelayTicks);
 63
 64        for (int attempt = 1; attempt < retryAttempt && upperBoundTicks < maxDelayTicks; attempt++)
 65        {
 66            upperBoundTicks = upperBoundTicks > maxDelayTicks / 2
 67                ? maxDelayTicks
 68                : Math.Min(upperBoundTicks * 2, maxDelayTicks);
 69        }
 70
 71        return upperBoundTicks;
 72    }
 73}
 74
 75/// <summary>
 76/// Supplies a random sample used by the retry backoff calculation.
 77/// </summary>
 78internal interface IManagedKeyRetryJitterSource
 79{
 80    double NextDouble();
 81}
 82
 83/// <summary>
 84/// Delays a retry while preserving cancellation.
 85/// </summary>
 86internal interface IManagedKeyRetryDelay
 87{
 88    ValueTask DelayAsync(TimeSpan delay, CancellationToken cancellationToken);
 89}
 90
 91internal sealed class SharedRandomManagedKeyRetryJitterSource : IManagedKeyRetryJitterSource
 92{
 6893    internal static SharedRandomManagedKeyRetryJitterSource Instance { get; } = new();
 94
 295    private SharedRandomManagedKeyRetryJitterSource()
 96    {
 297    }
 98
 99    public double NextDouble()
 100    {
 10101        return Random.Shared.NextDouble();
 102    }
 103}
 104
 105internal sealed class SystemManagedKeyRetryDelay : IManagedKeyRetryDelay
 106{
 107    internal static SystemManagedKeyRetryDelay Instance { get; } = new();
 108
 109    private SystemManagedKeyRetryDelay()
 110    {
 111    }
 112
 113    public ValueTask DelayAsync(TimeSpan delay, CancellationToken cancellationToken)
 114    {
 115        return delay <= TimeSpan.Zero
 116            ? ValueTask.CompletedTask
 117            : new ValueTask(Task.Delay(delay, cancellationToken));
 118    }
 119}

Methods/Properties

get_Instance()
.ctor()
NextDouble()