pythonactivemq-classicactivemq-artemisqpidqpid-proton

How to properly signal message redelivery in ActiveMQ Artemis using Qpid Proton Python?


Thanks for helping!!! I'm new to Artemis, but got really surprised by it's potencial!

I'm working with ActiveMQ Artemis and a Python client built with Qpid Proton (version 0.39.0). I'm trying to understand how to reject or release a message in Proton Python such that Artemis attempts redelivery, respecting the <max-delivery-attempts> setting configured in broker.xml.

Here’s the context:

broker.xml relevant section:

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

Python client logic:

In on_message, we release the message when HTTP callback fails:

self.release(event.delivery, delivered=True)

We also tried:

self.settle(event.delivery, state=Delivery.RELEASED)

But neither seemed to make Artemis increment the delivery count. Artemis keeps delivering the same message infinitely and never moves it to the DLQ.

We also tried adding this (suggested in the Artemis docs), but Artemis failed to start:

<persist-delivery-count-before-delivery>true</persist-delivery-count-before-delivery>

Question:

What’s the correct way to release or reject a message in Qpid Proton Python so that Artemis:

  1. Counts it as a failed delivery attempt, and
  2. Eventually sends it to the DLQ after max-delivery-attempts?

Thanks a lot again!!

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
    local_state.type = event.delivery.MODIFIED
    event.delivery.update(local_state)
    self.release(event.delivery, delivered=True)

Solution

  • In most cases you want to send a Modified disposition with the delivery failed flag set to true if you want the server to treat the message as a failed attempt and increase the delivery count. If you do this don't set the undeliverable here unless you want the server to avoid sending you the message again on the same connection.