I would like to open, what we consider a bug.

We are using the Spring framework "spring-jms" with rabbitmq-jms component. Because we want to know when new messages are coming, we use "DefaultMessageListenerContainer" (DMLC), we have configured it like that:

<bean id="some_listenerContainerListen" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <property name="autoStartup" value="false"/>
    <property name="concurrentConsumers" value="${some.jms.consumer.concurrent.medium}"/>
    <property name="connectionFactory" ref="cachingJmsRmqConnectionFactory"/>
    <property name="destination" ref="some_queue_inbound_listen"/>
    <property name="messageListener" ref="some_messageListener"/>
    <property name="sessionTransacted" value="true"/>       
</bean>

And we have defined 4 or 5 such listener, each with different settings.

So we can send messages, we receive them, we handle them. Everything works without any problems.

The issue we are facing, is that in RabbitMQ we are using some Quorum Queues, and every 4-5 hours we get tons of "segment" files, and they are not cleaned up. And after a while our filesystem is full, and we need to do some clean up, by stopping the applications, etc...

After some digging, and opening a ticket to the RabbitMQ people ( https://github.com/rabbitmq/discussions/issues/164 ) we have discovered that we are using "Channel.basicGet()", and this is creating these tons of segment files. Clearly in our code we have zero reference to Channel.basicGet(), but by doing some debugging, we have found that, DMLC will call through the RabbitMQ-jms api the Channel.basicGet function nearly every seconds (or maybe a little bit more), which is not a good practice in rabbitMQ ( https://www.cloudamqp.com/blog/2020-07-14-rabbitmq-basic-consume-vs-rabbitmq-basic-get.html ).

Here is the sequence from DMLC to Channel.basicGet() : in Spring-jms: - DMLC.AsyncMessageListenerInvoker.run() - DMLC.AsyncMessageListenerInvoker.executeOngoingLoop() - DMLC.AsyncMessageListenerInvoker.invokeListener() - AbstractPollingMessageListenerContainer.receiveAndExecute() - AbstractPollingMessageListenerContainer.doReceiveAndExecute() - AbstractPollingMessageListenerContainer.receiveMessage() - JmsDestinationAccessor.receiveFromConsumer()

The rest goes In RabbitMQ-jms: - RMQMessageConsumer.receive(long) - RMQMessageConsumer.receive(TimeTracker) - DelayedReceiver.get() - RMQMessageConsumer.getFromRabbitQueue() - AutoRecoveringChannel.basicGet()

We are looking at how to replace the DMLC on our side, so we don't use indirectly the basicGet method of RabbitMQ, and try to implement the basic Consume method, which is recommended way by RabbitMQ.

So this is the bug. if we use DMLC to handle messages, then on the rabbitmq side, we are facing a filesystem issue (disk full). We were using the vanilla way to do it (https://spring.io/guides/gs/messaging-rabbitmq/ & https://spring.io/guides/gs/messaging-jms/).

One way to solve it, would be to have like a "DefaultGETMessageListenerContainer" which use in RabbitMQ the basicGet, as of today, it could be default "DefaultMessageListenerContainer". And another one called maybe "DefaultConsumeMessageListenerContainer", which will use in RabbitMQ the basicConsume method. Or maybe show an example, on how to do it with the other method, with the documentation.

But as it is now, the DMLC is not ready for production, and should not be used with RabbitMQ, this is what we learned during the previous weeks of investigation to find the problem...

Am I wrong in my assumption? Do you see how we could solve it? Is this a bug, or a "feature"? Do we have missed some documentation somewhere that explain how to solve it?

Thank you for your help.

Regards, Alessandro

Comment From: jhoeller

DefaultMessageListenerContainer is designed around a MessageConsumer.receive polling mechanism, with various dynamic scaling options, based on common JMS compatibility concerns (including JTA integration concerns) for application servers and also for standalone providers such as ActiveMQ. It is widely used in production in such scenarios for more than a decade already.

Alternatively, we ship SimpleMessageListenerContainer based on MessageConsumer.setMessageListener, with a fixed number of consumers and no adaptive scaling. This is simply another variant of using the standard JMS API, less focused on EE application servers and typically only used with a standalone provider.

That said, we do not mean to target RabbitMQ with those components at all. Our primary RabbitMQ integration scenario is via the Spring AMQP project, bypassing JMS completely (see e.g. https://spring.io/guides/gs/messaging-rabbitmq/). We do not consider the RabbitMQ JMS bridge as a preferred way of integration here.

Maybe SimpleMessageListenerContainer works better with the RabbitMQ JMS bridge, but even that is not supported on our end. If you'd like to specifically integrate with RabbitMQ, you're probably better off without the JMS API to begin with. Our Spring AMQP project mirrors Spring's JMS support quite closely, so there shouldn't be anything you could be missing there.

Comment From: ptitvert

Thank you for the information :-)