KubeMQ
Client SDKsC#How-to guidesQueues

Ack Range

Acknowledge a range of KubeMQ queue messages by sequence number using the C# SDK.

Overview

A single poll response often bundles several messages into one batch, but "successfully processed" rarely applies to all of them uniformly — one handler might fail while its siblings succeed. Settling the whole batch together forces an all-or-nothing outcome: either you redeliver work you already finished, or you silently drop work you didn't. Per-message settlement lets each message's outcome reflect what actually happened to it, instead of the worst result in the batch.

Each message returned by PollAsync can be settled independently by calling msg.AckAsync() on it. Calling it on only some messages in the batch settles just those; the rest are left untouched — still pending, still redeliverable — until they're explicitly acked, nacked, or the visibility timeout expires.

Gotchas: messages you never touch aren't automatically fine — once the visibility timeout elapses, anything left unsettled goes back to the queue for redelivery, so a handler that forgets to call AckAsync() isn't "done," it's "will retry." Selective settlement only works when AutoAck is set to false on the poll request; with auto-ack on, the broker settles the entire batch the moment it's delivered, before your code runs. And there's no bulk "ack everything except these" call — you're responsible for tracking which messages you've already settled.

Prerequisites

  • KubeMQ server running on localhost:50000
  • C# SDK installed (dotnet add package KubeMQ.SDK.CSharp)

Code

Program.cs
// KubeMQ .NET SDK — QueuesStream: Ack Range (Per-Message)
//
// This example demonstrates acknowledging specific messages individually
// using per-message AckAsync() via the downstream receiver API.
//
// 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-range-client",
});
await client.ConnectAsync();

Console.WriteLine("Connected to KubeMQ server");

for (var i = 1; i <= 5; i++)
{
    await client.SendQueueMessageAsync(new QueueMessage
    {
        Channel = "csharp-queues.ack-range",
        Body = Encoding.UTF8.GetBytes($"Message #{i}")
    });
}

Console.WriteLine("Sent 5 messages");

await using var receiver = await client.CreateQueueDownstreamReceiverAsync();

var batch = await receiver.PollAsync(new QueuePollRequest
{
    Channel = "csharp-queues.ack-range",
    MaxMessages = 5,
    WaitTimeoutSeconds = 10,
    AutoAck = false,
});

Console.WriteLine($"Received {batch.Messages.Count} messages");

foreach (var msg in batch.Messages)
{
    var body = Encoding.UTF8.GetString(msg.Body.Span);
    if (body.Contains("#1") || body.Contains("#3"))
    {
        await msg.AckAsync();
        Console.WriteLine($"Acked: {body}");
    }
    else
    {
        Console.WriteLine($"Skipped: {body} (stays in queue)");
    }
}

Console.WriteLine("Selective ack completed. Remaining messages stay in queue.");
Console.WriteLine("Done.");

How It Works

  • PollAsync with AutoAck = false puts each message into a "locked" state visible only to this receiver. Messages remain in the queue until explicitly settled within the visibility timeout.
  • msg.AckAsync() removes the message permanently. Messages not acked (or nacked/requeued) are returned to the queue after the visibility timeout expires.
  • This example selectively acks messages containing #1 and #3 and leaves the rest — those unacknowledged messages will reappear for the next consumer.
  • Use this pattern for idempotent retry scenarios where only successfully processed items should be removed.

Was this page helpful?

On this page