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

Information
Class: AsyncResponse.Transports.RabbitMQ.RabbitMqAsyncResponseOptions
Assembly: AsyncResponse.Transports.RabbitMQ
File(s): /_/src/Transports/AsyncResponse.Transports.RabbitMQ/RabbitMqAsyncResponseOptions.cs
Line coverage
100%
Covered lines: 50
Uncovered lines: 0
Coverable lines: 50
Total lines: 209
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

File(s)

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

#LineLine coverage
 1namespace AsyncResponse.Transports.RabbitMQ;
 2
 3/// <summary>
 4/// Options for the RabbitMQ AsyncResponse transport.
 5/// </summary>
 6public sealed class RabbitMqAsyncResponseOptions
 7{
 8    public const string TransportName = "rabbitmq";
 9
 10    /// <summary>
 11    /// AMQP connection string. When set, it wins over the individual host/user/password settings.
 12    /// Default local RabbitMQ: <c>amqp://guest:guest@localhost:5672/</c>.
 13    /// </summary>
 138414    public string? ConnectionString { get; set; }
 15
 16    /// <summary>RabbitMQ host name used when <see cref="ConnectionString"/> is not set.</summary>
 48817    public string HostName { get; set; } = "localhost";
 18
 19    /// <summary>RabbitMQ AMQP port used when <see cref="ConnectionString"/> is not set.</summary>
 48220    public int Port { get; set; } = 5672;
 21
 22    /// <summary>RabbitMQ virtual host used when <see cref="ConnectionString"/> is not set.</summary>
 48623    public string VirtualHost { get; set; } = "/";
 24
 25    /// <summary>RabbitMQ user name used when <see cref="ConnectionString"/> is not set.</summary>
 48426    public string UserName { get; set; } = "guest";
 27
 28    /// <summary>RabbitMQ password used when <see cref="ConnectionString"/> is not set.</summary>
 48029    public string Password { get; set; } = "guest";
 30
 31    /// <summary>Client-provided connection name shown in RabbitMQ management UI and logs.</summary>
 107732    public string ClientProvidedName { get; set; } = "AsyncResponse";
 33
 34    /// <summary>Enables the RabbitMQ client's automatic connection recovery. Default: <c>true</c>.</summary>
 107135    public bool AutomaticRecoveryEnabled { get; set; } = true;
 36
 37    /// <summary>Enables automatic topology recovery. Default: <c>true</c>.</summary>
 107138    public bool TopologyRecoveryEnabled { get; set; } = true;
 39
 40    /// <summary>
 41    /// How long the client waits between automatic recovery attempts. Must be strictly positive:
 42    /// the RabbitMQ client uses the value directly in its recovery loop. Default: 5 seconds. The
 43    /// hosted subscribers' own restart retries back off separately, governed by
 44    /// <see cref="SubscriberRetryBaseDelay"/>/<see cref="SubscriberRetryMaxDelay"/>.
 45    /// </summary>
 226946    public TimeSpan NetworkRecoveryInterval { get; set; } = TimeSpan.FromSeconds(5);
 47
 48    /// <summary>Requested AMQP heartbeat interval.</summary>
 344749    public TimeSpan RequestedHeartbeat { get; set; } = TimeSpan.FromSeconds(30);
 50
 51    /// <summary>Declares exchanges, queues, and bindings from this package before publish/consume. Default: <c>true</c>
 111752    public bool DeclareTopology { get; set; } = true;
 53
 54    /// <summary>RabbitMQ direct exchange used to publish worker jobs.</summary>
 280655    public string WorkerExchange { get; set; } = "asyncresponse.worker";
 56
 57    /// <summary>Durable queue consumed by the worker subscriber hosted service.</summary>
 274858    public string WorkerQueue { get; set; } = "asyncresponse.worker";
 59
 60    /// <summary>Routing key used to bind and publish worker jobs.</summary>
 182861    public string WorkerRoutingKey { get; set; } = "asyncresponse.worker";
 62
 63    /// <summary>Worker queue handling options.</summary>
 217864    public RabbitMqSubscriberOptions WorkerSubscriber { get; } = new();
 65
 66    /// <summary>RabbitMQ direct exchange remote systems publish response messages to.</summary>
 184967    public string ResponseExchange { get; set; } = "asyncresponse.response";
 68
 69    /// <summary>Durable queue consumed by the response-ingress hosted service.</summary>
 222970    public string ResponseQueue { get; set; } = "asyncresponse.response";
 71
 72    /// <summary>Routing key used to bind and publish response messages.</summary>
 89173    public string ResponseRoutingKey { get; set; } = "asyncresponse.response";
 74
 75    /// <summary>Response queue handling options.</summary>
 184076    public RabbitMqSubscriberOptions ResponseSubscriber { get; } = new();
 77
 78    /// <summary>
 79    /// Optional dead-letter exchange. When set (and <see cref="DeclareTopology"/> is enabled), the worker and
 80    /// response queues are declared with <c>x-dead-letter-exchange</c> so messages rejected without requeue
 81    /// (see <see cref="RabbitMqSubscriberOptions.MaxDeliveryAttempts"/>) are routed here instead of dropped.
 82    /// Changing this on a queue that already exists requires recreating the queue (RabbitMQ rejects a redeclare
 83    /// with different arguments).
 84    /// </summary>
 214985    public string? DeadLetterExchange { get; set; }
 86
 87    /// <summary>
 88    /// Optional dead-letter queue declared and bound to <see cref="DeadLetterExchange"/>. Leave null to manage
 89    /// the dead-letter queue externally. It is declared only together with <see cref="DeadLetterExchange"/>:
 90    /// set on its own (a broker policy supplies the dead-letter exchange) it must already exist — it is still
 91    /// where a capped message is parked unless <see cref="ParkQueue"/> names another queue, and a park that
 92    /// finds no queue fails loudly and the delivery is requeued. Because it is bound to the dead-letter
 93    /// exchange it receives <em>every</em> dead-lettered message: when that exchange also feeds a TTL-retry
 94    /// queue, each retry hop leaves a copy here, including hops of messages that later succeed — use
 95    /// <see cref="ParkQueue"/> for that topology.
 96    /// </summary>
 103997    public string? DeadLetterQueue { get; set; }
 98
 99    /// <summary>
 100    /// Optional queue that receives only the messages the delivery cap parks (see
 101    /// <see cref="RabbitMqSubscriberOptions.MaxDeliveryAttempts"/>): a capped message that has already ridden
 102    /// the dead-letter cycle is published here through the default exchange and ACKed. Declared durable and
 103    /// deliberately <em>unbound</em> when <see cref="DeclareTopology"/> is enabled — with or without a
 104    /// <see cref="DeadLetterExchange"/> — so, unlike <see cref="DeadLetterQueue"/>, it never collects the
 105    /// retry hops of a TTL-retry cycle. When null, capped messages are parked in <see cref="DeadLetterQueue"/>.
 106    /// </summary>
 1547107    public string? ParkQueue { get; set; }
 108
 109    /// <summary>
 110    /// Routing key used both for the <c>x-dead-letter-routing-key</c> argument and for binding
 111    /// <see cref="DeadLetterQueue"/>. When null, dead-lettered messages keep their original routing key and the
 112    /// dead-letter queue is bound with the source queue's routing key.
 113    /// </summary>
 120114    public string? DeadLetterRoutingKey { get; set; }
 115
 116    /// <summary>The logical reply target name used by <c>WithReplyTarget()</c>. Default: <c>default</c>.</summary>
 484117    public string DefaultReplyTargetName { get; set; } = "default";
 118
 119    /// <summary>
 120    /// Named reply targets exposed to Core through <see cref="IAsyncResponseReplyTargetProvider"/>.
 121    /// When empty, <see cref="ResponseExchange"/> + <see cref="ResponseRoutingKey"/> become the default target.
 122    /// </summary>
 504123    public Dictionary<string, RabbitMqReplyTargetOptions> ReplyTargets { get; } = new(StringComparer.Ordinal);
 124
 125    /// <summary>Message header that carries the AsyncResponse correlation id. Default: <c>correlationId</c>.</summary>
 1005126    public string CorrelationIdHeader { get; set; } = "correlationId";
 127
 128    /// <summary>
 129    /// JSON paths inspected when a response message does not carry the correlation id as a
 130    /// message property/header. Paths are case-insensitive and support nested JSON strings.
 131    /// </summary>
 40132    public string[] CorrelationIdJsonPaths { get; set; } =
 474133    [
 474134        "CorrelationId",
 474135        "CustomParameters",
 474136        "CustomParameters.CorrelationId",
 474137        "PubSubParams.CustomParameters",
 474138        "PubSubParams.CustomParameters.CorrelationId",
 474139        "DagJsonParameters.CorrelationId"
 474140    ];
 141
 142    /// <summary>Initial delay after a subscriber loop failure.</summary>
 2363143    public TimeSpan SubscriberRetryBaseDelay { get; set; } = TimeSpan.FromMilliseconds(250);
 144
 145    /// <summary>Maximum delay after repeated subscriber loop failures.</summary>
 2443146    public TimeSpan SubscriberRetryMaxDelay { get; set; } = TimeSpan.FromSeconds(5);
 147
 148    /// <summary>
 149    /// Bounds the connection close while hosted consumers/publishers stop. The close completes in
 150    /// milliseconds when healthy; when it does not, the connection is abandoned anyway, so keep
 151    /// this short — it counts against the host's shutdown budget. Default: <c>5s</c>.
 152    /// </summary>
 2691153    public TimeSpan ShutdownTimeout { get; set; } = TimeSpan.FromSeconds(5);
 154
 155    /// <summary>
 156    /// The hosting shutdown budget that must contain RabbitMQ channel shutdown plus
 157    /// <see cref="RabbitMqSubscriberOptions.BackgroundDrainTimeout"/> when a subscriber uses
 158    /// <see cref="RabbitMqAckMode.AckAfterEnqueue"/>.
 159    /// </summary>
 547160    public TimeSpan? HostShutdownTimeout { get; set; } = TimeSpan.FromSeconds(30);
 161
 162    /// <summary>
 163    /// The broker's <c>consumer_timeout</c>, mirrored here because the client cannot read it: RabbitMQ
 164    /// closes a channel whose delivery stays unacknowledged longer than that (<c>PRECONDITION_FAILED</c>)
 165    /// and requeues the delivery, so a worker handler still running past it — in
 166    /// <see cref="RabbitMqAckMode.AckAfterHandlerCompletes"/> the delivery stays unacknowledged for the
 167    /// handler's whole run — has its job redelivered to another consumer mid-run. The value is advertised
 168    /// through <see cref="IWorkerTransportInFlightLimit.MaxInFlightDuration"/> so durable-flow timers that
 169    /// wait in process plan their waits inside it; nothing in this package enforces it. Default:
 170    /// <c>30 minutes</c>, the broker's default. Keep it equal to (or below) the broker's setting; set
 171    /// <c>null</c> only when the broker's <c>consumer_timeout</c> is disabled. Must be positive.
 172    /// </summary>
 1854173    public TimeSpan? BrokerConsumerTimeout { get; set; } = TimeSpan.FromMinutes(30);
 174
 175    /// <summary>Adds or replaces a named RabbitMQ reply target.</summary>
 176    public RabbitMqAsyncResponseOptions AddReplyTarget(
 177        string name,
 178        string exchange,
 179        string routingKey)
 180    {
 10181        ArgumentException.ThrowIfNullOrWhiteSpace(name);
 10182        ArgumentException.ThrowIfNullOrWhiteSpace(exchange);
 10183        ArgumentException.ThrowIfNullOrWhiteSpace(routingKey);
 184
 10185        ReplyTargets[name] = new RabbitMqReplyTargetOptions
 10186        {
 10187            Exchange = exchange,
 10188            RoutingKey = routingKey
 10189        };
 190
 10191        return this;
 192    }
 193}
 194
 195/// <summary>Options for one named RabbitMQ async-response reply target.</summary>
 196public sealed class RabbitMqReplyTargetOptions
 197{
 198    /// <summary>Exchange remote systems should publish responses to.</summary>
 199    public string? Exchange { get; set; }
 200
 201    /// <summary>Routing key remote systems should publish responses with.</summary>
 202    public string? RoutingKey { get; set; }
 203
 204    /// <summary>Queue that receives responses for this target. Optional; included as metadata for consumers.</summary>
 205    public string? Queue { get; set; }
 206
 207    /// <summary>Additional values copied to the transport-neutral reply target.</summary>
 208    public Dictionary<string, string> Properties { get; } = new(StringComparer.Ordinal);
 209}