RabbitMQ Publisher Confirms and Returns Callback with/without Mandatory flag
RabbitMQ is a great open source message broker, which is available for multiple platforms (cloud foundry, helm charts, docker containers, local development machine, etc.) and client libraries for multiple programming languages (Java, .NET, Python, Ruby, etc.).
While it’s easy to get started with RabbitMQ for simple messaging needs, it requires a bit more careful approach if you are standing up a production grade cluster or if you are developing applications with guaranteed delivery in mind. The latter part being the focus of this article.
While reliability guide on RabbitMQ provides fairly general principles of what to keep in mind or what to avoid, I was looking for a more hands-on approach to understand the parts and pieces involved in understanding publisher confirms. (The consumer acknowledgements are fairly straight forward to understand and the write up on Consumer Acknowledgements and Publisher Confirms is fairly detailed in walking through the various ACK options and Prefetch settings)
I was aided by this code sample that comes bundled with spring-amqp samples repo. I have created a simple table that walks through various flags that can be set and the behaviour observed (on my local macos development machine, running rabbit 3.7.7 available through brew and JDK 1.8):
What the above table shows is the setup where messages are published to an exchange with a routing queue that has a queue bound (routable queue) to it (the middle columns) and messages sent to an exchange with a routing queue that doesn’t have a queue bound (unroutable queue) (the right most columns in the screenshot above).
The 3 flags in the left-most columns are enabled/disabled by setting the options in application.properties. The 3 boolean flags make for 8 possible combinations and as such, you see 8 different rows for various behavior observed with the application.
As is evident from the results, in order to absolutely guarantee message delivery from publisher to the RabbitMQ broker it’s not sufficient to just enable the “publisher-confirms”, but you should ideally enable all 3 flags: “publisher-confirms”, “publisher-returns” and “template.mandatory”.
This condition is the last row in the table where you receive a positive ACK from broker that your message has been received and is also routed to a queue bound to the exchange with the routing key that you used to publish the message (routable queue). However, in the case of unroutable queue, you also receive a positive ACK (which is mis-leading if you were relying on ACK/NACKS alone for guaranteed delivery) but most importantly, the “returns callback” function is alerted with the replyCode: 312 and replyText: NO_ROUTE indicating that the message could not be properly routed.
You might notice a similarity between the last or 8th row in the table and 6th row which has similar behavior but the only difference is the explicit enabling of “publisher-returns” flag in application.properties which is a good convention to follow in any case if you are going to create a “returns callback” function. (Rest of the rows are mostly self-explanatory)
So, hopefully, this little exercise helped you in understanding the various flags you have at your disposal and how they enable the guaranteed delivery on a publishing application. The biggest takeaway for me was in order to be absolutely sure that a message gets routed to at least 1 queue, set “mandatory” flag and provide callback functions for publisher “returns” & “confirms” to record/retire the corresponding correlation ids.