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

Information
Class: AsyncResponse.Channels.Redis.RedisChannelMessageQueueSubscriber
Assembly: AsyncResponse.Channels.Redis
File(s): /_/src/Channels/AsyncResponse.Channels.Redis/RedisChannelSubscriber.cs
Line coverage
100%
Covered lines: 7
Uncovered lines: 0
Coverable lines: 7
Total lines: 52
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%
SubscribeAsync()100%11100%
.ctor(...)100%11100%
DisposeAsync()100%11100%

File(s)

/_/src/Channels/AsyncResponse.Channels.Redis/RedisChannelSubscriber.cs

#LineLine coverage
 1using StackExchange.Redis;
 2
 3namespace AsyncResponse.Channels.Redis;
 4
 5/// <summary>A live pub/sub subscription; disposing it unsubscribes.</summary>
 6internal interface IRedisChannelSubscription : IAsyncDisposable;
 7
 8/// <summary>
 9/// Async-capable subscribe seam over StackExchange.Redis pub/sub. The channel consumes this instead
 10/// of <see cref="ISubscriber.Subscribe(RedisChannel, Action{RedisChannel, RedisValue}, CommandFlags)"/>'s
 11/// synchronous callback — whose handlers the SDK runs on pool threads with no ordering — so
 12/// messages reach the channel one at a time, in order, off any Redis reader thread. Also the
 13/// unit-test seam: <see cref="ChannelMessageQueue"/> is sealed with no public constructor, so
 14/// tests fake this interface rather than the queue.
 15/// <para>
 16/// The handler must not wait for downstream capacity: the SDK queue behind this seam is
 17/// unbounded, so a handler parked on admission does not backpressure the publisher (Redis
 18/// pub/sub has none), it only lets that queue grow. The channel admits non-blockingly and faults
 19/// the wait as indeterminate when its bounded buffer is full.
 20/// </para>
 21/// </summary>
 22internal interface IRedisChannelSubscriber
 23{
 24    /// <summary>
 25    /// Subscribes to <paramref name="channel"/>, invoking <paramref name="onMessage"/> for each
 26    /// message sequentially (a message's task is awaited before the next is delivered).
 27    /// </summary>
 28    Task<IRedisChannelSubscription> SubscribeAsync(RedisChannel channel, Func<RedisChannel, RedisValue, Task> onMessage)
 29}
 30
 31/// <summary>
 32/// Production <see cref="IRedisChannelSubscriber"/> over <see cref="ISubscriber"/>: a
 33/// <see cref="ChannelMessageQueue"/> per subscription, whose <c>OnMessage(Func&lt;…, Task&gt;)</c>
 34/// loop awaits the handler — preserving per-channel ordering off the reader thread. The queue
 35/// itself is unbounded (an SDK detail), which is why the channel's handler never waits in it.
 36/// </summary>
 43837internal sealed class RedisChannelMessageQueueSubscriber(ISubscriber _subscriber) : IRedisChannelSubscriber
 38{
 39    /// <summary>Runs the SubscribeAsync operation.</summary>
 40    public async Task<IRedisChannelSubscription> SubscribeAsync(RedisChannel channel, Func<RedisChannel, RedisValue, Tas
 41    {
 36342        var queue = await _subscriber.SubscribeAsync(channel).ConfigureAwait(false);
 83543        queue.OnMessage(message => onMessage(message.Channel, message.Message));
 36344        return new Subscription(queue);
 36345    }
 46
 36347    private sealed class Subscription(ChannelMessageQueue _queue) : IRedisChannelSubscription
 48    {
 49        /// <summary>Releases resources held by this instance.</summary>
 36350        public ValueTask DisposeAsync() => new(_queue.UnsubscribeAsync());
 51    }
 52}