amqpactivemq-artemisqpidqpid-proton

Qpid Proton Python release(delivered=True) not triggering redelivery in ActiveMQ Artemis (2.40.0)


I'm using Apache ActiveMQ Artemis 2.40.0 as the broker and Qpid Proton Python 0.39.0 (python-qpid-proton) as the AMQP 1.0 client.

The goal is to have messages redelivered up to a configured limit (max-delivery-attempts) when the consumer explicitly does not acknowledge (NACK) a message, using the release(delivered=True) method from Proton's MessagingHandler.

I’ve configured the broker.xml in Artemis with the following <address-setting> block:

<address-setting match="#">
    <expiry-address>ExpiryQueue</expiry-address>
    <dead-letter-address>DLQ</dead-letter-address>
    <auto-create-dead-letter-resources>true</auto-create-dead-letter-resources>
    <dead-letter-queue-prefix></dead-letter-queue-prefix>
    <dead-letter-queue-suffix>.DLQ</dead-letter-queue-suffix>
    <max-delivery-attempts>5</max-delivery-attempts>
    <redelivery-delay>5000</redelivery-delay>
</address-setting>

The Python Code:

Here’s a snippet from the on_message method in my Qpid Proton consumer:

def on_message(self, event):
    message = event.message
    status = send_message_callback(self.enrollment["target_url"], message.body)

    if 200 <= status < 300:
        self.accept(event.delivery)
    else:
        # Explicitly NACK the message
        self.release(event.delivery, delivered=True)

Despite using release(delivered=True), Artemis seems to redeliver the message indefinitely (ignoring the <max-delivery-attempts>), unless I switch to reject(event.delivery), which immediately sends the message to the DLQ on the first failure.

I’ve also tried adding the tag <persist-delivery-count-before-delivery>true</persist-delivery-count-before-delivery> to the broker.xml, but without success.

Expected Behavior:

  1. Upon release(delivered=True), Artemis should:

    • Count a redelivery attempt
    • Retry the message based on max-delivery-attempts and redelivery-delay
    • Send it to the DLQ after 5 failed attempts
  2. release(delivered=True) should behave as a soft NACK, as described in Proton’s documentation

Problem:

Artemis keeps redelivering the message infinitely, never counting toward max-delivery-attempts. It seems the delivery count is not being persisted or acknowledged as expected from Proton.

What I’ve Tried:

Question:

Thanks in advance!

RESOLUTION:

I wasn't updating the local state object before releasing the message. So in order to use the max-delivery-attempts policy with the DLQ defined in broker.xml, it must complete these local properties:

def on_message(self, event):
    message = event.message
    status = send_message_callback(self.enrollment["target_url"], message.body)

    if 200 <= status < 300:
        self.accept(event.delivery)
    else:
        # Explicitly NACK the message
        local_state = event.delivery.local
        local_state.failed = True
        local_state.undeliverable = False
        event.delivery.update(local_state.type)
        self.settle(event.delivery, event.delivery.MODIFIED)

Solution

  • I think this is arguably a bug in the Qpid Proton Python bindings: If you are releasing a delivered message then I think the default reason for that would be that the delivery failed. I've raised a bug (https://issues.apache.org/jira/browse/PROTON-2883) against the bindings and this will probably be fixed in the next release (0.41).

    As an aside, I think the fixed code is a little more verbose than necessary, it could be:

    def on_message(self, event):
        message = event.message
        status = send_message_callback(self.enrollment["target_url"], message.body)
    
        if 200 <= status < 300:
            self.accept(event.delivery)
        else:
            # Explicitly NACK the message
            event.delivery.local.failed = True
            self.settle(event.delivery, event.delivery.MODIFIED)
    

    That's because the default undeliverable flag is False, and settling a delivery will update the delivery state.,