Backend Development 21 min read

Understanding Distributed Transactions: Two‑Phase Commit, Message‑Queue Solutions, and RocketMQ Transactional Messaging

This article explains how to guarantee data consistency when updating multiple tables or services by using local transactions for single‑node systems, two‑phase commit for distributed databases, and reliable message‑queue patterns—including RocketMQ transactional messages—to achieve eventual consistency and handle duplicate delivery.

IT Architects Alliance
IT Architects Alliance
IT Architects Alliance
Understanding Distributed Transactions: Two‑Phase Commit, Message‑Queue Solutions, and RocketMQ Transactional Messaging

When a user transfers money from an Alipay account to a YuEBao account, the operation consists of two database updates: deducting the amount from the Alipay table and adding the same amount to the YuEBao table. If the system crashes after the first update, the data becomes inconsistent.

The core problem can be abstracted as: after updating one table, how to ensure that another related table is also updated successfully.

In a single‑node environment the solution is a local transaction that wraps both updates in a single atomic unit:

BEGIN TRANSACTION;
UPDATE A SET amount = amount - 10000 WHERE userId = 1;
UPDATE B SET amount = amount + 10000 WHERE userId = 1;
COMMIT;

When the two tables reside on different database instances, local transactions no longer work and a distributed transaction is required. The most common approach is the Two‑Phase Commit (2PC) protocol, which involves a coordinator (TC) and multiple participants (Si). The coordinator first writes a prepare record to its log, asks each participant to prepare, collects their votes, and finally sends either commit or abort based on the votes.

Although 2PC guarantees consistency, it suffers from high latency and long lock times, making it unsuitable for high‑throughput services.

To avoid the performance penalty of 2PC, many systems use message‑queue based eventual consistency . The idea is to record a reliable message (the “ticket”) after the first update; the consumer later processes the message to complete the second update. Two patterns are described:

Coupled business‑message model : the business transaction and the message insertion share the same local transaction, ensuring that if the money is deducted the message is persisted.

Decoupled model : the producer first writes the business update, then sends a provisional message to a reliable queue; the message is only confirmed after the business transaction commits.

Idempotent consumption is achieved by maintaining a message_apply table that records processed message IDs; before applying a message the system checks this table to discard duplicates.

RocketMQ provides built‑in support for transactional messages. The workflow consists of three steps:

Send a prepared message to the broker.

Execute the local business transaction.

Based on the transaction result, send a commit or rollback to the broker.

If the broker does not receive a final status, it periodically checks the producer via TransactionCheckListener to decide the outcome.

// Example of sending a transactional message in RocketMQ
TransactionMQProducer producer = new TransactionMQProducer("groupName");
producer.setTransactionCheckListener(new TransactionCheckListenerImpl());
producer.start();
Message msg = new Message("TopicTransaction", "TagA", "KEY1", "Hello RocketMQ".getBytes());
SendResult sendResult = producer.sendMessageInTransaction(msg, new TransactionExecuterImpl(), null);
producer.shutdown();

The accompanying TransactionListenerImpl implements executeLocalTransaction (which performs the business logic) and checkLocalTransaction (used by the broker to resolve uncertain states). The consumer registers a MessageListenerOrderly to process messages and acknowledges success or suspension.

Finally, the article stresses that even with transactional messaging, rare failures can still break the debit‑credit balance. A reconciliation process that publishes a non‑transactional audit message after each successful transaction can be used to detect and correct mismatches between the producer and consumer sides.

Message Queuerocketmqdistributed transactionsTransactional MessagingTwo-Phase Commitdatabase consistencyidempotent processing
IT Architects Alliance
Written by

IT Architects Alliance

Discussion and exchange on system, internet, large‑scale distributed, high‑availability, and high‑performance architectures, as well as big data, machine learning, AI, and architecture adjustments with internet technologies. Includes real‑world large‑scale architecture case studies. Open to architects who have ideas and enjoy sharing.

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.