How RocketMQ Transactional Messages Ensure Distributed Consistency
This article explains the challenges of maintaining atomicity across multiple downstream services in distributed systems and details how RocketMQ's transactional message feature provides a two‑phase commit mechanism, lifecycle, implementation details, and practical usage to achieve eventual consistency.
Introduction
In distributed systems, a core business operation often needs to trigger several downstream services, and all of them must either succeed together or fail together to avoid partial inconsistencies. This problem is analogous to transactional guarantees in message queues.
Typical E‑commerce Scenario
When a user pays for an order, the system must update the order status, create a shipping record, adjust user points, and clear the shopping cart. These four branches must be consistent.
Traditional XA Transaction
Using an XA‑based distributed transaction wraps all branches into a single global transaction, ensuring correctness but causing large resource locks, low concurrency, and performance degradation as the number of branches grows.
Simplified Ordinary Message Approach
By treating the order update as a local transaction and the downstream changes as ordinary asynchronous messages, concurrency improves, but consistency can break in cases such as message send success while order update fails, or vice‑versa, and message timeouts leave the outcome unknown.
RocketMQ Transactional Messages
RocketMQ extends ordinary messages with a two‑phase commit protocol. A half (pre‑commit) message is stored after the producer sends it; the producer then executes a local transaction and finally reports a Commit or Rollback to the broker, which makes the message visible to consumers only on commit, guaranteeing global consistency.
Key Concepts
Transactional Message : A message that participates in a distributed transaction, achieving eventual consistency similar to XA.
Half Message : A message that has been sent to the broker but not yet confirmed; it remains invisible to consumers.
Message Check : The broker’s proactive query to the producer for the final status of a half message when the commit/rollback result is unknown.
Transactional Message Lifecycle
Initialization : Producer creates a half message.
Pending Commit : Broker stores the half message in a special transaction store; it is not visible to consumers.
Rollback : If the local transaction fails, the broker discards the half message.
Commit Pending Consumption : On successful local transaction, the broker converts the half message to a regular message, making it consumable.
Consuming : Consumer processes the message and reports the result; the broker retains the message for possible replay.
Deletion : After retention time expires or storage pressure, the broker physically deletes the message.
Basic Transactional Message Flow
The interaction proceeds as follows:
Producer sends a message to RocketMQ.
Broker persists the message and returns an ACK; the message is marked as a half message.
Producer executes its local transaction.
Producer reports Commit or Rollback to the broker.
If the broker does not receive a confirmation (e.g., network failure), it initiates a message check after a timeout.
Producer receives the check, re‑evaluates the local transaction outcome, and resends the confirmation.
Implementation Details
Handling Half Messages
The broker converts the original topic to RMQ_SYS_TRANS_HALF_TOPIC and stores the half message in the corresponding queue. The processing logic resides in SendMessageProcessor.
Commit / Rollback Commands
When the producer sends a commit, the broker writes an OP message indicating the offset of the half message, marks it deleted, restores the original topic, and writes the message to the commit log for consumption. For rollback, the broker writes an OP message but does not restore the half message, preventing consumer delivery. The core logic is in EndTransactionProcessor.
Message Check Process
If the broker lacks a corresponding OP message for a half message after the configured transactionTimeOut (default 6 s), it re‑queues the half message and sends a check command to the producer. The producer re‑examines the local transaction and replies again. Messages older than transactionCheckMaxTimeInMs (default 12 h) are skipped.
The check implementation can be found in TransactionalMessageServiceImpl#check.
Practical Usage
To use transactional messages, create a topic of type “transactional” via the console or CLI. When sending a transactional message, the producer must:
Begin a transaction and associate it with the local business logic.
Configure a transaction checker and bind the transactional topic list so the client can perform automatic state recovery.
After a successful commit, the transactional message becomes a regular message for consumers, requiring no special handling on the consumer side.
Precautions
Avoid a large number of pending transactions; excessive checks can degrade performance.
Do not reuse the same Group ID for transactional and non‑transactional messages, as the broker uses the Group ID for check routing.
Half messages that exceed the timeout are automatically rolled back by the broker.
Conclusion
RocketMQ’s transactional message mechanism provides a high‑performance, scalable way to achieve distributed transaction consistency without the heavy locking of XA, making it suitable for cloud‑native microservice architectures.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Alibaba Cloud Native
We publish cloud-native tech news, curate in-depth content, host regular events and live streams, and share Alibaba product and user case studies. Join us to explore and share the cloud-native insights you need.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
