I was reading .net documentation(https://www.rabbitmq.com/tutorials/tutorial-seven-dotnet) for the RabbitMq.Client library and reached this part
var channel = connection.CreateModel();
channel.ConfirmSelect();
channel.BasicAcks += (sender, ea) =>
{
// code when message is confirmed
};
channel.BasicNacks += (sender, ea) =>
{
//code when message is nack-ed
};
Quote: It can be tempting to re-publish a nack-ed message from the corresponding callback but this should be avoided, as confirm callbacks are dispatched in an I/O thread where channels are not supposed to do operations. A better solution consists in enqueuing the message in an in-memory queue which is polled by a publishing thread. A class like ConcurrentQueue
would be a good candidate to transmit messages between the confirm callbacks and a publishing thread.
I read about OS-level IO threads, but i'm pretty sure it's not about them. How can a thread be IO? Why they're tellings me i should't be doing this
channel.BasicNacks += async (sender, ea) =>
{
var someIoReuslt = await channel.BasicPublishAsync(queue,...);
};
I tried to look at the internal implementation of the Channel, how they're invoking all the callbacks that're subscribed on this event(nack received)
protected void HandleBasicAck(IncomingCommand cmd)
{
var ack = new BasicAck(cmd.MethodSpan);
if (!_basicAcksWrapper.IsEmpty)
{
var args = new BasicAckEventArgs(ack._deliveryTag, ack._multiple);
_basicAcksWrapper.Invoke(this, args);
}
HandleAckNack(ack._deliveryTag, ack._multiple, false);
}
apart from return type void i see no problem here in terms of performance!
The I/O operations in rabbitmq are mainly for communication between rabbitmq client and rabbit mq broker.
The callbacks ( BasicAcks, BasicNacks ) will use the I/O thread, so you may wonder why this can be a problem. Rabbitmq is designed for fast message transfer and non-blocking message transfer. When you perform heavy operations in the callback, these will happen on the I/O thread that rabbitmq uses, which will affect the performance and may even cause deadlocks ( if you perform heavy tasks in the I/O thread, communication might be interrupted ). Thus, the rabbitmq tutorial suggests using re-queuing strategy like ConcurrentQueue because it provides thread-safe communication between threads. It can hold nacked messages until they are re-published
For handling this re-publishing, you can create a queue service and consume within this service,
Microsoft official docs: https://learn.microsoft.com/en-us/dotnet/core/extensions/queue-service
Hope it helps,