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

  • You need to take the time to read the specification and understand what the various dispositions mean both on the target and at the source.

    Released means that the source should treat the message as having not been processed and return it to an initial state, where Rejected means the message is considered invalid and should enter a terminal state. The proper outcome for recording failed attempts is the Modified disposition which offers a delivery failed flag that indicates the delivery count should be incremented, which is what the server is going to use to eventually DLQ the message after a sufficient number of failed attempts.

    The AMQP specification is fairly explicit about what happens with the delivery count based on the outcome received so a reading of that can help, but as a simple rule the Modified(deliveryFailed=true) disposition is the one sure way to have the delivery count updated on the server.