How does the mandatory flag together with ReturnsCallback handle unroutable messages, and why can't publisher confirms alone detect them?
answer
- unroutable = still acked, silently dropped by default
- setMandatory(true) + setPublisherReturns(true)
- ReturnsCallback -> ReturnedMessage (replyCode 312 NO_ROUTE)
- return arrives BEFORE confirm; confirm still ack
- alternate-exchange as alternative
basics
~20 sSet the mandatory flag on RabbitTemplate. If a published message can't be routed to any queue, the broker returns it and your ReturnsCallback fires. Confirms can't catch this because the broker still acks unroutable messages.
solid answer
~40 sAn exchange with no matching queue for a message's routing key silently discards it by default — and the broker still sends an ack, so publisher confirms report success. To catch this, set rabbitTemplate.setMandatory(true) and enable publisher returns on the CachingConnectionFactory (setPublisherReturns(true)). Now, when a mandatory message is unroutable, the broker returns it to the producer and Spring invokes the registered RabbitTemplate.ReturnsCallback with a ReturnedMessage (the message, replyCode, replyText, exchange, routingKey). This lets you log, alert, or re-route. Note the ordering: the return arrives BEFORE the confirm, and the confirm still comes back as an ack. So a fully reliable producer checks both — a returned message means 'accepted but not routed', while a nack means 'not accepted at all'.
go deeper
Know the mandatory flag returns messages that can't reach any queue.
Wire setMandatory(true) + setPublisherReturns(true) + ReturnsCallback, and read ReturnedMessage fields.
Explain the return-then-ack ordering and why confirms alone are insufficient; know alternate-exchange as an option.
Design a producer that jointly interprets nacks, returns, and acks, and decides between returns vs alternate-exchange operationally.
## Why confirms miss unroutable messages When you publish to an exchange, the exchange applies its binding rules. If **no bound queue matches** the routing key, the default AMQP behaviour is to **silently drop** the message. Critically, the broker still considers it 'handled' and sends a **publisher confirm ack** — because from the broker's perspective it accepted and processed the publish. So publisher confirms report success even though the message vanished. This is the exact blind spot the mandatory flag fills. ## The mandatory flag The AMQP `mandatory` flag tells the broker: *if this message cannot be routed to at least one queue, do not drop it — return it to me.* In Spring AMQP: ```java rabbitTemplate.setMandatory(true); ``` and you must enable returns on the connection factory: ```java cachingConnectionFactory.setPublisherReturns(true); ``` (Setting `ConfirmType.CORRELATED`/`SIMPLE` handles confirms; `setPublisherReturns(true)` handles returns — they're independent switches.) ## ReturnsCallback Register a **`RabbitTemplate.ReturnsCallback`** (the modern functional interface; the older `ReturnCallback` took separate params): ```java rabbitTemplate.setReturnsCallback(returned -> { log.warn("Unroutable: exchange={} rk={} code={} text={} body={}", returned.getExchange(), returned.getRoutingKey(), returned.getReplyCode(), returned.getReplyText(), new String(returned.getMessage().getBody())); }); ``` The callback receives a single **`ReturnedMessage`** object bundling: the `Message`, the `replyCode` (e.g. 312 NO_ROUTE), the `replyText`, the `exchange`, and the `routingKey`. This tells you exactly which message failed to route and why. ## Ordering and interaction with confirms — the key gotcha For an unroutable mandatory message the sequence is: 1. **Return** is delivered first — `ReturnsCallback` fires. 2. **Confirm** is delivered after — and it is an **ack** (`ack=true`), not a nack. So an unroutable message produces BOTH a return AND a positive confirm. A naive producer that only watches confirms will think everything is fine. Reliable code interprets: - **nack (ack=false)** = broker did not accept the message at all. - **return** = broker accepted it but couldn't route it to any queue. - **ack + no return** = accepted and routed (the happy path). ## alternate-exchange as an alternative Instead of returns you can bind an **alternate-exchange** to the primary exchange (`x-alternate-exchange` argument) so unroutable messages flow to a catch-all exchange/queue rather than being returned. Returns and alternate-exchange solve the same problem differently; pick one. ## Gotchas - Forgetting `setPublisherReturns(true)` on the factory means the `ReturnsCallback` never fires even with `setMandatory(true)`. - The callback runs on an amqp/listener thread — keep it fast. - Returns catch 'no queue matched'; they do NOT catch 'queue exists but is full/rejecting' — those are different. - Mandatory does not wait; it's still asynchronous like confirms. ## When to use Use mandatory + returns whenever a misconfigured binding or a typo'd routing key must not silently swallow messages — pair it with confirms so you cover both 'not accepted' and 'accepted but unroutable'.
- If a message is unroutable and mandatory is true, do you get a nack from the confirm callback?No. You get a return (ReturnsCallback) plus a positive ack from the confirm callback. A nack means the broker didn't accept the message at all, which is a different failure.
- What must you enable besides setMandatory(true) for returns to work?setPublisherReturns(true) on the CachingConnectionFactory. Without it the broker/Spring won't deliver returned messages to your ReturnsCallback.
- What's an alternative to returns for handling unroutable messages?Configure an alternate-exchange (x-alternate-exchange) on the primary exchange so unroutable messages are re-routed to a catch-all exchange/queue instead of being returned to the producer.
saying these in an interview costs you the question
- Claiming publisher confirms alone detect unroutable messages
- Thinking an unroutable message triggers a nack
- Forgetting setPublisherReturns(true) and expecting ReturnsCallback to fire
- Believing the confirm arrives before the return