-
Notifications
You must be signed in to change notification settings - Fork 613
Closed
Description
Describe the bug
If the connection to the RabbitMQ server is interrupted (eg. networking issue, server restart), the client library will try to recover and recreate any open channels (provided the AutomaticRecoveryEnabled = true option is used). However, the recovery will fail if a channel is disposed in user code before the recovery operation succeeds.
This issue is present in version 7.0.0-rc.6 of RabbitMQ.Client - it doesn't seem to affect version 6.8.1
Reproduction steps
- Start a rabbitmq server using docker
docker run -d -h rabbit-test --name rabbit-test -p 5672:5672 rabbitmq:3-alpine- Create a new csharp console project, add a reference to
RabbitMQ.Clientversion7.0.0-rc.6 - Add the following code:
RabbitMQ.Client.ConnectionFactory connectionFactory = new()
{
AutomaticRecoveryEnabled = true,
UserName = "guest",
};
string exchangeName = "test-exchange";
// Initial setup: create exchange
{
using var connection = await connectionFactory.CreateConnectionAsync();
using var channel = await connection.CreateChannelAsync();
await channel.ExchangeDeclareAsync(exchangeName, "topic", true, false);
}
// Send message loop
{
using var connection = await connectionFactory.CreateConnectionAsync();
for (int i = 0; i < 300; i++)
{
try
{
using var channel = await connection.CreateChannelAsync(); // New channel for each message
await Task.Delay(1000);
await channel.BasicPublishAsync(exchangeName, "", new RabbitMQ.Client.BasicProperties(), System.Text.Encoding.UTF8.GetBytes("test"));
Console.WriteLine($"Sent message {i}");
}
catch (Exception ex)
{
Console.WriteLine($"Failed to send message {i}: {ex.Message}");
await Task.Delay(1000);
}
}
}- Restart the rabbitmq server to trigger autorecovery
docker restart rabbit-testExpected behavior
Expected output:
Sent message 0
Sent message 1
Sent message 2
Sent message 3
Failed to send message 4: Already closed: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=320, text='CONNECTION_FORCED - broker forced connection closure with reason 'shutdown'', classId=0, methodId=0
Failed to send message 5: Already closed: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=320, text='CONNECTION_FORCED - broker forced connection closure with reason 'shutdown'', classId=0, methodId=0
...
Failed to send message 17: Already closed: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=320, text='CONNECTION_FORCED - broker forced connection closure with reason 'shutdown'', classId=0, methodId=0
Sent message 18
Sent message 19
Actual output:
Sent message 0
Sent message 1
Sent message 2
Sent message 3
Sent message 4
Failed to send message 5: Already closed: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=320, text='CONNECTION_FORCED - broker forced connection closure with reason 'shutdown'', classId=0, methodId=0
Failed to send message 6: Already closed: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=320, text='CONNECTION_FORCED - broker forced connection closure with reason 'shutdown'', classId=0, methodId=0
...
Failed to send message 23: Already closed: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=320, text='CONNECTION_FORCED - broker forced connection closure with reason 'shutdown'', classId=0, methodId=0
Failed to send message 24: Already closed: The AMQP operation was interrupted: AMQP close-reason, initiated by Application, code=541, text='FailedAutoRecovery', classId=0, methodId=0
Failed to send message 25: Already closed: The AMQP operation was interrupted: AMQP close-reason, initiated by Application, code=541, text='FailedAutoRecovery', classId=0, methodId=0
Failed to send message 26: Already closed: The AMQP operation was interrupted: AMQP close-reason, initiated by Application, code=541, text='FailedAutoRecovery', classId=0, methodId=0
...
Notice how in message 24 onwards, the error message is FailedAutoRecovery.
Additional context
The following exception is thrown during autorecovery:
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'RabbitMQ.Client.Impl.AutorecoveringChannel'.
at RabbitMQ.Client.Impl.AutorecoveringChannel.<ThrowIfDisposed>g__ThrowDisposed|87_0()
at RabbitMQ.Client.Impl.AutorecoveringChannel.AutomaticallyRecoverAsync(AutorecoveringConnection conn, Boolean recoverConsumers, Boolean recordedEntitiesSemaphoreHeld, CancellationToken cancellationToken)
at RabbitMQ.Client.Framing.Impl.AutorecoveringConnection.RecoverChannelsAndItsConsumersAsync(Boolean recordedEntitiesSemaphoreHeld, CancellationToken cancellationToken)
at RabbitMQ.Client.Framing.Impl.AutorecoveringConnection.TryPerformAutomaticRecoveryAsync(CancellationToken cancellationToken)