Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatic recovery fails if the channel is disposed #1647

Closed
MatElcome opened this issue Jul 31, 2024 · 5 comments · Fixed by #1648
Closed

Automatic recovery fails if the channel is disposed #1647

MatElcome opened this issue Jul 31, 2024 · 5 comments · Fixed by #1648
Assignees
Labels
Milestone

Comments

@MatElcome
Copy link

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

  1. Start a rabbitmq server using docker
docker run -d -h rabbit-test --name rabbit-test -p 5672:5672 rabbitmq:3-alpine
  1. Create a new csharp console project, add a reference to RabbitMQ.Client version 7.0.0-rc.6
  2. 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);
        }
    }
}
  1. Restart the rabbitmq server to trigger autorecovery
docker restart rabbit-test

Expected 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)
@MatElcome MatElcome added the bug label Jul 31, 2024
@lukebakken lukebakken added this to the 7.0.0 milestone Jul 31, 2024
@lukebakken lukebakken self-assigned this Jul 31, 2024
@lukebakken
Copy link
Contributor

@MatElcome thank you for the complete issue report!!! I will release a new RC with a fix shortly.

lukebakken added a commit that referenced this issue Jul 31, 2024
* Fix object disposed exception during channel Recovery

Fixes #1647

* * Remove channels that do not recover successfully

* * Hold the `_channelsSemaphore` for a shorter period of time.
@lukebakken
Copy link
Contributor

lukebakken commented Jul 31, 2024

@MatElcome - please test out RC 7 when it is published, which should be within the next 30 minutes -

https://github.com/rabbitmq/rabbitmq-dotnet-client/releases/tag/v7.0.0-rc.7

https://github.com/rabbitmq/rabbitmq-dotnet-client/actions/runs/10184587833

Thank you very much!

@lukebakken
Copy link
Contributor

@MatElcome FYI - hot off of the presses - https://www.nuget.org/packages/RabbitMQ.Client/7.0.0-rc.7

@MatElcome
Copy link
Author

@lukebakken Perfect, RC 7 resolves the issue.

Thanks for such a quick fix - its greatly appreciated!

@lukebakken
Copy link
Contributor

@MatElcome thank YOU for testing the RC versions!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants