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

Information
Class: AsyncResponse.Transports.GooglePubSub.GooglePubSubSubscriberOptions
Assembly: AsyncResponse.Transports.GooglePubSub
File(s): /home/runner/work/AsyncResponse/AsyncResponse/src/Transports/AsyncResponse.Transports.GooglePubSub/GooglePubSubSubscriberOptions.cs
Line coverage
100%
Covered lines: 11
Uncovered lines: 0
Coverable lines: 11
Total lines: 122
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
.ctor()100%11100%
UseAckAfterEnqueue(...)100%66100%

File(s)

/home/runner/work/AsyncResponse/AsyncResponse/src/Transports/AsyncResponse.Transports.GooglePubSub/GooglePubSubSubscriberOptions.cs

#LineLine coverage
 1using Google.Cloud.PubSub.V1;
 2
 3namespace AsyncResponse.Transports.GooglePubSub;
 4
 5/// <summary>
 6/// Controls when a Google Pub/Sub message is acknowledged relative to AsyncResponse handling.
 7/// </summary>
 8public enum GooglePubSubAckMode
 9{
 10    /// <summary>
 11    /// ACK only after the AsyncResponse handler completes successfully; NACK if the handler throws.
 12    /// This is the default and preserves Pub/Sub retry semantics for handler failures.
 13    /// </summary>
 14    AckAfterHandlerCompletes = 0,
 15
 16    /// <summary>
 17    /// ACK immediately after the message is accepted into a bounded in-process background queue.
 18    /// Handler failures are logged and reported through
 19    /// <see cref="GooglePubSubSubscriberOptions.OnBackgroundFailure"/> because Pub/Sub has already
 20    /// been ACKed.
 21    /// </summary>
 22    AckAfterEnqueue = 1
 23}
 24
 25/// <summary>
 26/// Describes a handler failure that happened after a Google Pub/Sub message was already ACKed by
 27/// <see cref="GooglePubSubAckMode.AckAfterEnqueue"/>.
 28/// </summary>
 29public sealed class GooglePubSubBackgroundFailureContext
 30{
 31    internal GooglePubSubBackgroundFailureContext(
 32        string subscriptionId,
 33        string subscriberRole,
 34        PubsubMessage message,
 35        Exception exception)
 36    {
 37        SubscriptionId = subscriptionId;
 38        SubscriberRole = subscriberRole;
 39        Message = message;
 40        Exception = exception;
 41    }
 42
 43    /// <summary>The subscription whose background worker was handling the message.</summary>
 44    public string SubscriptionId { get; }
 45
 46    /// <summary>The logical subscriber role, such as <c>Worker</c> or <c>ResponseIngress</c>.</summary>
 47    public string SubscriberRole { get; }
 48
 49    /// <summary>The Pub/Sub message that failed after being ACKed.</summary>
 50    public PubsubMessage Message { get; }
 51
 52    /// <summary>The Pub/Sub message id, when provided by Google Pub/Sub.</summary>
 53    public string MessageId => Message.MessageId;
 54
 55    /// <summary>The exception thrown by the background handler.</summary>
 56    public Exception Exception { get; }
 57}
 58
 59/// <summary>
 60/// Per-subscription Google Pub/Sub subscriber behavior.
 61/// </summary>
 62public sealed class GooglePubSubSubscriberOptions
 63{
 64    /// <summary>
 65    /// Controls when the Pub/Sub callback returns ACK. Defaults to
 66    /// <see cref="GooglePubSubAckMode.AckAfterHandlerCompletes"/>.
 67    /// </summary>
 68    public GooglePubSubAckMode AckMode { get; set; } = GooglePubSubAckMode.AckAfterHandlerCompletes;
 69
 70    /// <summary>
 71    /// Number of background workers used by <see cref="GooglePubSubAckMode.AckAfterEnqueue"/>.
 72    /// Must be explicitly set to a positive value for early ACK mode.
 73    /// Values greater than one allow concurrent handling and therefore do not preserve message
 74    /// ordering.
 75    /// </summary>
 76    public int BackgroundWorkerCount { get; set; }
 77
 78    /// <summary>
 79    /// Maximum number of messages waiting in the background queue for
 80    /// <see cref="GooglePubSubAckMode.AckAfterEnqueue"/>. Must be explicitly set to a positive value.
 81    /// When full, the Pub/Sub callback parks awaiting queue space and ACKs once the message is
 82    /// accepted — it does not NACK on a full queue; NACK is returned only when the write fails
 83    /// (shutdown/disposal), so the message is redelivered rather than lost.
 84    /// </summary>
 85    public int BackgroundQueueCapacity { get; set; }
 86
 87    /// <summary>
 88    /// Maximum time to wait for queued/running background handlers while the hosted subscriber stops.
 89    /// </summary>
 390    public TimeSpan BackgroundDrainTimeout { get; set; } = TimeSpan.FromSeconds(20);
 91
 92    /// <summary>
 93    /// Optional callback invoked when a background handler fails after the message was already ACKed.
 94    /// Use it to publish to a dead-letter path, increment operator-visible metrics, or alert on
 95    /// already-ACKed work that Pub/Sub cannot redeliver.
 96    /// </summary>
 97    public Func<GooglePubSubBackgroundFailureContext, ValueTask>? OnBackgroundFailure { get; set; }
 98
 99    /// <summary>
 100    /// Explicitly opts this subscriber into ACK-after-enqueue behavior.
 101    /// </summary>
 102    public GooglePubSubSubscriberOptions UseAckAfterEnqueue(
 103        int backgroundWorkerCount,
 104        int backgroundQueueCapacity,
 105        TimeSpan? backgroundDrainTimeout = null)
 106    {
 3107        ArgumentOutOfRangeException.ThrowIfNegativeOrZero(backgroundWorkerCount);
 3108        ArgumentOutOfRangeException.ThrowIfNegativeOrZero(backgroundQueueCapacity);
 109
 3110        if (backgroundDrainTimeout is { } timeout && timeout <= TimeSpan.Zero)
 3111            throw new ArgumentOutOfRangeException(nameof(backgroundDrainTimeout), timeout, "Drain timeout must be positi
 112
 3113        AckMode = GooglePubSubAckMode.AckAfterEnqueue;
 3114        BackgroundWorkerCount = backgroundWorkerCount;
 3115        BackgroundQueueCapacity = backgroundQueueCapacity;
 116
 3117        if (backgroundDrainTimeout is not null)
 3118            BackgroundDrainTimeout = backgroundDrainTimeout.Value;
 119
 3120        return this;
 121    }
 122}