Ack & Reject
Selectively acknowledge or reject individual KubeMQ queue messages using the C# SDK.
Overview
Ack and reject give you per-message control over queue delivery instead of an all-or-nothing batch outcome. When PollAsync fetches a batch with AutoAck = false, each message stays locked on the broker — invisible to other consumers — until the consumer explicitly settles it. That's what you need when one bad record in a batch shouldn't take the rest down with it.
Settlement happens through calls on the received message: AckAsync(), which permanently removes it from the queue, and NackAsync(), which returns it to the queue immediately for another consumer. A third option, ReQueueAsync(), also returns the message but increments its receive count — pair that with MaxReceiveCount and MaxReceiveQueue for dead-letter routing after repeated failures.
Gotchas: an unsettled message isn't gone — it snaps back to the queue once the visibility timeout expires, so a slow consumer looks identical to a rejecting one; settle every message before that deadline, and never assume a batch is fully processed until you've called AckAsync(), NackAsync(), or ReQueueAsync() on each one individually.
Prerequisites
- KubeMQ server running on
localhost:50000 - C# SDK installed (
dotnet add package KubeMQ.SDK.CSharp)
Code
// KubeMQ .NET SDK — Queues: Acknowledge, Nack, and Requeue
//
// This example demonstrates different message settlement options:
// - AckAsync(): Successfully processed, remove from queue
// - NackAsync(): Processing failed, reject message
// - ReQueueAsync(): Return message to queue for retry
//
// Uses QueueDownstreamReceiver.PollAsync for transactional message settlement.
//
// Prerequisites:
// - KubeMQ server running on localhost:50000
// - dotnet run
using KubeMQ.Sdk.Client;
using KubeMQ.Sdk.Queues;
using System.Text;
await using var client = new KubeMQClient(new KubeMQClientOptions
{
ClientId = "csharp-queues-ack-reject-client",
});
await client.ConnectAsync();
Console.WriteLine("Connected to KubeMQ server");
// Send test messages
for (var i = 1; i <= 3; i++)
{
await client.SendQueueMessageAsync(new QueueMessage
{
Channel = "csharp-queues.ack-reject",
Body = Encoding.UTF8.GetBytes($"Message #{i}")
});
}
Console.WriteLine("Sent 3 messages");
// Receive via downstream receiver (supports manual settlement)
await using var receiver = await client.CreateQueueDownstreamReceiverAsync();
var batch = await receiver.PollAsync(new QueuePollRequest
{
Channel = "csharp-queues.ack-reject",
MaxMessages = 3,
WaitTimeoutSeconds = 10,
AutoAck = false,
});
foreach (var msg in batch.Messages)
{
var body = Encoding.UTF8.GetString(msg.Body.Span);
Console.WriteLine($"Processing: {body}");
if (body.Contains("#1"))
{
await msg.AckAsync();
Console.WriteLine(" -> Acknowledged (success)");
}
else if (body.Contains("#2"))
{
await msg.NackAsync();
Console.WriteLine(" -> Nacked (rejected)");
}
else
{
await msg.ReQueueAsync();
Console.WriteLine(" -> Requeued (will retry)");
}
}
Console.WriteLine("Done.");
How It Works
PollAsyncwithAutoAck = falselocks the batch for this receiver. Each message must be explicitly settled or it returns to the queue after the visibility timeout.AckAsync()removes the message permanently — use after successful processing.NackAsync()rejects the message and returns it to the queue immediately for another consumer or retry attempt.ReQueueAsync()also returns the message to the queue, which increments its receive count. Pair this withMaxReceiveCountandMaxReceiveQueueto implement dead-letter routing after a configurable number of failures.
Related
Was this page helpful?