Backend Development 33 min read

Ensuring Distributed Transaction Consistency in High‑Concurrency Flash‑Sale Systems with Transactional Messages and RocketMQ

This article explains how to achieve strong consistency for high‑traffic flash‑sale (seckill) scenarios by using distributed transaction principles, flexible transaction models, and Alibaba Cloud RocketMQ transactional messages, providing detailed architectural guidance, implementation steps, code examples, and operational best practices.

High Availability Architecture
High Availability Architecture
High Availability Architecture
Ensuring Distributed Transaction Consistency in High‑Concurrency Flash‑Sale Systems with Transactional Messages and RocketMQ

Preface

Flash‑sale and seckill are common e‑commerce patterns that generate massive concurrent traffic, challenging system performance and stability. Similar high‑concurrency scenarios exist in ticketing, online education enrollment, and large‑scale events.

To address these challenges, the industry adopts techniques such as static‑dynamic separation, timed release, asynchronous processing, token queues, multi‑level caching, cheat detection, traffic protection, and full‑link stress testing.

This article focuses on guaranteeing consistency for flash‑sale business by building a high‑performance, highly‑available distributed consistency mechanism based on transactional messages.

Transaction Consistency Principles Review

A transaction is an indivisible unit of work that must either fully succeed or fully fail, characterized by the ACID properties: Atomicity, Consistency, Isolation, Durability.

Dirty read: Transaction A reads uncommitted data from Transaction B.

Non‑repeatable read: Two reads of the same data within a transaction return different results.

Phantom read: Two range queries within a transaction return different row counts.

Higher isolation levels reduce these anomalies but increase performance cost. Common isolation levels:

READ_UNCOMMITTED – allows all anomalies.

READ_COMMITTED – prevents dirty reads.

REPEATABLE_READ – prevents dirty and non‑repeatable reads.

SERIALIZABLE – prevents all three anomalies but has the lowest throughput.

Relational databases support ACID only for single‑node transactions; distributed transactions require additional mechanisms.

Distributed Transactions in Flash‑Sale Business

Three typical scenarios that generate distributed transactions:

One transaction accesses two different databases.

One transaction spans multiple data shards managed by sharding middleware.

One transaction invokes multiple micro‑services, each with its own data store.

In a flash‑sale flow, the order‑creation stage may involve three micro‑services (inventory, order, cart), and the payment‑success stage may involve four sub‑transactions (order status, inventory deduction, shipping notification, points addition). Ensuring consistency across these services is critical.

Implementation of Distributed Transactions

Traditional Distributed Transactions

Traditional XA two‑phase commit provides strong consistency but suffers from performance bottlenecks, single‑point‑of‑failure risk, and data‑inconsistency when the coordinator crashes after committing.

Flexible (BASE) Transactions

Flexible transactions relax strict isolation, allowing intermediate inconsistent states while guaranteeing eventual consistency. They achieve higher concurrency and better fault tolerance, making them suitable for flash‑sale workloads.

Traditional Distributed Transaction

Flexible Transaction

Business Refactor

No

Yes

Consistency

Strong

Eventual

Rollback Support

Yes

Implement fallback interface

Isolation

Supported

Relaxed or lock interface

Concurrent Performance

Low

High

Suitable Scenario

Low‑concurrency, short‑lived transactions

High‑concurrency, long‑lived transactions

Among flexible transaction models (TCC, Saga, transactional messages, max‑effort notification), this article concentrates on transactional messages.

Transactional Message Principle Analysis

Flash‑Sale Scenario Decomposition

The critical stages are order creation (inventory lock → order record) and payment success (order status update, inventory deduction, shipping notification, points addition). Each stage must be atomic across services.

Using an asynchronous message queue, the first participant sends a "half‑message" before executing its local transaction. After the local transaction succeeds, it sends a confirmation message; otherwise, the half‑message is discarded. The message queue stores the half‑message and delivers it only after receiving the confirmation.

If the participant crashes after the local commit, the queue performs a back‑check: it queries the participant for the transaction status and decides whether to deliver or discard the half‑message.

Ensuring Remote Transaction Success

Message delivery may fail due to network issues or remote consumer crashes. A retry mechanism is required: the queue repeatedly attempts delivery until the remote consumer acknowledges successful processing.

