Summary
Both RMQ gateways (RMQ.Sync and RMQ.Async) declare their queues as transient, non-exclusive, non-auto-delete. RabbitMQ 4.3 blocks that combination by default, so with the out-of-the-box subscription configuration a Brighter consumer cannot connect at all on 4.3.
CI pins 4.2, so this is invisible today.
Measured
Observed against stock rabbitmq:4-management (RabbitMQ 4.3.5). Every RMQ.Sync gateway test fails during Receive:
Paramore.Brighter.ChannelFailureException : Error connecting to RabbitMQ, see inner exception for details
---- RabbitMQ.Client.Exceptions.OperationInterruptedException : The AMQP operation was interrupted:
AMQP close-reason, initiated by Peer, code=541,
text='INTERNAL_ERROR - Feature `transient_nonexcl_queues` is deprecated.
By default, this feature is not permitted anymore.
The feature will be removed from a future major RabbitMQ version, regardless of the
configuration; actual version to be determined.'
classId=50, methodId=10
Stack reaches the broker through:
RmqMessageConsumer.CreateQueue() -> Channel.QueueDeclare(...)
RmqMessageConsumer.EnsureChannel()
RmqMessageConsumer.Receive(TimeSpan?)
Cause
durable: false, exclusive: false, autoDelete: false is exactly the deprecated transient_nonexcl_queues combination.
src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageConsumer.cs:460
Channel.QueueDeclare(_queueName.Value, _isDurable, false, false, SetQueueArguments());
//NOTE: hasDlq cannot be true if _deadLetterQueuename is null
if (_hasDlq) Channel.QueueDeclare(_deadLetterQueueName!.Value, _isDurable, false, false, new Dictionary<string, object>());
src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageConsumer.cs:541 and :546 do the same asynchronously.
_isDurable comes from the subscription, and false is the default on both gateways — RmqSubscription.cs:106 and :165 (Sync), :113 and :176 (Async). So the failure is the default path, not an unusual configuration. Setting isDurable: true on the subscription avoids it.
Note the dead-letter queue declaration inherits _isDurable too, so a durable subscription fixes both declarations together.
Impact
- On RabbitMQ 4.3 and later, any consumer that does not explicitly opt into
isDurable: true fails to start.
- The deprecation notice says the feature "will be removed from a future major RabbitMQ version, regardless of the configuration" — so permitting it via broker config is a stopgap, not a fix.
Suggested direction
Decide what Brighter should declare when isDurable: false. The options that keep working on 4.3+ are a durable queue, or an auto-delete and/or exclusive queue where the transient lifetime is actually what the caller wanted. Worth settling deliberately, since it changes queue lifetime semantics for existing users.
How it was found
Incidentally, while verifying the migration of the RMQ CI images off the brightercommand/rabbitmq:*-management-delay plugin builds onto stock images (#4240 / #4297). The machine happened to have a 4.3.5 broker on localhost:5672; the CI images are 4.2 and 3.13 and are unaffected.
Summary
Both RMQ gateways (
RMQ.SyncandRMQ.Async) declare their queues as transient, non-exclusive, non-auto-delete. RabbitMQ 4.3 blocks that combination by default, so with the out-of-the-box subscription configuration a Brighter consumer cannot connect at all on 4.3.CI pins 4.2, so this is invisible today.
Measured
Observed against stock
rabbitmq:4-management(RabbitMQ 4.3.5). EveryRMQ.Syncgateway test fails duringReceive:Stack reaches the broker through:
Cause
durable: false,exclusive: false,autoDelete: falseis exactly the deprecatedtransient_nonexcl_queuescombination.src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageConsumer.cs:460src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageConsumer.cs:541and:546do the same asynchronously._isDurablecomes from the subscription, andfalseis the default on both gateways —RmqSubscription.cs:106and:165(Sync),:113and:176(Async). So the failure is the default path, not an unusual configuration. SettingisDurable: trueon the subscription avoids it.Note the dead-letter queue declaration inherits
_isDurabletoo, so a durable subscription fixes both declarations together.Impact
isDurable: truefails to start.Suggested direction
Decide what Brighter should declare when
isDurable: false. The options that keep working on 4.3+ are a durable queue, or an auto-delete and/or exclusive queue where the transient lifetime is actually what the caller wanted. Worth settling deliberately, since it changes queue lifetime semantics for existing users.How it was found
Incidentally, while verifying the migration of the RMQ CI images off the
brightercommand/rabbitmq:*-management-delayplugin builds onto stock images (#4240 / #4297). The machine happened to have a 4.3.5 broker onlocalhost:5672; the CI images are 4.2 and 3.13 and are unaffected.