< Summary - AsyncResponse (Release / net8.0+net10.0 / unit+integration)

Information
Class: AsyncResponse.ShutdownBudgetValidator
Assembly: AsyncResponse.Core
File(s): /home/runner/work/AsyncResponse/AsyncResponse/src/AsyncResponse.Core/ShutdownBudgetValidator.cs
Line coverage
100%
Covered lines: 19
Uncovered lines: 0
Coverable lines: 19
Total lines: 59
Line coverage: 100%
Branch coverage
100%
Covered branches: 12
Total branches: 12
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Validate(...)100%1212100%

File(s)

/home/runner/work/AsyncResponse/AsyncResponse/src/AsyncResponse.Core/ShutdownBudgetValidator.cs

#LineLine coverage
 1using System.Text;
 2
 3namespace AsyncResponse;
 4
 5/// <summary>
 6/// Shared startup check that a transport's worst-case graceful-shutdown spend fits inside the
 7/// mirrored <c>HostShutdownTimeout</c>. Early-ACK subscribers keep working after the broker ACK,
 8/// so on shutdown the host must wait for the background drain (plus, on some transports, a bounded
 9/// connection close/join) — if that budget exceeds what the Generic Host allows, work that was
 10/// already ACKed is silently lost. Each transport passes only the timeouts it actually spends at
 11/// shutdown; a single shared comparison keeps the ten hand-copied checks from drifting again.
 12/// </summary>
 13internal static class ShutdownBudgetValidator
 14{
 15    /// <summary>
 16    /// Validates that the summed <paramref name="components"/> fit within
 17    /// <paramref name="hostShutdownTimeout"/>. A <c>null</c> budget opts out of the check
 18    /// (the caller validates it externally); a non-positive budget is a configuration error.
 19    /// </summary>
 20    /// <param name="transportName">Human-readable transport name used in the guidance text (e.g. "Kafka").</param>
 21    /// <param name="hostShutdownTimeoutName">Full option path of the transport's mirror option (e.g. "KafkaAsyncRespons
 22    /// <param name="hostShutdownTimeout">The configured mirror of <c>HostOptions.ShutdownTimeout</c>, or <c>null</c> to
 23    /// <param name="components">Option path + value of each timeout the transport spends during graceful shutdown.</par
 24    public static void Validate(
 25        string transportName,
 26        string hostShutdownTimeoutName,
 27        TimeSpan? hostShutdownTimeout,
 28        params (string Name, TimeSpan Value)[] components)
 29    {
 330        if (hostShutdownTimeout is not { } budget)
 231            return;
 32
 333        if (budget <= TimeSpan.Zero)
 234            throw new InvalidOperationException($"{hostShutdownTimeoutName} must be positive when set.");
 35
 336        var total = TimeSpan.Zero;
 337        foreach (var (_, value) in components)
 338            total += value;
 39
 40        // Equality is allowed: the host grants exactly ShutdownTimeout, so a budget that matches
 41        // it can still complete. Only a strictly larger spend guarantees truncation.
 342        if (total <= budget)
 343            return;
 44
 245        var itemized = new StringBuilder();
 246        for (var i = 0; i < components.Length; i++)
 47        {
 248            if (i > 0)
 249                itemized.Append(" plus ");
 250            itemized.Append(components[i].Name).Append(" (").Append(components[i].Value).Append(')');
 51        }
 52
 253        throw new InvalidOperationException(
 254            $"{itemized} requires a shutdown budget of {total}, which exceeds " +
 255            $"{hostShutdownTimeoutName} ({budget}). Increase Microsoft.Extensions.Hosting.HostOptions.ShutdownTimeout " 
 256            $"and mirror that value in {hostShutdownTimeoutName}, " +
 257            $"or reduce the {transportName} shutdown/drain timeouts.");
 58    }
 59}