So far, the worker treats every error the same way: It throws, the processor abandons the message, and after ten deliveries it lies in the dead-letter queue. For a briefly overloaded database that is right, because the third or fourth attempt works. For a notice with broken JSON it is wrong: No attempt will heal it. Ten attempts cost time and operations, and in the end the dead letter says only “maximum delivery count exceeded”, not what was really going on.
In this lesson the worker tells the two kinds of errors apart. A poison message that can never be processed it puts away at once with a reason of its own. A small tool shows what lies in the dead letter and requeues messages after a repair. And an alert reports as soon as something lies there.
The dead-letter queue
Every queue and every subscription has a dead-letter queue (DLQ) of its own. It comes into being on its own and can be neither
deleted nor configured separately. With the SDK you reach it through SubQueue.DeadLetter, as in lesson 3.5.2; tools and the CLI
write the path tender-import/$deadletterqueue. There are three properties you should know:
- Messages in the dead letter don't expire. The time to live doesn't apply there.
- Nothing cleans it up. What lies there stays until someone picks it up and completes it.
- Every message carries two properties:
DeadLetterReasonandDeadLetterErrorDescription.
The reason is set either by Service Bus itself or by your code. Service Bus knows these reasons:
| Reason | When |
|---|---|
MaxDeliveryCountExceeded |
The message was delivered more often than MaxDeliveryCount allows without being completed. |
TTLExpiredException |
The message expired, and the entity has deadLetteringOnMessageExpiration switched on. |
Session ID is null |
A message without a SessionId arrived in an entity with sessions. |
HeaderSizeExceeded |
The message's properties have grown too large. |
MaxTransferHopCountExceeded |
A message was forwarded across more than four entities. |
Your code puts a message there with DeadLetterMessageAsync(message, reason, description). You choose the reason yourself, as a short,
fixed code like InvalidJson, so that you can filter by it in the tool and in queries. The description explains the individual case.