SoFunction
Updated on 2025-04-05

7 solutions for Java to automatically cancel order timeout

Preface

In e-commerce, takeaway, ticketing and other systems, automatic cancellation of orders that are not paid without paying is a common requirement.

This function looks simple at first glance, and many beginners may even think: "Isn't it just adding a timer?" But in actual work, the complexity of details will often exceed expectations.

Here we gradually analyze various implementation solutions from basic to advanced, and finally share some common optimization techniques in production, hoping that they will be helpful to you.

1. Use DelayQueue

Applicable scenarios: The order quantity is small and the system concurrency is not high.

Delay queue is a data structure in Java concurrency package(), which is specifically used to handle delay tasks.

When the order is created, it is placed in the delay queue and the timeout time is set.

After the delay time has arrived, the queue will trigger the consumption logic and perform a cancel operation.

Sample code:

import .*;
 
public class OrderCancelService {
    private static final DelayQueue<OrderTask> delayQueue = new DelayQueue<>();
 
    public static void main(String[] args) throws InterruptedException {
        // Start the consumer thread        new Thread(() -> {
            while (true) {
                try {
                    OrderTask task = (); // Get expired tasks                    ("Cancel order:" + ());
                } catch (InterruptedException e) {
                    ().interrupt();
                }
            }
        }).start();
 
        // Simulate order creation        for (int i = 1; i <= 5; i++) {
            (new OrderTask(i, () + 5000)); // Cancel in 5 seconds            ("Order" + i + "Created");
        }
    }
 
    static class OrderTask implements Delayed {
        private final long expireTime;
        private final int orderId;
 
        public OrderTask(int orderId, long expireTime) {
             = orderId;
             = expireTime;
        }
 
        public int getOrderId() {
            return orderId;
        }
 
        @Override
        public long getDelay(TimeUnit unit) {
            return (expireTime - (), );
        }
 
        @Override
        public int compareTo(Delayed o) {
            return (, ((OrderTask) o).expireTime);
        }
    }
}

advantage:

  • Simple implementation and clear logic.

shortcoming:

  • Relying on memory, system restart will lose tasks.

  • As order volume increases, memory usage will increase significantly.

2. Database-based polling

Applicable scenarios:There are many orders, but the system does not require high real-time.

Polling is the easiest way to think of: scan the database regularly and update the timed out order status to "Canceled".

Sample code:

public void cancelExpiredOrders() {
    String sql = "UPDATE orders SET status = 'CANCELLED' WHERE status = 'PENDING' AND create_time < ?";
    try (Connection conn = ();
         PreparedStatement ps = (sql)) {
        (1, new Timestamp(() - 30 * 60 * 1000)); // 30 minutes unpaid cancellation        int affectedRows = ();
        ("Cancel order quantity:" + affectedRows);
    } catch (SQLException e) {
        ();
    }
}

advantage:

  • The data is highly reliable and does not depend on memory.

  • Low implementation cost without the need to introduce third-party components.

shortcoming:

  • Frequent scanning of databases will bring greater performance overhead.

  • Poor real-time performance (usually timed task intervals are minute levels).

Optimization suggestions:

  • Add indexes to relevant fields to avoid full table scanning.

  • Combined with the strategy of dividing tables and storing databases, reduce the pressure on a single table.

3. Based on Redis queue

Applicable scenarios:Suitable for small and medium-sized projects that require real-time performance.

Redis's List or Sorted Set data structures are ideal for use as delay task queues.

We can save the order timeout as Score and the order ID as Value into the ZSet of Redis, and regularly withdraw the expired order and cancel it.

example:

public void addOrderToQueue(String orderId, long expireTime) {
    ("order_delay_queue", expireTime, orderId);
}
 
