How to Prevent Duplicate Order Submissions in Flash‑Sale Systems: An Idempotency Guide

The article explains the concept of idempotency, outlines common duplicate‑submission scenarios such as form resubmission, malicious voting, client retries, and message re‑consumption, and details three practical implementation methods—database constraints, token‑based middleware, and state‑machine logic—complete with code examples and trade‑off considerations.

Shepherd Advanced Notes
Shepherd Advanced Notes
Shepherd Advanced Notes
How to Prevent Duplicate Order Submissions in Flash‑Sale Systems: An Idempotency Guide

Idempotency Definition

Idempotency, originally a mathematical property (f(x)=f(f(x))), means that repeated execution of the same operation under the same conditions produces the same effect and result. In programming, an idempotent API returns the same outcome and does not change system state after the first successful call.

Typical Scenarios

Front‑end users repeatedly click a submit button when a network timeout hides the success response.

Malicious users submit the same vote or order multiple times.

HTTP clients automatically retry on timeout, causing duplicate requests.

Message‑queue consumers re‑process a message when acknowledgement fails.

Implementation Approaches

Three common techniques are described.

1. Database‑Level Guarantees

Unique Index

ALTER TABLE `mall`.`order`
ADD UNIQUE INDEX `uniq_order_no`(`order_no`) USING BTREE;

When a duplicate order_no is inserted, MySQL throws DuplicateKeyException (or MySQLIntegrityConstraintViolationException in Spring). The application catches the exception and returns an appropriate error.

Pessimistic Lock

SELECT * FROM table_xxx WHERE id='xxx' FOR UPDATE;

The SELECT … FOR UPDATE must be executed inside a transaction. It locks the selected row until the transaction commits, guaranteeing exclusive access but potentially degrading performance. The table must use the InnoDB engine and the locked column must be a primary key or unique index; otherwise the whole table could be locked.

Optimistic Lock

UPDATE amount = amount + 100, version = version + 1
WHERE user_id = 1 AND version = 1;

A version column is incremented on each update. The UPDATE succeeds only when the current version matches the expected value, avoiding long‑lasting locks but causing frequent conflicts on hot rows.

Example from a BI tool: a version field is stored in the main table; each API call supplies the version, and the service updates the row only if the version matches, then returns the next version number.

2. Middleware Token Mechanism

A token (global ID) is generated per operation and stored in Redis. The client sends the token in a request header. The server atomically checks the token and deletes it, ensuring a single successful execution.

public void submitOrder(OrderSubmitDTO orderSubmit) {
    String orderToken = orderSubmit.getToken();
    Long userId = orderSubmit.getUserId();
    // Atomic verification using Lua script
    String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    Long result = stringRedisTemplate.execute(
        new DefaultRedisScript<>(script, Long.class),
        Lists.newArrayList(USER_ORDER_TOKEN_PREFIX + userId),
        orderToken);
    if (result < 1) {
        throw new BusinessException("Order token expired or invalid, please retry");
    }
    // ... continue business logic
}

The Lua script guarantees that the read‑check‑delete sequence is atomic, preventing race conditions under high concurrency.

3. State‑Machine Based Idempotency

When a table contains a well‑defined status field, the transition logic itself can enforce idempotency. For example, an order moves from status 2 (paid) to status 3 (completed) only if it is currently in status 2:

UPDATE `order`
SET status = 3, update_time = NOW()
WHERE user_id = 1 AND status = 2;

This technique works only for tables that have an explicit status column and where the business process follows a predictable state progression.

Practical Notes

Pessimistic locks may hold rows for a long time; assess performance impact before use.

Optimistic locking is unsuitable for extremely high‑frequency updates because of conflict overhead.

Token‑based solutions require atomic operations; Lua scripts or distributed locks are recommended.

State‑machine checks are simple but limited to scenarios with explicit status fields.

Additional Illustrations

Scheduled Task Idempotency

@Scheduled(cron = "0 0/3 * * * ?")
public void task() {
    log.info("execute task time: " + IasDateUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
    // 1. Query tasks with status "pending"
    List<DataBusTask> dataBusTaskList = dataBusTaskService.getListByStatus();
    // 2. Process each task
    dataBusTaskList.forEach(dataBusTask -> {
        String uuid = dataBusTaskService.findTaskByName(dataBusTask.getName(), dataBusTask.getUserId());
        if (uuid != null) {
            dataBusTask.setStatus(DataBusConstant.TASK_SUCCESS);
            dataBusTask.setUuid(uuid);
            dataBusTaskDAO.updateById(dataBusTask);
            log.info("update dataBusTask success");
        } else {
            delBatch(dataBusTask);
        }
    });
}

The task first queries rows with a specific status, then updates them only once, achieving idempotent execution for low‑concurrency background jobs.

Distributed Lock Alternative

If a global unique index cannot be defined (e.g., in a distributed system), a distributed lock (Redis, Zookeeper, Redisson, etc.) can be acquired before inserting or updating data, then released after the operation.

GitHub repository with reference implementations:

https://github.com/plasticene/plasticene-boot-starter-parent

Gitee mirror:

https://gitee.com/plasticene3/plasticene-boot-starter-parent

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

databaseBackend Developmentoptimistic lockidempotencytokenpessimistic lockduplicate order
Shepherd Advanced Notes
Written by

Shepherd Advanced Notes

Dedicated to sharing advanced Java technical insights, daily work snippets, and the power of persistent effort.

0 followers
Reader feedback

How this landed with the community

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.