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

Information
Class: AsyncResponse.Channels.Redis.RedisChannelMessageQueueSubscriber.Subscription
Assembly: AsyncResponse.Channels.Redis
File(s): /home/runner/work/AsyncResponse/AsyncResponse/src/Channels/AsyncResponse.Channels.Redis/RedisChannelSubscriber.cs
Line coverage
100%
Covered lines: 2
Uncovered lines: 0
Coverable lines: 2
Total lines: 46
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%
DisposeAsync()100%11100%

File(s)

/home/runner/work/AsyncResponse/AsyncResponse/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 so message handling can await the per-channel serial executor (bounded
 12/// backpressure) without sync-over-async blocking a Redis reader thread. Also the unit-test seam:
 13/// <see cref="ChannelMessageQueue"/> is sealed with no public constructor, so tests fake this
 14/// interface rather than the queue.
 15/// </summary>
 16internal interface IRedisChannelSubscriber
 17{
 18    /// <summary>
 19    /// Subscribes to <paramref name="channel"/>, invoking <paramref name="onMessage"/> for each
 20    /// message sequentially (a message's task is awaited before the next is delivered).
 21    /// </summary>
 22    Task<IRedisChannelSubscription> SubscribeAsync(RedisChannel channel, Func<RedisChannel, RedisValue, Task> onMessage)
 23}
 24
 25/// <summary>
 26/// Production <see cref="IRedisChannelSubscriber"/> over <see cref="ISubscriber"/>: a
 27/// <see cref="ChannelMessageQueue"/> per subscription, whose <c>OnMessage(Func&lt;…, Task&gt;)</c>
 28/// loop awaits the handler — preserving per-channel ordering while propagating executor
 29/// backpressure to the queue instead of blocking a reader thread.
 30/// </summary>
 31internal sealed class RedisChannelMessageQueueSubscriber(ISubscriber _subscriber) : IRedisChannelSubscriber
 32{
 33    /// <summary>Runs the SubscribeAsync operation.</summary>
 34    public async Task<IRedisChannelSubscription> SubscribeAsync(RedisChannel channel, Func<RedisChannel, RedisValue, Tas
 35    {
 36        var queue = await _subscriber.SubscribeAsync(channel).ConfigureAwait(false);
 37        queue.OnMessage(message => onMessage(message.Channel, message.Message));
 38        return new Subscription(queue);
 39    }
 40
 141    private sealed class Subscription(ChannelMessageQueue _queue) : IRedisChannelSubscription
 42    {
 43        /// <summary>Releases resources held by this instance.</summary>
 144        public ValueTask DisposeAsync() => new(_queue.UnsubscribeAsync());
 45    }
 46}