Delayed Messages
Send KubeMQ queue messages with a delivery delay using the C# SDK so they arrive later.
Which to use
This is the task-oriented guide for sending delayed messages — send one with a delivery delay, confirm it's hidden, then receive it once the delay expires. For the QueueMessage.DelaySeconds property reference and its edge cases, see Delay Policy.
Overview
A delivery delay holds a queue message out of consumers' reach for a fixed window after it's sent — the message is accepted and persisted immediately, but invisible to pollers until the delay expires. It's the building block for scheduled work — a reminder to fire in an hour, a retry with back-off, a task queued for off-peak processing — without standing up a separate scheduler or cron service.
Set it with DelaySeconds on the QueueMessage before sending; the broker does the waiting. A poll via PollAsync against the channel before the delay elapses simply returns no messages — it isn't hidden in a separate place, it's the same queue, just not yet eligible for delivery. Once the delay window passes, the next poll retrieves it normally for acknowledgment.
Gotchas: the delay is set once at send time, per message, and can't be extended or shortened afterward — if you need a different wait, send a new message. A long delay still counts as an in-flight, persisted message, so it survives a broker restart, but it also occupies queue storage for the whole waiting period. Don't confuse this with a visibility timeout after delivery — that's a separate mechanism for redelivery on failed acknowledgment, not initial availability.
Prerequisites
- KubeMQ server running on
localhost:50000 - C# SDK installed (
dotnet add package KubeMQ.SDK.CSharp)
Code
// KubeMQ .NET SDK — Queues: Delayed Messages
//
// This example demonstrates sending messages with a delivery delay.
// The message becomes visible to consumers only after the delay expires.
// 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-delayed-messages-client",
});
await client.ConnectAsync();
Console.WriteLine("Connected to KubeMQ server");
// Send a message with 5-second delay
var sendResult = await client.SendQueueMessageAsync(new QueueMessage
{
Channel = "csharp-queues.delayed-messages",
Body = Encoding.UTF8.GetBytes("Delayed notification"),
DelaySeconds = 5
});
Console.WriteLine($"Sent delayed message (5s delay): {sendResult.MessageId}");
// Create one receiver and reuse it for both polls
await using var receiver = await client.CreateQueueDownstreamReceiverAsync();
// Immediate receive — should not find the message yet
var immediateBatch = await receiver.PollAsync(new QueuePollRequest
{
Channel = "csharp-queues.delayed-messages",
MaxMessages = 1,
WaitTimeoutSeconds = 1,
AutoAck = false,
});
Console.WriteLine($"Immediate receive: {(immediateBatch.HasMessages ? "found" : "empty (expected)")}");
if (immediateBatch.HasMessages)
{
await immediateBatch.AckAllAsync();
}
// Wait for delay to expire, then receive again
Console.WriteLine("Waiting 6 seconds for delay to expire...");
await Task.Delay(6000);
var delayedBatch = await receiver.PollAsync(new QueuePollRequest
{
Channel = "csharp-queues.delayed-messages",
MaxMessages = 1,
WaitTimeoutSeconds = 5,
AutoAck = false,
});
if (delayedBatch.HasMessages)
{
foreach (var msg in delayedBatch.Messages)
{
Console.WriteLine($"Delayed receive: {Encoding.UTF8.GetString(msg.Body.Span)}");
await msg.AckAsync();
}
}
Console.WriteLine("Done.");
How It Works
DelaySeconds = 5sets a broker-side visibility delay. The message is accepted and persisted immediately but hidden from consumers until the delay expires.- The immediate
PollAsyncwithWaitTimeoutSeconds = 1returns empty — the message is not yet visible. - After
Task.Delay(6000)the delay window has passed, and the secondPollAsyncfinds and delivers the message for manual acknowledgment. - This pattern is useful for retry backoff (send with increasing delay on each failure) or scheduled job delivery without an external scheduler.
Related
- Delay Policy — field-level reference for
DelaySeconds - Pattern overview
- C# SDK Reference
- Send & Receive
- Ack All
Was this page helpful?