Expiration Policy
Set message expiration (TTL) on KubeMQ stream queues using the C# SDK to auto-remove stale messages.
Overview
An expiration policy puts a hard time limit on how long a queue message may sit unconsumed. It solves a different problem than a dead-letter policy — this isn't about messages that fail processing, it's about messages that go stale: a price quote, a one-time code, a cache-invalidation signal, where late delivery is actively wrong, not just delayed. Instead of every consumer re-checking timestamps itself, the deadline lives on the message and the broker enforces it.
At the API level, ExpirationSeconds = 30 attaches a per-message TTL when you build the QueueMessage, and the clock starts the moment the broker accepts it via SendQueueMessageAsync, not when a consumer picks it up. Let the TTL elapse unconsumed and the broker silently removes it — a later poll just comes back empty, no error, no trace.
Gotchas: expiration is silent — no DLQ routing, no event, just a message that vanishes — so pair it with monitoring if you need visibility into how much work is being dropped. The timer starts at send time, not when a consumer picks up the work, so a message can expire mid-backlog even while a consumer is actively polling. And setting the TTL too short for your real consumer lag just turns ordinary slowness into silent data loss.
Prerequisites
- KubeMQ server running on
localhost:50000 - C# SDK installed (
dotnet add package KubeMQ.SDK.CSharp)
Code
// KubeMQ .NET SDK — QueuesStream: Message Expiration Policy
//
// This example demonstrates sending a queue message with an expiration policy
// and receiving it via the downstream receiver API. Messages that are not consumed
// within the expiration window are automatically discarded.
//
// 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-expiration-policy-client",
});
await client.ConnectAsync();
Console.WriteLine("Connected to KubeMQ server");
var msg = new QueueMessage
{
Channel = "csharp-queues.expiration-policy",
Body = Encoding.UTF8.GetBytes("Expires in 30s"),
ExpirationSeconds = 30,
};
var sendResult = await client.SendQueueMessageAsync(msg);
Console.WriteLine($"Sent: {sendResult.MessageId}, IsError={sendResult.IsError}");
await using var receiver = await client.CreateQueueDownstreamReceiverAsync();
// Receive via downstream receiver before expiration
var batch = await receiver.PollAsync(new QueuePollRequest
{
Channel = "csharp-queues.expiration-policy",
MaxMessages = 1,
WaitTimeoutSeconds = 5,
AutoAck = false,
});
if (batch.HasMessages)
{
Console.WriteLine($"Received before expiration: {Encoding.UTF8.GetString(batch.Messages[0].Body.Span)}");
await batch.AckAllAsync();
}
else
{
Console.WriteLine("No messages received (may have expired).");
}
Console.WriteLine("Done.");
How It Works
ExpirationSeconds = 30sets a TTL on the message. If no consumer polls and acknowledges the message within 30 seconds of it being enqueued, the broker discards it silently.- The downstream receiver polls before the TTL window expires, so the message is found and acknowledged.
sendResult.IsErrorindicates whether the broker accepted the message. A false value confirms it was successfully stored.- Use expiration for time-sensitive work items (e.g., notifications, rate-limited jobs) where stale messages are worse than dropped messages.
Related
Was this page helpful?