How Seata’s AT Mode Solves Distributed Transaction Challenges in Microservices
This article explains the fundamentals of single‑source and multi‑source transactions, reviews common distributed‑transaction patterns such as 2PC, 3PC, TCC, transaction‑status tables and message‑queue based eventual consistency, and then details the implementation and isolation mechanisms of Seata’s AT mode for microservice architectures.
0. Introduction
Computer systems contain many unreliable components; software techniques such as TCP, RAID, and ARIES‑based transaction mechanisms are used to guarantee correct processing. This article first reviews ACID properties of a single‑node database transaction, then explains why those guarantees break down when a business operation spans multiple independent data sources, and finally surveys the most common distributed‑transaction approaches.
1. Single‑Data‑Source vs Multi‑Data‑Source Transactions
A local transaction relies on the database’s built‑in ACID support (e.g., InnoDB’s undo/redo logs). In a microservice architecture each service owns its own database, so a business flow—such as an e‑commerce order that updates inventory and creates an order record—requires a global transaction. Local transactions cannot ensure global atomicity, consistency, isolation, or durability across these independent databases, which motivates distributed‑transaction mechanisms.
2. Common Distributed‑Transaction Solutions
2.1 Distributed Transaction Model
Key roles:
Transaction Participant : each database that holds part of the data.
Transaction Coordinator (Transaction Manager) : the service that invokes participants and drives the global commit/rollback.
Resource Manager : often synonymous with participant.
The coordinator records the progress of every participant and ensures that either all participants commit or all roll back, thereby achieving global ACID.
2.2 Two‑Generals Problem and Idempotency
The classic two‑generals problem illustrates uncertainty when a message may be lost or delayed. In distributed transactions this appears as an unknown request outcome. Clients must retry until an explicit acknowledgment is received. Idempotent operations guarantee that repeated execution does not corrupt state; typical implementations use a deduplication table keyed by a global transaction ID (XID).
2.3 Two‑Phase Commit (2PC) & Three‑Phase Commit (3PC)
2PC consists of:
Prepare phase – the coordinator asks each participant to prepare; participants reply YES (ready) or NO (cannot lock).
Commit phase – if all replies are YES, the coordinator sends a commit command; otherwise it sends a rollback.
Drawbacks:
Performance bottleneck: participants hold locks throughout both phases.
Blocking when the coordinator crashes – participants wait indefinitely.
Uncertainty caused by lost acknowledgments (the two‑generals problem).
3PC adds a pre‑commit (prepare‑to‑commit) phase with timeout‑based checks, reducing the blocking window but still cannot fully avoid coordinator failures.
2.4 TCC (Try‑Confirm‑Cancel)
TCC splits a global transaction into three application‑level steps:
Try : lock required resources and return YES if successful.
Confirm : if all tries succeeded, invoke confirm APIs to permanently apply changes.
Cancel : if any try fails or times out, invoke cancel APIs to roll back.
Both confirm and cancel must be idempotent; a common technique is a deduplication table that records whether a given XID has already been confirmed or cancelled.
2.5 Transaction‑Status‑Table Scheme
The coordinator maintains a status table with columns global_trx_id , operation , and status . After each successful participant call the status is advanced (e.g., INIT → STEP1_DONE → ALL_DONE). A background worker periodically scans the table; if a transaction does not reach the final state within a configurable timeout, it retries the unfinished steps or marks the transaction as ERROR for manual intervention. All participant APIs must be idempotent with respect to the global transaction ID.
2.6 Message‑Queue Based Final Consistency
Services publish business events to a reliable broker (e.g., RabbitMQ). Consumers process the events inside a local transaction and acknowledge only after successful processing. The broker automatically retries delivery of unacknowledged messages. To avoid duplicate processing, each consumer keeps a deduplication table keyed by the message ID. This pattern eliminates long‑lived locks but requires careful handling of the two‑generals problem to prevent lost or duplicated messages.
3. Seata AT Mode Implementation
3.1 Overview of AT Mode Workflow
Seata’s AT (Automatic Transaction) mode builds on relational‑database local transactions. It intercepts SQL statements, records custom undo logs (before‑image and after‑image), and then runs a two‑phase‑commit‑like protocol without the blocking behavior of XA. The two phases are:
First phase – acquire a global lock, execute the local transaction, write undo logs, and release the local lock.
Second phase – if the global transaction is to be committed, Seata asynchronously deletes the undo logs; if a rollback is required, Seata replays the undo logs to generate compensation SQL.
3.2 Detailed AT Mode Process (E‑commerce Example)
Assume a shopping service invokes an inventory service and an order service within a single global transaction.
Register a global transaction with Seata and obtain an XID.
Execute local transactions on the inventory and order databases up to the “prepare” stage; each writes an undo_log entry.
Register branch transactions (branch ID) under the same XID.
Commit the local transactions.
Each branch reports its commit status to Seata.
Seata aggregates the statuses and decides to commit or roll back globally.
Seata notifies each branch: on commit, branches delete their undo_log rows; on rollback, branches replay the undo logs to generate compensation statements and then delete the logs.
The undo_log table stores:
branch_id
xid
context
rollback_info – serialized before‑image/after‑image data
log_status
3.3 Commit Path
If all branches report success, Seata sends a commit command. Each branch simply deletes its undo_log rows; the data changes have already been persisted, so the commit is fast and non‑blocking.
3.4 Rollback Path
If any branch fails, Seata triggers a global rollback. For each branch:
Retrieve undo logs by xid and branch_id.
Validate that the after‑image still matches the current row (otherwise the rollback fails because another transaction modified the row).
Generate compensation SQL from the before‑image and after‑image.
Execute the compensation SQL.
Delete the corresponding undo_log entry.
3.5 Isolation Guarantees
AT mode uses a global lock to guarantee write isolation: a branch must acquire the global lock before committing its local transaction; if the lock is unavailable, it retries until a timeout. Read isolation defaults to read‑uncommitted , which may expose dirty reads. To achieve read‑committed , Seata proxies SELECT FOR UPDATE statements, acquiring a global lock and blocking the query until the lock is obtained.
4. Comparison of Approaches
All surveyed solutions—2PC, 3PC, TCC, transaction‑status tables, message‑queue based eventual consistency, and Seata AT mode—share the same core idea: a coordinator tracks the progress of local transactions and drives a global commit or rollback. Each approach balances performance, complexity, and fault tolerance differently, and real‑world implementations often adapt the pure protocols to meet specific business requirements.
5. Illustrative Code Snippets
Global transaction skeleton (pseudo‑code):
start global_trx;
call inventory_service.decreaseStock(...);
call order_service.createOrder(...);
commit global_trx;Order‑service local transaction that also persists a message for the MQ (ensuring atomicity):
// Within a single DB transaction
INSERT INTO t_order (...);
INSERT INTO t_local_msg (msg_id, payload) VALUES (...);
-- commitRepo‑service consumer handling a stock‑deduction message with idempotency:
if exists in dedup_table(msg_id) then
return; // already processed
end if;
UPDATE t_repo SET count = count - 1 WHERE production_code = ?;
INSERT INTO dedup_table(msg_id) VALUES (msg_id);
ACK to broker;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.
ITPUB
Official ITPUB account sharing technical insights, community news, and exciting events.
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.
