Expiration Policy
Set message expiration and TTL on KubeMQ stream queues in Node.js so stale messages are dropped before they are ever delivered.
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, policy: { expirationSeconds: 30 } attaches a per-message TTL when you build the message, and the clock starts the moment the broker accepts it via sendQueueMessage, 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 - Node.js SDK installed (
npm install kubemq-js)
Code
import { KubeMQClient, createQueueMessage } from 'kubemq-js';
async function main() {
const client = await KubeMQClient.create({
address: 'localhost:50000',
clientId: 'js-queues-stream-expiration-policy-client',
});
try {
const result = await client.sendQueueMessage(
createQueueMessage({
channel: 'js-queues-stream.expiration-policy',
body: 'this message expires in 30 seconds',
policy: { expirationSeconds: 30 },
}),
);
console.log('Sent message ID:', result.messageId);
const expiresAt =
result.expirationAt instanceof Date && !isNaN(result.expirationAt.getTime())
? result.expirationAt.toISOString()
: 'N/A';
console.log('Expires at:', expiresAt);
} finally {
await client.close();
}
}
main().catch(console.error);
How It Works
policy: { expirationSeconds: 30 }sets a TTL on the message; if no consumer receives it within 30 seconds, the server automatically discards it.result.expirationAtis aDateconfirming the expiry deadline — theinstanceof Date && !isNaN(...)guard handles older server versions that may not set it.- Unlike dead-letter routing, expired messages are simply discarded — there is no DLQ involved unless you also set
maxReceiveQueue. - This pattern is useful for time-sensitive work (e.g., alerts, cache invalidation) where processing a stale message is worse than missing it.
Related
Was this page helpful?