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

Information
Class: AsyncResponse.Transports.Redis.RedisReplyTargetOptions
Assembly: AsyncResponse.Transports.Redis
File(s): /home/runner/work/AsyncResponse/AsyncResponse/src/Transports/AsyncResponse.Transports.Redis/RedisAsyncResponseTransportOptions.cs
Line coverage
100%
Covered lines: 1
Uncovered lines: 0
Coverable lines: 1
Total lines: 172
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
.ctor()100%11100%

File(s)

/home/runner/work/AsyncResponse/AsyncResponse/src/Transports/AsyncResponse.Transports.Redis/RedisAsyncResponseTransportOptions.cs

#LineLine coverage
 1namespace AsyncResponse.Transports.Redis;
 2
 3/// <summary>
 4/// Options for the Redis Streams AsyncResponse transport. Runs against Redis 5+ and any RESP-compatible
 5/// server that implements Redis Streams and consumer groups — validated on Redis 8, Valkey, and
 6/// Dragonfly. (Garnet does not implement stream commands, so it works as a <em>channel</em> but not as
 7/// this transport.) Publish-time trimming uses plain <c>XADD … MAXLEN ~ N</c> (no Redis 8 trim-mode
 8/// token), so it stays portable across all of these servers.
 9/// </summary>
 10public sealed class RedisAsyncResponseTransportOptions
 11{
 12    public const string TransportName = "redis";
 13
 14    /// <summary>
 15    /// Prefix used when a stream name is not explicitly configured. The default worker stream is
 16    /// <c>{KeyPrefix}:transport:worker</c>, the default response stream is
 17    /// <c>{KeyPrefix}:transport:response</c>, and the default dead-letter stream is
 18    /// <c>{KeyPrefix}:transport:deadletter</c>. Use a unique prefix per app/environment when several
 19    /// deployments share one Redis.
 20    /// </summary>
 21    public string KeyPrefix { get; set; } = "asyncresponse";
 22
 23    /// <summary>
 24    /// Redis stream used by <see cref="RedisWorkerTransport"/> to publish worker jobs. When null,
 25    /// <see cref="KeyPrefix"/> determines the stream name.
 26    /// </summary>
 27    public string? WorkerStream { get; set; }
 28
 29    /// <summary>Consumer group used by the hosted worker subscriber.</summary>
 30    public string WorkerConsumerGroup { get; set; } = "asyncresponse-workers";
 31
 32    /// <summary>Worker stream handling options.</summary>
 33    public RedisSubscriberOptions WorkerSubscriber { get; } = new();
 34
 35    /// <summary>
 36    /// Redis stream remote systems can append response payloads to. The hosted response-ingress
 37    /// subscriber reads this stream and forwards payloads into <see cref="IAsyncResponseIngress"/>.
 38    /// When null, <see cref="KeyPrefix"/> determines the stream name.
 39    /// </summary>
 40    public string? ResponseStream { get; set; }
 41
 42    /// <summary>Consumer group used by the hosted response-ingress subscriber.</summary>
 43    public string ResponseConsumerGroup { get; set; } = "asyncresponse-responses";
 44
 45    /// <summary>Response stream handling options.</summary>
 46    public RedisSubscriberOptions ResponseSubscriber { get; } = new();
 47
 48    /// <summary>
 49    /// Consumer name used inside Redis consumer groups. When null, the package generates a stable
 50    /// process-local name containing machine name, process id, and a short random suffix. Configure
 51    /// this only when your orchestrator guarantees uniqueness per running process.
 52    /// </summary>
 53    public string? ConsumerName { get; set; }
 54
 55    /// <summary>
 56    /// Creates the worker and response consumer groups on subscriber startup. The groups start at
 57    /// the beginning of the stream so messages published before the first subscriber starts are not
 58    /// skipped. Corollary: pointing a brand-new consumer group at a stream that already holds history
 59    /// replays that entire backlog (re-running old worker jobs, re-ingesting old responses). Use a
 60    /// fresh <see cref="KeyPrefix"/>/stream per deployment, or only rename groups while the stream is empty.
 61    /// </summary>
 62    public bool CreateConsumerGroups { get; set; } = true;
 63
 64    /// <summary>
 65    /// Maximum stream length used by XADD for worker and response messages. Redis trims
 66    /// approximately by default, so streams stay bounded without making every publish pay the exact
 67    /// trim cost. Set null to disable publish-time trimming.
 68    /// </summary>
 69    public long? StreamMaxLength { get; set; } = 100_000;
 70
 71    /// <summary>
 72    /// Uses approximate MAXLEN trimming for <see cref="StreamMaxLength"/>. Approximate trimming is
 73    /// much cheaper on hot streams and is the recommended default.
 74    /// </summary>
 75    public bool UseApproximateStreamTrimming { get; set; } = true;
 76
 77    /// <summary>
 78    /// Enables dead-lettering when a message reaches <see cref="RedisSubscriberOptions.MaxDeliveryAttempts"/>
 79    /// or a background handler fails after early ACK. When true and <see cref="DeadLetterStream"/> is
 80    /// null, <see cref="KeyPrefix"/> determines the stream name.
 81    /// </summary>
 82    public bool DeadLetterEnabled { get; set; } = true;
 83
 84    /// <summary>Redis stream that receives poison messages and already-ACKed background failures.</summary>
 85    public string? DeadLetterStream { get; set; }
 86
 87    /// <summary>Maximum dead-letter stream length. Set null to disable dead-letter stream trimming.</summary>
 88    public long? DeadLetterStreamMaxLength { get; set; } = 100_000;
 89
 90    /// <summary>The logical reply target name used by <c>WithReplyTarget()</c>. Default: <c>default</c>.</summary>
 91    public string DefaultReplyTargetName { get; set; } = "default";
 92
 93    /// <summary>
 94    /// Named reply targets exposed to Core through <see cref="IAsyncResponseReplyTargetProvider"/>.
 95    /// When empty, the resolved <see cref="ResponseStream"/> becomes the default target.
 96    /// </summary>
 97    public Dictionary<string, RedisReplyTargetOptions> ReplyTargets { get; } = new(StringComparer.Ordinal);
 98
 99    /// <summary>
 100    /// Redis stream field carrying the AsyncResponse correlation id. Response messages may omit it
 101    /// when the id is present in the JSON body via <see cref="CorrelationIdJsonPaths"/>.
 102    /// </summary>
 103    public string CorrelationIdField { get; set; } = "correlationId";
 104
 105    /// <summary>
 106    /// Redis stream field containing the serialized JSON payload. Worker messages contain a
 107    /// serialized <see cref="WorkerJobEnvelope"/>; response messages contain the remote payload JSON.
 108    /// </summary>
 109    public string PayloadField { get; set; } = "payload";
 110
 111    /// <summary>
 112    /// JSON paths inspected when a response message does not carry the correlation id in
 113    /// <see cref="CorrelationIdField"/>. Paths are case-insensitive and support nested JSON strings.
 114    /// </summary>
 115    public string[] CorrelationIdJsonPaths { get; set; } =
 116    [
 117        "CorrelationId",
 118        "CustomParameters",
 119        "CustomParameters.CorrelationId",
 120        "PubSubParams.CustomParameters",
 121        "PubSubParams.CustomParameters.CorrelationId",
 122        "DagJsonParameters.CorrelationId"
 123    ];
 124
 125    /// <summary>Per-command timeout applied by the transport wrapper before retry/backoff logic.</summary>
 126    public TimeSpan OperationTimeout { get; set; } = TimeSpan.FromSeconds(10);
 127
 128    /// <summary>Maximum attempts for Redis publish commands. Set to 1 to disable publish retries.</summary>
 129    public int PublishMaxAttempts { get; set; } = 3;
 130
 131    /// <summary>Initial delay before retrying a failed publish command.</summary>
 132    public TimeSpan PublishRetryBaseDelay { get; set; } = TimeSpan.FromMilliseconds(50);
 133
 134    /// <summary>Maximum delay between publish retry attempts.</summary>
 135    public TimeSpan PublishRetryMaxDelay { get; set; } = TimeSpan.FromSeconds(1);
 136
 137    /// <summary>Initial delay after a subscriber loop Redis failure.</summary>
 138    public TimeSpan SubscriberRetryBaseDelay { get; set; } = TimeSpan.FromMilliseconds(100);
 139
 140    /// <summary>Maximum delay after repeated subscriber loop Redis failures.</summary>
 141    public TimeSpan SubscriberRetryMaxDelay { get; set; } = TimeSpan.FromSeconds(5);
 142
 143    /// <summary>
 144    /// The hosting shutdown budget that must contain
 145    /// <see cref="RedisSubscriberOptions.BackgroundDrainTimeout"/> when a subscriber uses
 146    /// <see cref="RedisAckMode.AckAfterEnqueue"/>.
 147    /// </summary>
 148    public TimeSpan? HostShutdownTimeout { get; set; } = TimeSpan.FromSeconds(30);
 149
 150    /// <summary>Adds or replaces a named Redis reply target.</summary>
 151    public RedisAsyncResponseTransportOptions AddReplyTarget(string name, string responseStream)
 152    {
 153        ArgumentException.ThrowIfNullOrWhiteSpace(name);
 154        ArgumentException.ThrowIfNullOrWhiteSpace(responseStream);
 155
 156        ReplyTargets[name] = new RedisReplyTargetOptions { ResponseStream = responseStream };
 157        return this;
 158    }
 159}
 160
 161/// <summary>Options for one named Redis Streams async-response reply target.</summary>
 162public sealed class RedisReplyTargetOptions
 163{
 164    /// <summary>Redis stream remote systems should XADD response payloads to.</summary>
 165    public string? ResponseStream { get; set; }
 166
 167    /// <summary>Consumer group that receives responses for this target. Optional metadata.</summary>
 168    public string? ConsumerGroup { get; set; }
 169
 170    /// <summary>Additional values copied to the transport-neutral reply target.</summary>
 3171    public Dictionary<string, string> Properties { get; } = new(StringComparer.Ordinal);
 172}

Methods/Properties

.ctor()