The Order Timeout Question That Fails Most Backend Interviews
A couple of days ago, a friend who has been doing Java for 3 years came to me for an interview review.
The second-round questions weren't particularly obscure; you could even say they were very common:
After a user places an order, if they don't pay within 30 minutes, how does the system automatically cancel the order?
He barely paused and answered directly:
Write a scheduled task that scans the order table every minute and changes unpaid orders that have timed out to 'cancelled'.
In a small system, this answer isn't really off the mark.
But the interviewer followed up with three questions, and he was immediately stuck:
- If there are tens of millions of unpaid orders, you're going to scan the database every minute?
- Scanning every minute means an order might be cancelled at 30 minutes and 59 seconds. Is this margin of error acceptable for the business?
- If the machine running the scheduled task goes down, what happens to the missed orders?
Many people fail this question not because they don't know how to do it, but because they only answered how to "make it run," not how to "make it run stably."
1. Why "Scheduled Task Scanning the Database" Is Not a Good Answer
The problem with this solution isn't that it's unusable, but that it becomes very strained as the scale increases.
So what this type of question really wants you to express is:
Don't make the system constantly go to the database to "find" which orders have expired; instead, let the "expired orders" enter the processing pipeline themselves.
This is a typical Delayed Task Design.
2. Many People Think of Redis Expiration Listening, but It's Not Suitable as the Main Solution
Some candidates will say:
When placing an order, write a Key to Redis with a TTL of 30 minutes. After the Key expires, listen for the event and then cancel the order.
This idea sounds logical, but it's best not to treat it as the core solution in an interview.
The reason is simple:
So, you can mention Redis expiration listening to show you know this path, but it's best to point out casually:
This solution is simple but not reliable enough, making it unsuitable as the main pipeline for order timeout cancellation.
3. A More Stable Approach: Redis ZSet as a Delay Queue
In this question, a more conventional and easier-to-elaborate answer is Redis ZSet.
The idea is not complicated:
scorestores the order timeout timememberstores the order ID- The consumer retrieves expired orders based on the current time
When Producing Data
ZADD order_delay_queue 1737558000 order_10086
Here, 1737558000 is the timestamp of "current time + 30 minutes".
When Consuming Data
ZRANGEBYSCORE order_delay_queue 0 current_timestamp LIMIT 0 100
What is retrieved are the orders that have already expired.
4. Why This Solution Is Better Than Scanning the Database
It has two core advantages:
First, it doesn't scan the order table. The database doesn't need to repeatedly check "which data has timed out"; the query pressure is shifted from MySQL to Redis.
Second, it only processes expired tasks. Instead of pulling out a massive number of orders to filter through, it directly processes the small batch that should be cancelled.
5. What Really Sets You Apart Is Not ZSet, but How You Handle "Task Loss"
Interviewers usually don't stop at the "use ZSet" step.
The more critical question is:
If you take an order out of the ZSet, but the service crashes before the business logic finishes executing, what happens to this order?
At this point, if you can only say "just retry it," that's basically not enough.
A more robust design is to implement two-phase processing.
flowchart TD
A[User places order] --> B[Write to Redis ZSet<br/>score=timeout time]
B --> C[Consumer scans expired tasks]
C --> D[Lua script atomically transfers to processing_queue]
D --> E[Execute order cancellation logic]
E --> F{Cancellation successful?}
F -- Yes --> G[Delete from processing_queue]
F -- No --> H[Keep task, wait for compensation retry]
The two most important points here are:
1. Don't Directly "Take and Delete"
A safer approach is:
- First, atomically transfer from
delay_queuetoprocessing_queue - After the business logic executes successfully, delete from
processing_queue - If the service crashes midway, a compensation thread can still retrieve the task from
processing_queue
This way, tasks won't be directly lost due to service exceptions.
2. The Business Interface Must Be Idempotent
Even if a task is executed repeatedly, it cannot be cancelled repeatedly.
For example, when cancelling an order, the SQL should include a status condition:
UPDATE orders
SET status='CANCELED'
WHERE order_id = ?
AND status='WAIT_PAY';
Only orders that are "pending payment" are allowed to be cancelled.
If the order has already been paid or cancelled, this SQL will no longer take effect.
This is a very high-scoring statement in an interview:
Delayed tasks can be delivered repeatedly, but the business result must be idempotent.
6. If the Order Volume Reaches Tens of Millions, How to Proceed Further?
If the data scale continues to grow, a single ZSet will gradually become a big Key.
At this point, you can't just say "I use Redis"; you also need to add sharding.
Sharding Idea
Hash based on the order ID, distributing tasks into multiple queues:
The routing rule looks something like this:
queueIndex = hash(orderId) % 16
The benefits of this are very direct:
- A single ZSet won't inflate indefinitely
- Enables multi-threaded, multi-instance parallel consumption
- If one shard has a backlog, it won't drag down all tasks
Of course, when multiple nodes consume concurrently, you must still guarantee one thing:
Task claiming must be atomic, and order cancellation must be idempotent.
If either of these is missing, the solution is not complete.
7. If the Interviewer Continues to Probe, You Can Answer at a Higher Level
For even larger scenarios, you can elevate the solution one more level:
You don't need to go into great depth here, but you can casually add:
If the company already has a mature MQ system, I would prioritize evaluating its delayed message capability; if pursuing higher scheduling efficiency, I might consider a combination of "persistent queue + time wheel."
The purpose of this sentence is not to show off, but to tell the interviewer:
You know this is not a "memorize Redis" question, but a "delayed task system design" question.
8. How to Answer Smoothly in an Interview?
If you want to answer this question more completely, you can follow this sequence:
Order timeout cancellation is essentially a delayed task problem. For small systems, you can use a scheduled task to scan the database, but in high-concurrency scenarios, this solution will have issues with high database pressure, unstable timeliness, and poor task fault tolerance.
I prefer using Redis ZSet as a delay queue. When placing an order, write the order ID into the ZSet, with the score storing the expiration time 30 minutes later. A background thread pulls expired orders based on the current time and triggers the cancellation logic.
To avoid concurrent duplicate consumption, I would use a Lua script to ensure the atomicity of task claiming. To prevent task loss if the service crashes after a task is taken, I would introduce a processing queue, where tasks are ACKed and deleted only after successful processing, and failed tasks go through compensation retries.
Additionally, the order cancellation interface itself must have idempotency control, ensuring an order can only change from pending payment to cancelled, preventing dirty data from repeated execution.
If the data volume continues to increase, I would shard the delay queue and, if necessary, evaluate MQ delayed message solutions. Finally, I would keep a low-frequency fallback scanning task to ensure eventual data consistency in extreme cases.
This set of answers basically already surpasses the level of just "knowing how to do it."
Final Thoughts
On the surface, this question is about "cancelling an order after 30 minutes."
But what the interviewer really wants to see are a few other things:
- Do you have a sense of scale?
- Do you have a sense of reliability?
- Do you know that tasks can be lost, duplicated, or backlogged?
- Do you know to leave a safety net for the system?
Many people don't lack the ability to write features; they just habitually only talk about the "happy path."
Yet system design questions value the failure path the most.
So, the next time you're asked about "automatic order timeout cancellation," don't rush to answer "scheduled task."
First, bring up the keywords delayed task, atomicity, idempotency, compensation mechanism, eventual consistency, and your answer will stand firm.
Top 1 from juejin.cn, machine-translated. The original thread is authoritative.
Write a scheduled task that scans the order table once a minute and changes unpaid orders that have timed out to canceled. Can you come up with a more sophisticated copy? This one is so overused.