Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Java DelayQueue: release retry work only after its deadline

Last updated: 5 Oct 20265 min read
tutorial
AdvancedBy AITrove Editorial

DelayQueue makes an element available to take only after its Delayed value reports a nonpositive remaining delay. Its capacity is unbounded unless the application adds admission control.

Operational contract

The ticket stores a monotonic nanoTime deadline and orders itself by that deadline. The scheduler rejects negative delays and accepts at most 47 seconds for this example. poll returns null when no ticket has expired, even if the queue contains future work. Distinct tickets sharing a deadline can be removed in either order; add a sequence field when tie order matters. The class itself has no backlog cap, so a service accepting untrusted retry volume needs a separate quota before scheduling.

Failure case

An order retry is due after 23 seconds. The worker calls poll after 8 seconds and receives no ticket. A missing result does not mean the queue is empty. When the deadline passes, the ticket becomes eligible; worker scheduling can still delay actual processing beyond the due time.

Java code

Java
import java.time.Duration;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

public class OrderRetryQueue {
    private final DelayQueue<RetryTicket> retries = new DelayQueue<>();

    public void schedule(String orderId, Duration delay) {
        if (delay.isNegative() || delay.compareTo(Duration.ofSeconds(47)) > 0)
            throw new IllegalArgumentException("Delay outside policy");
        retries.offer(new RetryTicket(orderId, System.nanoTime() + delay.toNanos()));
    }

    public String pollDue() {
        RetryTicket ticket = retries.poll();
        return ticket == null ? null : ticket.orderId;
    }

    private static final class RetryTicket implements Delayed {
        private final String orderId;
        private final long deadline;
        private RetryTicket(String orderId, long deadline) {
            this.orderId = orderId;
            this.deadline = deadline;
        }
        public long getDelay(TimeUnit unit) {
            return unit.convert(deadline - System.nanoTime(), TimeUnit.NANOSECONDS);
        }
        public int compareTo(Delayed other) {
            return Long.compare(deadline, ((RetryTicket) other).deadline);
        }
    }
}

Performance and ownership cost

Insertion and eligible-head removal use heap-like O(log N) work for N tickets; checking an ineligible head is cheap. Memory is O(N), with no built-in ceiling. The short 47-second horizon keeps signed nanoTime subtraction and ordering within a practical window, but does not cap queued volume.

Common Mistakes

  • Do not interpret poll returning null as an empty queue.
  • Do not accept unlimited retry tickets into an unbounded queue.
  • Do not use wall-clock time for elapsed-delay calculations.

Connected lessons

java
timed queues and handoffs
delayqueue-retry-expiry
Storage details