-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Added slow consumer events Slow consumers are subscriptions which are unable to process their messages fast enough. When the internal subscription channel is full oldest messages should be dropped and new messages should continued to be processed. This is aligned with NATS at most once delivery. See also https://docs.nats.io/running-a-nats-service/nats_admin/slow_consumers * Avoid capturing too many references Now only captures 'this' reference and not 'connection' in addition. * Set channel default capacity 1024 Also opts SubPending* rename. * Don't drop messages in perf test
- Loading branch information
Showing
7 changed files
with
191 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
namespace NATS.Client.Core; | ||
|
||
public interface INatsError | ||
{ | ||
string Message { get; } | ||
} | ||
|
||
public sealed class MessageDroppedError : INatsError | ||
{ | ||
public MessageDroppedError(NatsSubBase subscription, int pending, string subject, string? replyTo, NatsHeaders? headers, object? data) | ||
{ | ||
Subscription = subscription; | ||
Pending = pending; | ||
Subject = subject; | ||
ReplyTo = replyTo; | ||
Headers = headers; | ||
Data = data; | ||
Message = $"Dropped message from {subject} with {pending} pending messages"; | ||
} | ||
|
||
public NatsSubBase Subscription { get; } | ||
|
||
public int Pending { get; } | ||
|
||
public string Subject { get; } | ||
|
||
public string? ReplyTo { get; } | ||
|
||
public NatsHeaders? Headers { get; } | ||
|
||
public object? Data { get; } | ||
|
||
public string Message { get; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
namespace NATS.Client.Core.Tests; | ||
|
||
public class SlowConsumerTest | ||
{ | ||
private readonly ITestOutputHelper _output; | ||
|
||
public SlowConsumerTest(ITestOutputHelper output) => _output = output; | ||
|
||
[Fact] | ||
public async Task Slow_consumer() | ||
{ | ||
await using var server = NatsServer.Start(); | ||
var nats = server.CreateClientConnection(new NatsOpts { SubPendingChannelCapacity = 3 }); | ||
|
||
var lost = 0; | ||
nats.OnError += (_, e) => | ||
{ | ||
if (e is MessageDroppedError dropped) | ||
{ | ||
Interlocked.Increment(ref lost); | ||
_output.WriteLine($"LOST {dropped.Data}"); | ||
} | ||
}; | ||
|
||
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); | ||
var cancellationToken = cts.Token; | ||
|
||
var sync = 0; | ||
var end = 0; | ||
var count = 0; | ||
var signal = new WaitSignal(); | ||
var subscription = Task.Run( | ||
async () => | ||
{ | ||
await foreach (var msg in nats.SubscribeAsync<string>("foo.>", cancellationToken: cancellationToken)) | ||
{ | ||
if (msg.Subject == "foo.sync") | ||
{ | ||
Interlocked.Increment(ref sync); | ||
continue; | ||
} | ||
if (msg.Subject == "foo.end") | ||
{ | ||
Interlocked.Increment(ref end); | ||
break; | ||
} | ||
await signal; | ||
Interlocked.Increment(ref count); | ||
_output.WriteLine($"GOOD {msg.Data}"); | ||
} | ||
}, | ||
cancellationToken); | ||
|
||
await Retry.Until( | ||
"subscription is ready", | ||
() => Volatile.Read(ref sync) > 0, | ||
async () => await nats.PublishAsync("foo.sync", cancellationToken: cancellationToken)); | ||
|
||
for (var i = 0; i < 10; i++) | ||
{ | ||
await nats.PublishAsync("foo.data", $"A{i}", cancellationToken: cancellationToken); | ||
} | ||
|
||
signal.Pulse(); | ||
|
||
for (var i = 0; i < 10; i++) | ||
{ | ||
await nats.PublishAsync("foo.data", $"B{i}", cancellationToken: cancellationToken); | ||
} | ||
|
||
await Retry.Until( | ||
"subscription is ended", | ||
() => Volatile.Read(ref end) > 0, | ||
async () => await nats.PublishAsync("foo.end", cancellationToken: cancellationToken)); | ||
|
||
await subscription; | ||
|
||
// we should've lost most of the messages because of the low channel capacity | ||
Volatile.Read(ref count).Should().BeLessThan(20); | ||
Volatile.Read(ref lost).Should().BeGreaterThan(0); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters