KubeMQ
Client SDKsJavaHow-to guidesQueues

Delayed Messages

Send KubeMQ Queue messages with a delivery delay using the Java SDK so consumers receive them after a set interval.

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 delayInSeconds builder option 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 .delayInSeconds(...) on the QueueMessage builder before sending; the broker does the waiting. A poll against the channel before the delay elapses (via receiveQueueMessages) simply returns zero 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.

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
  • Java SDK installed (implementation 'io.kubemq.sdk:kubemq-sdk-Java:3.1.1' (Gradle) or Maven dependency from Getting Started)

Code

DelayedMessagesExample.java
package io.kubemq.example.queues;

import io.kubemq.sdk.queues.*;
import java.util.UUID;

/**
 * Delayed Messages Example
 *
 * Demonstrates sending messages with a delay before they become available for consumption.
 */
public class DelayedMessagesExample {
    private static final String ADDRESS = "localhost:50000";
    private static final String CLIENT_ID = "java-queues-delayed-messages-client";
    private static final String CHANNEL = "java-queues.delayed-messages";

    public static void main(String[] args) throws InterruptedException {
        // Create a queues client connected to the KubeMQ server
        try (QueuesClient client = QueuesClient.builder().address(ADDRESS).clientId(CLIENT_ID).build()) {
            // Create the queue channel
            client.createQueuesChannel(CHANNEL);

            // Send a message with delay (not available for consumption until delay expires)
            int delaySeconds = 3;
            client.sendQueueMessage(QueueMessage.builder()
                    .id(UUID.randomUUID().toString()).channel(CHANNEL)
                    .body(("Delayed by " + delaySeconds + "s").getBytes())
                    .delayInSeconds(delaySeconds).build());
            System.out.println("Sent message with " + delaySeconds + "s delay.");

            // Try to receive immediately (message not yet available)
            System.out.println("Trying to receive immediately...");
            QueuesPollResponse resp1 = client.receiveQueueMessages(QueuesPollRequest.builder()
                    .channel(CHANNEL).pollMaxMessages(1).pollWaitTimeoutInSeconds(1).autoAckMessages(true).build());
            System.out.println("Messages available: " + resp1.getMessages().size() + " (expected 0)");

            // Wait for the delay to expire
            System.out.println("Waiting for delay to expire...");
            Thread.sleep((delaySeconds + 1) * 1000);

            // Receive the message after delay has expired
            QueuesPollResponse resp2 = client.receiveQueueMessages(QueuesPollRequest.builder()
                    .channel(CHANNEL).pollMaxMessages(1).pollWaitTimeoutInSeconds(2).autoAckMessages(true).build());
            if (!resp2.getMessages().isEmpty()) {
                System.out.println("Received after delay: " + new String(resp2.getMessages().get(0).getBody()));
            }

            // Clean up resources
            client.deleteQueuesChannel(CHANNEL);
        }
    }
}

How It Works

  • delayInSeconds(3) on the QueueMessage tells the broker to hold the message for 3 seconds before making it available for consumers.
  • The first poll (1-second timeout) returns empty because the delay has not expired; the second poll after Thread.sleep((delaySeconds + 1) * 1000) finds the message available.
  • The delay is set per-message at publish time; different messages on the same channel can have different delays.

Was this page helpful?

On this page