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

Information
Class: AsyncResponse.Transports.RabbitMQ.RabbitMqSubscriberOptions
Assembly: AsyncResponse.Transports.RabbitMQ
File(s): /_/src/Transports/AsyncResponse.Transports.RabbitMQ/RabbitMqSubscriberOptions.cs
Line coverage
100%
Covered lines: 17
Uncovered lines: 0
Coverable lines: 17
Total lines: 142
Line coverage: 100%
Branch coverage
100%
Covered branches: 6
Total branches: 6
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_AckMode()100%11100%
get_PrefetchCount()100%11100%
get_MaxDeliveryAttempts()100%11100%
get_BackgroundWorkerCount()100%11100%
get_BackgroundQueueCapacity()100%11100%
get_BackgroundDrainTimeout()100%11100%
get_OnBackgroundFailure()100%11100%
UseAckAfterEnqueue(...)100%66100%

File(s)

/_/src/Transports/AsyncResponse.Transports.RabbitMQ/RabbitMqSubscriberOptions.cs

#LineLine coverage
 1namespace AsyncResponse.Transports.RabbitMQ;
 2
 3/// <summary>
 4/// Controls when a RabbitMQ delivery is acknowledged relative to AsyncResponse handling.
 5/// </summary>
 6public enum RabbitMqAckMode
 7{
 8    /// <summary>
 9    /// ACK only after the AsyncResponse handler completes successfully; NACK/requeue if the handler
 10    /// throws. This is the default and preserves broker redelivery for handler failures.
 11    /// </summary>
 12    AckAfterHandlerCompletes = 0,
 13
 14    /// <summary>
 15    /// ACK immediately after the delivery is accepted into a bounded in-process background queue.
 16    /// Handler failures are logged and reported through <see cref="RabbitMqSubscriberOptions.OnBackgroundFailure"/>
 17    /// because RabbitMQ has already been ACKed.
 18    /// </summary>
 19    AckAfterEnqueue = 1
 20}
 21
 22/// <summary>
 23/// Describes a handler failure that happened after a RabbitMQ delivery was already ACKed by
 24/// <see cref="RabbitMqAckMode.AckAfterEnqueue"/>.
 25/// </summary>
 26public sealed class RabbitMqBackgroundFailureContext
 27{
 28    internal RabbitMqBackgroundFailureContext(
 29        string queue,
 30        string subscriberRole,
 31        string exchange,
 32        string routingKey,
 33        ulong deliveryTag,
 34        Exception exception)
 35    {
 36        Queue = queue;
 37        SubscriberRole = subscriberRole;
 38        Exchange = exchange;
 39        RoutingKey = routingKey;
 40        DeliveryTag = deliveryTag;
 41        Exception = exception;
 42    }
 43
 44    /// <summary>The queue whose background worker was handling the delivery.</summary>
 45    public string Queue { get; }
 46
 47    /// <summary>The logical subscriber role, such as <c>Worker</c> or <c>ResponseIngress</c>.</summary>
 48    public string SubscriberRole { get; }
 49
 50    /// <summary>The exchange the delivery came from.</summary>
 51    public string Exchange { get; }
 52
 53    /// <summary>The routing key the delivery came with.</summary>
 54    public string RoutingKey { get; }
 55
 56    /// <summary>The RabbitMQ delivery tag.</summary>
 57    public ulong DeliveryTag { get; }
 58
 59    /// <summary>The exception thrown by the background handler.</summary>
 60    public Exception Exception { get; }
 61}
 62
 63/// <summary>
 64/// Per-queue RabbitMQ subscriber behavior.
 65/// </summary>
 66public sealed class RabbitMqSubscriberOptions
 67{
 68    /// <summary>
 69    /// Controls when the RabbitMQ delivery is ACKed. Defaults to
 70    /// <see cref="RabbitMqAckMode.AckAfterHandlerCompletes"/>.
 71    /// </summary>
 291472    public RabbitMqAckMode AckMode { get; set; } = RabbitMqAckMode.AckAfterHandlerCompletes;
 73
 74    /// <summary>
 75    /// Prefetch count set through <c>basic.qos</c>. Default: <c>16</c>.
 76    /// </summary>
 239277    public ushort PrefetchCount { get; set; } = 16;
 78
 79    /// <summary>
 80    /// Maximum number of times a delivery may be attempted in <see cref="RabbitMqAckMode.AckAfterHandlerCompletes"/>
 81    /// mode before a failing handler rejects it without requeue (dead-lettering it via
 82    /// <see cref="RabbitMqAsyncResponseOptions.DeadLetterExchange"/> when configured, otherwise dropping it).
 83    /// Default: <c>0</c>, meaning unlimited — a failing handler requeues forever, which can hot-loop on a poison
 84    /// message. Set a positive cap (with a dead-letter exchange) to bound retries. The attempt count is read from
 85    /// the broker's <c>x-death</c> header and the <c>redelivered</c> flag; because <c>basic.nack</c> requeue does
 86    /// not increment <c>x-death</c>, the resolved attempt never exceeds 2 on its own, so values above 2 only take
 87    /// effect when the dead-letter path forms a TTL-retry cycle that re-delivers the message (each dead-letter
 88    /// hop increments <c>x-death</c>). A value above 2 without such a cycle behaves like 2 and logs a startup
 89    /// warning. Ignored for <see cref="RabbitMqAckMode.AckAfterEnqueue"/>, which acknowledges before handling
 90    /// and never redelivers.
 91    /// </summary>
 102692    public int MaxDeliveryAttempts { get; set; }
 93
 94    /// <summary>
 95    /// Number of background workers used by <see cref="RabbitMqAckMode.AckAfterEnqueue"/>.
 96    /// Must be explicitly set to a positive value for early ACK mode.
 97    /// </summary>
 26098    public int BackgroundWorkerCount { get; set; }
 99
 100    /// <summary>
 101    /// Maximum number of deliveries waiting in the background queue for
 102    /// <see cref="RabbitMqAckMode.AckAfterEnqueue"/>. When full, the channel's delivery loop pauses
 103    /// (deliveries are dispatched per channel, sequentially) until a worker frees capacity.
 104    /// </summary>
 215105    public int BackgroundQueueCapacity { get; set; }
 106
 107    /// <summary>
 108    /// Maximum time to wait for queued/running background handlers while the hosted subscriber stops.
 109    /// </summary>
 1308110    public TimeSpan BackgroundDrainTimeout { get; set; } = TimeSpan.FromSeconds(20);
 111
 112    /// <summary>
 113    /// Optional callback invoked when a background handler fails after the delivery was already ACKed.
 114    /// Use it to publish to a dead-letter path, increment operator-visible metrics, or alert on
 115    /// already-ACKed work that RabbitMQ cannot redeliver.
 116    /// </summary>
 24117    public Func<RabbitMqBackgroundFailureContext, ValueTask>? OnBackgroundFailure { get; set; }
 118
 119    /// <summary>
 120    /// Explicitly opts this subscriber into ACK-after-enqueue behavior.
 121    /// </summary>
 122    public RabbitMqSubscriberOptions UseAckAfterEnqueue(
 123        int backgroundWorkerCount,
 124        int backgroundQueueCapacity,
 125        TimeSpan? backgroundDrainTimeout = null)
 126    {
 67127        ArgumentOutOfRangeException.ThrowIfNegativeOrZero(backgroundWorkerCount);
 65128        ArgumentOutOfRangeException.ThrowIfNegativeOrZero(backgroundQueueCapacity);
 129
 63130        if (backgroundDrainTimeout is { } timeout && timeout <= TimeSpan.Zero)
 2131            throw new ArgumentOutOfRangeException(nameof(backgroundDrainTimeout), timeout, "Drain timeout must be positi
 132
 61133        AckMode = RabbitMqAckMode.AckAfterEnqueue;
 61134        BackgroundWorkerCount = backgroundWorkerCount;
 61135        BackgroundQueueCapacity = backgroundQueueCapacity;
 136
 61137        if (backgroundDrainTimeout is not null)
 53138            BackgroundDrainTimeout = backgroundDrainTimeout.Value;
 139
 61140        return this;
 141    }
 142}