Full Process Diagram

(Diagram omitted – original shows half‑message → local commit → confirm → delivery → remote execution → ack.)

Transactional Message Practice

RocketMQ Message Queue

RocketMQ is a low‑latency, high‑throughput, highly‑available distributed messaging middleware provided by Alibaba Cloud. It fully supports transactional messages, including half‑message, confirm message, back‑check, and retry mechanisms.

Creating Resources

Steps include creating a RocketMQ instance, a Topic (set to "transactional" type), and two Group IDs—one for the local transaction participant and one for the remote participant.

Local Transaction Participant Code

Java example shows how to initialize a TransactionProducer , configure Group ID, AccessKey, SecretKey, and NameServer address, and start the producer.

Properties properties = new Properties();
properties.put(PropertyKeyConst.GROUP_ID, "XXX");
properties.put(PropertyKeyConst.AccessKey, "XXX");
properties.put(PropertyKeyConst.SecretKey, "XXX");
properties.put(PropertyKeyConst.NAMESRV_ADDR, "XXX");
TransactionProducer producer = ONSFactory.createTransactionProducer(properties, new LocalTransactionCheckerImpl());
producer.start();

A LocalTransactionCheckerImpl implements LocalTransactionChecker to answer the queue’s back‑check by inspecting business state.

public class LocalTransactionCheckerImpl implements LocalTransactionChecker {
    private static final Logger LOGGER = LoggerFactory.getLogger(LocalTransactionCheckerImpl.class);
    private static final BusinessService businessService = new BusinessService();
    @Override
    public TransactionStatus check(Message msg) {
        String transactionKey = msg.getKey();
        try {
            boolean isCommit = businessService.checkBusinessService(transactionKey);
            return isCommit ? TransactionStatus.CommitTransaction : TransactionStatus.RollbackTransaction;
        } catch (Exception e) {
            LOGGER.error("Transaction Key:{}", transactionKey, e);
            return TransactionStatus.Unknow;
        }
    }
}

The producer sends a half‑message together with a lambda that executes the local transaction and returns the appropriate TransactionStatus :

Message message = new Message(TOPIC, null, transactionKey, body.getBytes());
SendResult result = producer.send(message, (msg, arg) -> {
    try {
        boolean ok = businessService.execBusinessService(transactionKey);
        return ok ? TransactionStatus.CommitTransaction : TransactionStatus.RollbackTransaction;
    } catch (Exception e) {
        LOGGER.error("Transaction Key:{}", transactionKey, e);
        return TransactionStatus.RollbackTransaction;
    }
}, null);

Remote Transaction Participant Code

The remote consumer subscribes to the same Topic with a different Group ID, processes the message, and returns Action.CommitMessage to acknowledge successful handling.

Consumer consumer = ONSFactory.createConsumer(props);
consumer.subscribe(TOPIC, "*", (msg, context) -> {
    LOGGER.info("Receive: {}", msg);
    businessService.doBusiness(msg);
    return Action.CommitMessage;
});
consumer.start();

Transaction Rollback Considerations

When a remote transaction fails due to business errors (e.g., insufficient inventory, invalid address), the system must trigger a compensating transaction to revert the local changes and possibly initiate refunds. This compensating flow is itself a distributed transaction and can be implemented with a separate Topic.

Other Considerations

Message Idempotency

RocketMQ guarantees no loss but may deliver duplicates. Consumers must implement idempotent handling using a unique business key (the global transaction ID) to avoid double processing.

Daily Reconciliation

Because flexible transactions allow an intermediate state, periodic reconciliation is required to detect and resolve stuck or failed transactions (e.g., messages that exceeded retry limits, business exceptions, idempotency failures, or infrastructure outages).

Conclusion

Transactional messages provide an elegant way to achieve distributed consistency for high‑concurrency flash‑sale scenarios. Leveraging Alibaba Cloud RocketMQ’s high performance and reliability, developers can build robust, eventually consistent systems. However, adopting this model requires careful business analysis, code changes, and operational safeguards such as daily reconciliation.

e-commerceMicroserviceshigh concurrencyrocketmqdistributed transactionsflexible transactionstransactional messages
High Availability Architecture
Written by

High Availability Architecture

Official account for High Availability Architecture.

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.