Dead Letter Queue
Route failed KubeMQ queue messages to a dead-letter queue in Ruby, diverting poison messages after retries are exhausted.
Which to use
This page is the task-oriented walkthrough: send a message with DLQ routing configured, let it exhaust retries, and consume the diverted message. For the QueueMessagePolicy field reference — max_receive_count/max_receive_queue defaults and edge cases — see Dead Letter Policy.
Overview
A dead-letter queue (DLQ) gives a poison message somewhere to go instead of looping through consumers forever. When a message keeps failing — a malformed payload, a downstream outage, a handler bug — retrying it forever wastes consumer cycles and blocks everything behind it. A DLQ takes that decision out of your hands: past a set number of failed attempts, the broker diverts the message to a separate channel instead of retrying it again.
Routing runs on two settings inside QueueMessagePolicy: max_receive_count and max_receive_queue. Every failed delivery — a nack_all, a reject, or an expired visibility window — increments the receive count; past the threshold, the broker reroutes the message to the DLQ instead of redelivering it. The DLQ itself is an ordinary queue, consumed like any other channel.
Gotchas: the DLQ doesn't drain itself — a dedicated consumer must watch it. The count increments on any failed delivery, not just deliberate rejections — a slow consumer that lets the visibility window lapse counts the same as an explicit nack. A typo in the DLQ channel name quietly creates an unrelated channel instead of failing loudly.
Prerequisites
- KubeMQ server running on
localhost:50000 - Ruby SDK installed (
gem install kubemq)
Code
require 'kubemq'
address = ENV.fetch('KUBEMQ_ADDRESS', 'localhost:50000')
channel = 'ruby-queues.dead-letter-queue'
dlq_channel = 'ruby-queues.dead-letter-queue-dlq'
begin
client = KubeMQ::QueuesClient.new(address: address, client_id: 'qstream-dlq-example')
puts "Connected to #{address}"
policy = KubeMQ::Queues::QueueMessagePolicy.new(max_receive_count: 2, max_receive_queue: dlq_channel)
msg = KubeMQ::Queues::QueueMessage.new(
channel: channel,
metadata: 'fragile-message',
body: 'may-fail',
policy: policy
)
client.send_queue_message(msg)
puts "Sent message with max_receive_count=2, DLQ=#{dlq_channel}"
receiver = client.create_downstream_receiver
2.times do |attempt|
request = KubeMQ::Queues::QueuePollRequest.new(channel: channel, max_items: 1, wait_timeout: 3)
response = receiver.poll(request)
puts "Attempt #{attempt + 1}: #{response.messages.size} messages"
response.nack_all if response.messages.any?
end
sleep 1
dlq_request = KubeMQ::Queues::QueuePollRequest.new(channel: dlq_channel, max_items: 1, wait_timeout: 3)
dlq_response = receiver.poll(dlq_request)
puts "DLQ messages: #{dlq_response.messages.size}"
dlq_response.messages.each do |m|
puts " DLQ: #{m.metadata}"
m.ack
end
rescue KubeMQ::Error => e
puts "KubeMQ error: #{e.message}"
ensure
receiver&.close
client&.close
puts 'Done'
endHow It Works
QueueMessagePolicywithmax_receive_countandmax_receive_queueconfigures automatic dead-letter routing.- After exceeding the max receive count, the broker moves the message to the dead-letter queue.
- Review timeouts, channel names, and client IDs before running against shared environments.
Related
Was this page helpful?