Backend Development 16 min read

Understanding Transactional Messages in Distributed Systems: RocketMQ and Kafka

This article explains the principles of distributed transaction messages, comparing 2PC, TCC, and transactional messaging, and provides detailed walkthroughs of RocketMQ and Kafka implementations, including their two‑phase processes, broker handling, and source‑code insights for ensuring data consistency in asynchronous systems.

Architect
Architect
Architect
Understanding Transactional Messages in Distributed Systems: RocketMQ and Kafka

When we talk about transaction messages in a message queue, the concept of ACID immediately comes to mind. In monolithic systems, strict ACID constraints are rarely enforced, and in distributed systems we often have to compromise to achieve eventual consistency because availability is paramount.

Distributed Transaction

Common distributed transaction models include 2PC, TCC, and transactional messages. This article focuses on transactional messages while briefly mentioning 2PC and TCC.

2PC

Two‑Phase Commit (2PC) involves a coordinator and participants. In the prepare phase the coordinator asks participants to prepare; in the commit phase the coordinator decides to commit if all participants are ready, otherwise it rolls back.

2PC is limited to database‑level transactions and enforces strong consistency through synchronous blocking, which can cause long‑lasting resource locks and single‑point‑of‑failure risks.

TCC

Try‑Confirm‑Cancel (TCC) guarantees business‑level transactions. Each business operation must implement three methods: try (reserve resources), confirm (perform the actual operation), and cancel (rollback if any try fails). TCC introduces high coupling with business logic and requires idempotent confirm/cancel steps.

Transactional Message

Transactional messages are suited for asynchronous update scenarios where real‑time data consistency is not critical. Their purpose is to ensure that the producer and consumer see consistent data, e.g., deleting a shopping‑cart entry only after an order is successfully placed.

RocketMQ Transactional Message

RocketMQ implements transactional messages as a two‑phase process. When a transaction starts, a half‑message is sent to the broker; this message is invisible to consumers and stored in a special queue.

After the half‑message is sent, the local transaction is executed. Depending on the transaction result, the producer sends either a commit or a rollback message.

If the commit/rollback message fails to reach the broker, the broker periodically checks the transaction status by invoking a callback interface on the producer.

Below is a simplified usage example (code omitted for brevity).

RocketMQ Transactional Message Source Code Analysis

The core method is sendMessageInTransaction , which constructs a half‑message, sends it to the broker, executes the local transaction, and reports the transaction state back to the broker.

On the broker side, SendMessageProcessor#sendMessage detects the MessageConst.PROPERTY_TRANSACTION_PREPARED flag, stores the half‑message under the internal topic RMQ_SYS_TRANS_HALF_TOPIC , and prevents consumers from reading it.

The broker later processes commit or rollback requests via EndTransactionProcessor#processRequest . A commit rewrites the message to the real topic/queue, while a rollback records the half‑message in a half_op topic for later cleanup.

A background service TransactionalMessageCheckService periodically scans half‑messages and invokes TransactionalMessageServiceImpl#check to query the producer for the transaction outcome.

Kafka Transactional Message

Kafka’s transactional messages differ: they ensure that multiple messages sent within a single transaction are either all committed or all aborted, leveraging Kafka’s idempotent producer to achieve exactly‑once semantics for the producer side.

Kafka uses a transaction coordinator (part of the broker) to log transaction states. The producer sends a begin request, writes messages, then sends a commit or abort request. Consumers filter out transactional messages until the transaction is completed.

Kafka’s exactly‑once guarantee applies only to specific scenarios where the same Kafka cluster is both the source and the sink of data; it does not provide the same end‑to‑end transactional guarantees as RocketMQ.

Conclusion

Both RocketMQ and Kafka support transactional messaging, but RocketMQ’s model aligns with the typical need to coordinate a local business operation with a message send, while Kafka’s model is suited for stream‑processing pipelines that require exactly‑once delivery across multiple messages.

distributed systemsKafkaMessage Queuerocketmq2PCtccTransactional Messaging
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

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.