public void processExpiredOrders() {
    long now = ();
    Set<String> expiredOrders = ("order_delay_queue", 0, now);
    for (String orderId : expiredOrders) {
        ("Cancel order:" + orderId);
        ("order_delay_queue", orderId); // Delete processed orders    }
}

advantage:

  1. High real-time performance.

  2. Redis has excellent performance and low latency.

shortcoming:

  1. Redis has limited capacity and is suitable for small and medium-sized tasks.

  2. Additional handling of Redis downtime or data loss is required.

4. Redis Key Expired Callback

Applicable scenarios:It requires high real-time real-time performance of timeout events, and it is hoped to rely on the characteristics of Redis itself to achieve simple task scheduling.

Redis provides the expiration function of Key, combined withkeyeventEvent notification mechanism can realize the automatic cancellation logic of orders.

When the order sets the timeout time, Redis will send a notification when the Key expires. We only need to subscribe to this event and process it accordingly.

example:

  1. Set the expiration date of the order:

public void setOrderWithExpiration(String orderId, long expireSeconds) {
    ("order:" + orderId, expireSeconds, "PENDING");
}
  • Subscribe to Redis's expiration events:

public void subscribeToExpirationEvents() {
    Jedis jedis = new Jedis("localhost");
    (new JedisPubSub() {
        @Override
        public void onPMessage(String pattern, String channel, String message) {
            if (("__keyevent@0__:expired")) {
                ("Expiration event received, cancel the order:" + message);
                // Execute the business logic of canceling orders            }
        }
    }, "__keyevent@0__:expired"); // Subscribe to expired events}

advantage:

  1. Simple implementation, directly utilize Redis's expiration mechanism.

  2. High real-time performance, and respond immediately after an expired event is triggered.

shortcoming:

  1. Relying on Redis's event notification function, it needs to be enablednotify-keyspace-eventsConfiguration.

  2. If expired keys are used extensively in Redis, performance issues may occur.

Notes:To use the Key expiration event, you need to ensure that the Redis configuration file is innotify-keyspace-eventsThe value of   containsEx. for example:

notify-keyspace-events Ex

5. Based on message queues (such as RabbitMQ)

Applicable scenarios:High concurrency system, high real-time requirements.

When an order is created, the order message is sent to the delay queue (such as RabbitMQ'sx-delayed-messagePlugin).

After the delay time has arrived, the message will be re-delivered to the consumer, and the consumer performs a cancellation operation.

Sample code (taking RabbitMQ as an example):

public void sendOrderToDelayQueue(String orderId, long delay) {
    Map<String, Object> args = new HashMap<>();
    ("x-delayed-type", "direct");
    ConnectionFactory factory = new ConnectionFactory();
    try (Connection connection = ();
         Channel channel = ()) {
        ("delayed_exchange", "x-delayed-message", true, false, args);
        ("delay_queue", true, false, false, null);
        ("delay_queue", "delayed_exchange", "");
 
         props = new ()
                .headers(("x-delay", delay)) // Delay time                .build();
        ("delayed_exchange", "", props, ());
    } catch (Exception e) {
        ();
    }
}

advantage:

  1. Message queues support distributed and perform well under high concurrency.

  2. High data reliability and no news loss is easy.

shortcoming:

  1. Introducing message queues increases system complexity.

  2. The problem of queue pileup needs to be dealt with.

6. Use the timed task framework

Applicable scenarios:Order cancellation operations are complex and require distributed support.

Timed task frameworks, such as Quartz and Elastic-Job, can efficiently manage task scheduling and are suitable for processing batch tasks.

For example, Quartz can execute order cancellation logic regularly by configuring Cron expressions.

Sample code:

@Scheduled(cron = "0 */5 * * * ?")
public void scanAndCancelOrders() {
    ("Start scanning and cancel expired orders");
    // Here we call the database update logic}

advantage:

  1. A mature scheduling framework supports complex task scheduling.

  2. High flexibility and supports distributed scaling.

shortcoming:

  1. Limited support for real-time.

  2. The framework itself is more complex.

7. Based on triggered event stream processing

Applicable scenarios:It is necessary to deal with order cancellations with high real-time performance, and combine complex business logic, such as dynamically adjusting the timeout time according to user behavior.

You can use event stream processing frameworks such as Apache Flink or Spark Streaming to process order status in real time and trigger timeout events.

After each order is generated, it can be used as part of the event stream, and the timeout cancel logic is triggered by the stream calculation when the order is not paid.

Sample code (taking Apache Flink as an example):

DataStream<OrderEvent> orderStream = (orderEvents);
 
orderStream
    .keyBy(OrderEvent::getOrderId)
    .process(new KeyedProcessFunction<String, OrderEvent, Void>() {
        @Override
        public void processElement(OrderEvent event, Context ctx, Collector<Void> out) throws Exception {
            // Register a timer            ().registerProcessingTimeTimer(() + 30000); // 30 seconds timeout        }
 
        @Override
        public void onTimer(long timestamp, OnTimerContext ctx, Collector<Void> out) throws Exception {
            // Timer triggers, executes order cancellation logic            ("Order timeout cancellation, order ID:" + ());
        }
    });

advantage:

  1. High real-time performance and supports complex event processing logic.

  2. Suitable for dynamic adjustment of timeout time to meet flexible business needs.

shortcoming:

  1. A flow computing framework was introduced, and the system complexity was increased.

  2. High requirements for operation and maintenance.

Summarize

Each solution has its own applicable scenario. When choosing, remember to consider it in combination with business needs, order quantity, and concurrent quantity.

If your project is small, you can use delay queues or Redis directly; in large high-concurrency systems, message queues and event stream processing are often the first choice.

Of course, code implementation is just the first step, and more importantly, performance tuning is performed during actual deployment and operation to ensure the stability of the system.

The above is the detailed content of 7 solutions for Java to automatically cancel order timeout. For more information about Java order timeout, please pay attention to my other related articles!