Handling outstanding Publisher Confirms and deliveryTag reset during channel recovery in RabbitMQ.Client 7.x (.NET 8) #212716
🏷️ Discussion TypeQuestion BodyWe are upgrading our event publisher service to the new Under high throughput, we implement publisher confirms using However, when a network partition or broker restart triggers automatic recovery (
What is the recommended design pattern in Guidelines
|
Replies: 1 comment
|
To safely handle pending publisher confirms during connection or channel recovery in Here is the recommended pattern using the async 1. Fault Pending Confirms on Channel ShutdownAttach a handler to public class ConfirmedPublisher
{
private readonly ConcurrentDictionary<ulong, TaskCompletionSource<bool>> _pendingConfirms = new();
private IChannel _channel;
public async Task InitializeAsync(IConnection connection)
{
var options = new CreateChannelOptions(
publisherConfirmationsEnabled: true,
publisherConfirmationTrackingEnabled: true
);
_channel = await connection.CreateChannelAsync(options);
_channel.ChannelShutdownAsync += OnChannelShutdownAsync;
_channel.BasicAcksAsync += OnBasicAckAsync;
_channel.BasicNacksAsync += OnBasicNackAsync;
}
private Task OnChannelShutdownAsync(object sender, ShutdownEventArgs args)
{
// Fault all lingering tasks for the current channel instance fast
foreach (var key in _pendingConfirms.Keys)
{
if (_pendingConfirms.TryRemove(key, out var tcs))
{
tcs.TrySetException(new ChannelClosedException(args));
}
}
return Task.CompletedTask;
}
private Task OnBasicAckAsync(object sender, BasicAckEventArgs args)
{
HandleAckNack(args.DeliveryTag, args.Multiple, success: true);
return Task.CompletedTask;
}
private Task OnBasicNackAsync(object sender, BasicNackEventArgs args)
{
HandleAckNack(args.DeliveryTag, args.Multiple, success: false);
return Task.CompletedTask;
}
private void HandleAckNack(ulong sequenceNumber, bool multiple, bool success)
{
if (multiple)
{
var keysToClean = _pendingConfirms.Keys.Where(k => k <= sequenceNumber).ToList();
foreach (var key in keysToClean)
{
if (_pendingConfirms.TryRemove(key, out var tcs))
{
if (success) tcs.TrySetResult(true);
else tcs.TrySetException(new Exception("Message NACKed by broker"));
}
}
}
else
{
if (_pendingConfirms.TryRemove(sequenceNumber, out var tcs))
{
if (success) tcs.TrySetResult(true);
else tcs.TrySetException(new Exception("Message NACKed by broker"));
}
}
}
} |
To safely handle pending publisher confirms during connection or channel recovery in
RabbitMQ.Client7.x, you should avoid sharing confirmation state across channel recycles. BecausedeliveryTagsequence numbers reset to1on a new channel session, lingeringTaskCompletionSourceinstances must be faulted immediately when the underlying channel closes.Here is the recommended pattern using the async
IChannellifecycle:1. Fault Pending Confirms on Channel Shutdown
Attach a handler to
IChannel.ChannelShutdownAsyncso that any unexpected disconnection drains and faults all unconfirmed tasks with a explicit exception.