Backend Development 17 min read

Design and Implementation of Delayed Task Processing for Order Systems

This article explains various approaches to delayed task handling—such as database polling, JDK DelayQueue, Redis expiration listeners, Redisson delay queues, RocketMQ delayed messages, and RabbitMQ dead‑letter queues—evaluating their advantages, drawbacks, and best‑practice recommendations for reliable order‑expiration workflows.

Java Captain
Java Captain
Java Captain
Design and Implementation of Delayed Task Processing for Order Systems

In everyday development we often encounter scenarios that require delayed processing, such as automatically cancelling an order after 30 minutes of non‑payment or sending a short message to a user 15 minutes after registration. These use cases are typical in e‑commerce and payment systems where an order must be closed if not paid within a certain time.

Database polling (scheduled task) – A low‑cost solution that periodically scans the database for expired orders and updates their status. Example code:

@Scheduled(cron = "0 0 22 * * ?")
public void pmNotify() {
    this.pmService.todoNotify();
}

Advantages: easy to implement, low cost, no external dependencies. Disadvantages: limited time precision due to fixed scan intervals and increased database load as order volume grows.

JDK DelayQueue – An unbounded in‑memory queue where elements implement Delayed . Orders are placed into the queue with a timeout (e.g., 30 minutes) and a dedicated thread continuously takes expired elements. Advantages: no third‑party components, no database required. Drawbacks: potential JVM OOM for large queues, loss of data on JVM restart, and a constantly running thread consumes CPU.

Redis expiration listener – By enabling notify-keyspace-events Ex in redis.conf , Redis can publish expiration events. A listener can be implemented by extending KeyspaceEventMessageListener :

public abstract class KeyspaceEventMessageListener implements MessageListener, InitializingBean, DisposableBean {
    private static final Topic TOPIC_ALL_KEYEVENTS = new PatternTopic("__keyevent@*");
    // ...
    public void init() {
        if (StringUtils.hasText(keyspaceNotificationsConfigParameter)) {
            RedisConnection connection = listenerContainer.getConnectionFactory().getConnection();
            try {
                Properties config = connection.getConfig("notify-keyspace-events");
                if (!StringUtils.hasText(config.getProperty("notify-keyspace-events"))) {
                    connection.setConfig("notify-keyspace-events", keyspaceNotificationsConfigParameter);
                }
            } finally {
                connection.close();
            }
        }
        doRegister(listenerContainer);
    }
    protected void doRegister(RedisMessageListenerContainer container) {
        listenerContainer.addMessageListener(this, TOPIC_ALL_KEYEVENTS);
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        init();
    }
}

Pros: high performance of Redis for setting and consuming keys. Cons: unbounded queue can cause JVM OOM, expiration events are not generated exactly at the timeout (they fire when Redis actually deletes the key), and prior to Redis 5.0 expiration messages were not persisted, leading to possible loss.

Redisson distributed delay queue – Redisson provides RDelayedQueue built on a Redis sorted set where the score is the delivery timestamp. A background scanner moves due items to a ready list. It guarantees no message loss as long as Redis stays up and is easy to use thanks to Lua scripts ensuring atomicity. The main drawback is the dependency on Redis.

RocketMQ delayed messages – Messages are stored in the broker with a delay (e.g., 30 minutes) and become consumable only after the delay expires. This decouples business logic and offers high throughput, but introduces the complexity of a heavyweight MQ component, including potential message loss and idempotency concerns.

RabbitMQ dead‑letter queue – By configuring a TTL on normal queues and routing expired messages to a dead‑letter exchange, RabbitMQ can emulate delayed delivery. The approach suffers from FIFO ordering issues (a long‑TTL message at the head blocks later messages) and adds system complexity. The official rabbitmq-delayed-message-exchange plugin is recommended for true delayed messages.

Best practice – For low‑traffic scenarios, database scanning is sufficient. In production, combine a scanning job with a middleware (e.g., Redisson) as a compensation mechanism to avoid loss. Concurrency should be handled with the “one lock, two checks, three updates” pattern, using optimistic‑lock SQL such as:

# Pay success
update pay_info set status = 'paySuccess' where orderNo = '1' and status = 'paying';
# Cancel
update pay_info set status = 'cancel' where orderNo = '1' and status = 'paying';

Monitoring is essential: separate system monitoring (performance, method availability) from business monitoring (process health, alerts). Examples include tracking TP99/TP999, AVG, MAX metrics and setting alerts when scheduled jobs miss their execution windows.

Edge cases like a payment completing exactly when an automatic cancellation triggers are resolved by checking the current status before updating, ensuring only one path succeeds. Distributed locks (e.g., Redis‑based) can also be used to serialize cancel and pay operations.

Overall, the article provides a comprehensive comparison of delayed‑task solutions, their trade‑offs, and practical guidelines for building reliable order‑expiration mechanisms.

distributed systemsRedisSpringMessage Queuedelayed tasksorder expiration
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.