rabbitmq / rabbitmq-dotnet-client

RabbitMQ .NET client for .NET Standard 2.0+ and .NET 4.6.2+
https://www.rabbitmq.com/dotnet.html
Other
2.08k stars 582 forks source link

IModel.IsClosed set to false after dispose #1086

Closed eduard-bystrov closed 10 months ago

eduard-bystrov commented 3 years ago

Example:

public static async Task Main(string[] args)
{
    var factory = new ConnectionFactory() { HostName = "localhost" };
    using var connection = factory.CreateConnection();

    var channel = connection.CreateModel();

    channel.QueueDeclare(queue: "task_queue", exclusive: false, autoDelete: false);

    var message = "Hello";
    var body = Encoding.UTF8.GetBytes(message);

    // restart rmq service here
    while (true)
    {
        try
        {
            var properties = channel.CreateBasicProperties();
            channel.BasicPublish(exchange: "", routingKey: "task_queue", basicProperties: properties, body: body);
            Console.WriteLine(" [x] Sent {0}", message);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
            break;
        }

        await Task.Delay(1000);
    }

    Console.WriteLine($"IsClosed:{channel.IsClosed}");
    channel.Dispose();
    Console.WriteLine($"IsClosed:{channel.IsClosed}");
}

// [x] Sent Hello
//  ...
// RabbitMQ.Client.Exceptions.AlreadyClosedException: 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
//    at RabbitMQ.Client.Impl.SessionBase.Transmit(OutgoingCommand& cmd)
//    at RabbitMQ.Client.Impl.ModelBase.ModelSend(MethodBase method, ContentHeaderBase header, ReadOnlyMemory`1 body)
//    at RabbitMQ.Client.Framing.Impl.Model._Private_BasicPublish(String exchange, String routingKey, Boolean mandatory, IBasicProperties basicProperties, ReadOnlyMemory`1 body)
//    at RabbitMQ.Client.Impl.ModelBase.BasicPublish(String exchange, String routingKey, Boolean mandatory, IBasicProperties basicProperties, ReadOnlyMemory`1 body)
//    at RabbitMQ.Client.Impl.AutorecoveringModel.BasicPublish(String exchange, String routingKey, Boolean mandatory, IBasicProperties basicProperties, ReadOnlyMemory`1 body)
// IsClosed:True
// IsClosed:False

Is this the correct behavior?

michaelklishin commented 3 years ago

You are supposed to close the channel before disposing it but I guess as a usability improvement we can update the state, too.

According to the stack trace the node was forced to shut down, so there may be connection recovery side effects at play.