How RocketMQ Transactional Messages Ensure Data Consistency in Order‑Inventory Scenarios

The article explains how RocketMQ’s transactional message mechanism guarantees consistency between order creation and inventory deduction by using half‑messages, a local transaction callback, a second‑phase confirmation, and broker‑side timeout checks, while highlighting common pitfalls and best‑practice recommendations.

samdeepthink
samdeepthink
samdeepthink
How RocketMQ Transactional Messages Ensure Data Consistency in Order‑Inventory Scenarios

Developers of e‑commerce ordering systems often face the classic problem of keeping the order record and inventory deduction consistent when the two operations reside in separate services connected by a network. If the order write succeeds but inventory deduction fails, or vice‑versa, data inconsistency occurs.

Reserve a Slot for the Message

The process starts when the producer sends a half‑message. When the transactional message API is invoked, RocketMQ marks the message as a half‑message instead of writing it directly to the business topic. The broker stores this half‑message in an internal queue and hides the original target topic and queue ID in the message properties.

Consumers cannot see the half‑message; it is analogous to a check that has been written but not yet stamped.

The crucial point is that the half‑message must be persisted successfully before the local transaction is considered safe. If the local transaction later fails, the producer can explicitly tell the broker to roll back the half‑message, ensuring the consumer never receives it.

Local Transaction Determines Commit or Rollback

After the half‑message is stored, RocketMQ invokes the business‑implemented TransactionListener interface. The executeLocalTransaction method performs the local database operations such as writing the order table or deducting inventory, and returns one of three states: COMMIT_MESSAGE – local transaction succeeded. ROLLBACK_MESSAGE – local transaction failed. UNKNOW – the outcome is uncertain.

It is important that executeLocalTransaction contains only lightweight local logic; heavy operations (external calls, additional messages) can prolong the transaction‑message lifecycle.

Second‑Phase Confirmation to the Broker

When the local transaction finishes, the producer sends a one‑way confirmation to the broker indicating whether the half‑message should be committed or rolled back. This request does not wait for a broker response because the scenario does not require absolute reliability at this step; a later broker‑side check will handle any lost confirmations.

The normal path is the second‑phase confirmation; the exception path is the broker’s periodic check (回查) for messages that remain unconfirmed.

Broker Handles Commit or Rollback

Upon receiving the confirmation, the broker processes two cases:

If the state is commit, the broker removes the half‑message from the internal queue, restores the original topic and queue ID, and writes the message to the business topic so that consumers can consume it. An operation log is recorded to indicate successful processing.

If the state is rollback, the broker discards the half‑message and records a log so that the message will never be delivered.

The operation log also serves for de‑duplication during later checks.

Timeout and Broker‑Initiated Check

The broker runs a periodic task that scans half‑messages without a corresponding operation log. For messages that have exceeded a configured timeout, the broker proactively sends a check request to the producer.

The producer’s checkLocalTransaction method must be able to determine the real status of the local transaction based on the message. It returns commit, rollback, or unknown. If unknown, the broker repeats the check up to a configured maximum; after that, the message is discarded.

Common Pitfalls When Deploying

Both executeLocalTransaction and checkLocalTransaction must be implemented. Omitting a proper checkLocalTransaction implementation causes the broker to repeatedly check and eventually discard the message, breaking consistency.

Keep executeLocalTransaction lightweight; avoid external service calls or sending additional messages, which would prolong the transaction’s pending state.

Configure transactionTimeOut and transactionCheckMax sensibly. A timeout that is too short triggers premature checks; too many check attempts add pressure to the business system.

Consumers must be idempotent. After a transactional message is committed, retries may cause the same message to be delivered multiple times, so inventory deduction must not be performed twice.

Summary

RocketMQ transactional messages do not turn message sending and database writes into a true atomic operation—cross‑network atomicity is impossible. Instead, the mechanism splits the work into four steps: half‑message reservation, local transaction execution, second‑phase confirmation, and broker‑side timeout checks. This creates a chain that can be confirmed, rolled back, or compensated.

As long as the business correctly executes the local transaction and implements checkLocalTransaction to return the real status, consistency is delegated to RocketMQ. Understanding this flow makes it easy to map the source code classes such as TransactionListener, EndTransactionProcessor, and TransactionalMessageCheckService to their respective responsibilities.

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.

microservicesDistributed ConsistencyMessage QueueRocketMQTransactional Messaging
samdeepthink
Written by

samdeepthink

Knowledge Planet: Old Dock's Tech Chronicles Zhihu: SamDeepThinking A technical manager who still codes heavily on the front line. From junior developer to tech lead, then tech manager, now leading the whole front‑ and back‑end development team—leveling up along the way. I have some insights on programming, career development, and tech management.

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.