# Delay queues and scheduled delivery

> A delay queue stores messages until a specified time, then makes them available for processing. It supports reminders, retry backoff, and timed actions without keeping a process asleep, but making a message eligible is not the same as guaranteeing execution at that exact moment.

Source: https://sysround.com/concepts/delay-queue
Updated: 2026-11-05


## How delay queues and scheduled delivery works

Store each scheduled item durably with an ID, a due_at timestamp, a payload or reference, and a version. For example, a reminder created at 09:00 with due_at = 09:15 must remain unavailable until its deadline, even if the scheduling service restarts.

Keep pending items ordered by due time. A database implementation can use an index on (state, due_at) to find pending rows whose deadline has passed; larger systems can partition schedules and group deadlines into time buckets. A memory heap or timing wheel can accelerate near-term scheduling, but durable storage must support recovery.

Dispatchers claim due items using atomic state changes or expiring leases, then publish them to a ready queue. Avoid marking an item dispatched before publishing it: a crash could lose the work. One option is to atomically record a dispatch outbox entry with the state change, then relay that entry to the queue.

Workers consume ready messages and acknowledge successful processing. A dispatcher or worker crash can cause redelivery, so use a stable occurrence ID for deduplication and make side effects idempotent where possible. Schedule retries with a new due time, bounded exponential backoff, and jitter rather than sleeping inside a worker.

Treat edits and cancellations as state changes, not just attempts to delete queue entries. Include the schedule version in each message and check authoritative state before acting. When cancellation must win against execution, define an atomic transition that decides which operation wins; a separate read followed by a send leaves a race.

Define timing semantics explicitly: due_at usually means eligible at or after that time, not guaranteed completion then. Use a defined clock source and account for clock skew, polling intervals, queue backlog, and worker capacity. Measure dispatch and execution lateness separately, and define whether overdue work runs, expires, or is coalesced after an outage.

## Worked example: Calendar reminders that survive edits and outages

Suppose a calendar service holds 2 million pending reminders and targets starting delivery within 5 seconds of each deadline under normal load. It stores reminder_id, event_version, due_at, and state, indexed by state and due_at. Every second, dispatchers claim batches of due rows and atomically create outbox entries. Relays publish those entries to a ready queue. If a meeting moves, the service increments its version and creates a replacement reminder; workers discard old-version messages. A burst of 30,000 reminders at 09:00 requires roughly 6,000 starts per second to meet the target, before allowing for scheduling overhead. After an outage, a defined policy skips reminders for meetings that have already ended.

## Trade-offs

- **Timing precision versus scheduling cost.** Frequent polling and fine-grained timer buckets reduce scheduling delay but increase database queries, wakeups, and coordination work.
- **Broker feature versus custom scheduler.** Native delayed delivery reduces implementation work, but supported delay ranges, cancellation, ordering, and durability guarantees vary by broker.
- **Durability versus write overhead.** Persisting schedules, claims, and dispatch records enables crash recovery but adds storage operations and cleanup.
- **Reliable delivery versus duplicate execution.** Retrying uncertain dispatches avoids silent loss but requires deduplication or idempotency at the side-effect boundary.
- **Cancellation versus execution races.** Version checks reject stale messages, but strict cancellation semantics require an authoritative decision about whether execution has already begun.
- **Burst capacity versus idle resources.** Many items sharing one deadline can overwhelm dispatchers or workers, so capacity planning must consider due-time bursts rather than only average arrival rates.

## When to use it in a system design interview

- Work must become eligible later: reminders, scheduled notifications, escalation deadlines, or deferred jobs.
- Failed operations need durable retry backoff without occupying worker threads or repeatedly cycling through a ready queue.
- A workflow needs a timeout action, such as releasing an unpaid reservation, with a fresh state check before changing anything.
- A crawler must enforce each host's next-allowed-fetch time while continuing to process other hosts.
- Schedules must survive restarts and support edits, cancellations, or recovery after downtime.

## How to bring it up in the interview

- Start with the timing contract: maximum scheduling horizon, acceptable lateness, whether early execution is forbidden, and what happens to overdue work.
- Propose the simplest durable design that fits: an indexed schedule table and polling dispatchers may be enough before introducing time buckets or specialized timer services.
- Walk through a crash between claiming, publishing, and recording completion; interviewers will probe whether work can disappear or be dispatched twice.
- Separate message delivery from side-effect correctness, and explain the idempotency key and cancellation-versus-execution rule.
- Estimate pending schedule count and peak items due per second, then discuss partitioning, worker backpressure, oldest overdue age, and recovery capacity.

## Common mistakes

- Treating due_at as a promise that a worker starts or finishes at that exact time.
- Using in-memory timers as the only record of scheduled work.
- Assuming message TTL schedules delivery; TTL commonly expires messages rather than making them available later.
- Publishing to a queue and updating the schedule database as unrelated writes without explaining the crash gap.
- Assuming deletion or a version check can retract an external action that is already in progress.

## Interview questions where it comes up

- [Design a distributed job scheduler](https://sysround.com/questions/design-job-scheduler): Durable due-time tracking, reliable dispatch, and recovery from missed deadlines form the core execution path.
- [Design Google Calendar](https://sysround.com/questions/design-google-calendar): Reminder delivery requires durable scheduling plus correct handling of event edits, cancellations, retries, and outages.
- [Design an on-call escalation system like PagerDuty](https://sysround.com/questions/design-pagerduty): Escalation deadlines must survive crashes and resolve races between timer execution and incident acknowledgement.
- [Design a notification system](https://sysround.com/questions/design-notification-system): Durable delayed retries keep provider failures from blocking workers or causing immediate retry storms.
- [Design a web crawler](https://sysround.com/questions/design-web-crawler): The URL frontier must schedule hosts by their next eligible fetch time to enforce politeness without blocking unrelated work.
- [Design a distributed message queue like RabbitMQ](https://sysround.com/questions/design-message-queue): Delayed retry eligibility and visibility-lease expiry require durable timing state and safe redelivery under failures.

## FAQ

### What is the difference between a delay queue and a regular message queue?

A regular work queue generally makes accepted messages immediately eligible for consumption. A delay queue adds a not-before time and withholds each message until then. Both still need delivery guarantees, acknowledgments, and failure recovery.

### How do you implement delayed message delivery?

Use a broker's delayed-delivery feature or persist schedules in a database indexed by due time and have dispatchers move due work into a ready queue. Make the database-to-queue handoff recoverable through an outbox or an equivalent protocol that safely retries uncertain publishes. Verify the required delay horizon, durability, cancellation behavior, and timing precision.

### How is a delay queue different from cron or a visibility timeout?

Cron defines recurring schedules; a scheduler can expand those schedules into individual occurrences delivered through a delay queue. A visibility timeout temporarily hides a message after a consumer receives it and allows redelivery if processing is not acknowledged. Neither mechanism alone guarantees exactly-once execution or exact start times.

## Related concepts

- [Message queues](https://sysround.com/concepts/message-queues)
- [Distributed cron and job scheduling](https://sysround.com/concepts/distributed-cron)
- [Retries, backoff and jitter](https://sysround.com/concepts/retries-backoff)
- [Idempotency and idempotency keys](https://sysround.com/concepts/idempotency)


---
SysRound (https://sysround.com) is an AI practice tool for system design interviews: a voice or typed AI interviewer, a shared whiteboard, then feedback on every skill and a model answer. Its feedback is AI-generated and can be wrong.
