1
votes

Background

I have a JMS message queue on Apache Artemis 2.7.0.redhat-00056. The broker is configured with a redelivery-delay of 10 minutes. If I publish a message to the queue and it fails on the consumer then it goes back to the queue as a scheduled message to be delivered in 10 minutes time. Any subsequent messages that are published are processed straightaway, so the queue is not blocked by the scheduled message.

If a number of messages are sent in quick succession then what happens is they all fail and get scheduled for 10 minutes time. In this case it looks like Artemis is trying to preserve the message order.

Documentation

The docs on redelivery say the following:

Other subsequent messages will be delivery regularly, only the cancelled message will be sent asynchronously back to the queue after the delay.

Redelivery documentation

Problem

It seems inconsistent to me that if you publish the messages in close succession that Artemis appears to preserve the order, whereas if there is a slight delay between messages then the queue does not block and only the failed messages are scheduled with a delay (as per the docs).

I'm trying to find a solution so that if one message fails and needs to be redelivered in 10 minutes that it doesn't block subsequent messages.

Example

It doesn't need anything special to recreate this. As described you just need to send some messages in quick succession to a queue that has a redelivery policy on the broker. I've been testing with a basic example as follows:

Spring boot app that produces five messages on startup.

@SpringBootApplication
public class ArtemisTestApplication
{

    private Logger logger = LoggerFactory.getLogger(ArtemisTestApplication.class);

    @Autowired
    private JmsTemplate jmsTemplate;

    @PostConstruct
    public void init()
    {
        send("Message1");
        send("Message2");
        send("Message3");
        send("Message4");
        send("Message5");
    }

    public void send(String msg)
    {
        logger.debug("Sending message :{}", msg);
        jmsTemplate.convertAndSend("jms.queue.TestQueue", msg);
    }

    public static void main(String[] args)
    {
        SpringApplication.run(ArtemisTestApplication.class, args);
    }

}

Consume messages and throw an error to trigger the redelivery policy.

@Component
public class TestConsumer
{
    private Logger logger = LoggerFactory.getLogger(TestConsumer.class);

    @JmsListener(destination = "jms.queue.TestQueue")
    public void receive(TextMessage message) throws JMSException
    {
        logger.debug("Message received: {}", message.getText());
        throw new RuntimeException("Force redelivery policy");
    }
}

The app was generated using the spring boot initializr. Other than giving it a name, the only thing of note selected was the artemis dependancy under messaging.

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-artemis</artifactId>
    </dependency>

In application.properties I have configured the connection properties to the locally running instance of Artemis.

spring.artemis.mode=native
spring.artemis.host=localhost
spring.artemis.port=61616
spring.artemis.user=
spring.artemis.password=

And on the broker I have configured the queue with a redelivery policy. Note: I set the delay to 0 here and the problem still occurs in that all messages are blocked until the first message has had three attempts and been moved to the DLQ. If you change the delay to a positive number then you see all five messages are scheduled for delivery later.

<address-settings>      
    <address-setting match="jms.queue.TestQueue">            
        <dead-letter-address>DLQ</dead-letter-address>                      
        <redelivery-delay>0</redelivery-delay>    
        <max-delivery-attempts>3</max-delivery-attempts>
    </address-setting>    
  </address-settings>

<addresses>     
    <address name="DLQ">            
        <anycast>               
          <queue name="DLQ" />            
        </anycast>         
      </address>       
      <address name="jms.queue.TestQueue">            
        <anycast>               
          <queue name="jms.queue.TestQueue" />            
        </anycast>         
      </address>                      
</addresses>
1
Could you provide a minimal, reproducible example for this?Justin Bertram
Also, you say, "In this case Artemis is trying to preserve the message order." What's your basis for this claim?Justin Bertram
Among various articles I read yesterday I found something that said Artemis will try to preserve the order for messages sent by the same producer. Other than that it is anecdotal, I can see the group of messages using the listScheduledMessages operation and they are all scheduled for 10 minutes later, however, only the first was actually delivered and when the subsequent messages are eventually delivered the delivery count is 1. So it certainly behaves like it is preserving the order.Ben Thurley
I've added an example as requested although tbh I don't see it helping much here. There is nothing special about the code, it's the most basic example of adding a few messages in quick succession and then consuming them with a redelivery policy. It's a question about how the queue works rather than my code.Ben Thurley
Artemis will try to preserve the order of messages sent by the same producer because that's what the JMS specification expects. However, things like the redistribution or load-balancing of messages in a cluster or redelivery delay may change the order ultimately. All those situation are not addressed by the JMS specification.Justin Bertram

1 Answers

1
votes

I have come to the conclusion that this is a bug with Artemis. I raised a ticket for this and a comment has been left with somebody else experiencing the same issue.

https://issues.apache.org/jira/browse/ARTEMIS-2417

In the mean time I have had to change our client application to handle the redelivery policy itself. If there is an error reading the message then we increment a counter on the message and write it as a new message with the required delay. The message being consumed is then acknowledged to unblock the queue and allow the other messages to be read. I have left the redelivery policy configured on the broker as a fall back in case there is an error outside of this logic or something not caught. It's not ideal but it is at least now meeting the requirements.