By Zhu Jinjun
XA protocol is a distributed transaction processing specification proposed by the X/Open organization. It mainly defines the interface between the transaction manager (TM) and the local resource manager (RM). Currently, mainstream databases (such as Oracle and DB2) all support the XA protocol.
Since MySQL 5.0, its InnoDB storage engine has supported the XA protocol. In this article, the MySQL database is used as the experiment environment to introduce the source code.
For a distributed transaction, the two-phase commit process is divided into prepare and commit phases. Let’s take the e-commerce system as an example. The distributed system has three services: order, account, and inventory, as shown in the following figure:
In the first phase, the transaction coordinator sends a prepare request to a transaction participant. The transaction participant will reply yes if it can commit the transaction on receiving the request or no if it cannot.
In the second phase, if all transaction participants reply yes, the transaction coordinator will send a commit request to all transaction participants. Otherwise, a rollback request is sent.
There are three problems with the two-phase commit:
The three-phase commit makes improvements to solve the problems of two-phase commit:
canCommit
and preCommit
.The following figure shows how it works:
The preCommit
phase introduced enables the coordinator to check the status of each transaction participant for a second time before the commit to ensure the status consistency. However, the problem here is that if a rollback request is sent in the third phase, but some nodes do not receive it, these nodes will submit the transaction after timeout, resulting in data inconsistency.
The XA transaction syntax is listed below:
In the first phase: start the XA transaction. In this phase, xid indicates the global transaction ID:
XA {START|BEGIN} xid [JOIN|RESUME]
End the XA transaction:
XA END xid [SUSPEND [FOR MIGRATE]]
The second phase is the prepare phase:
XA PREPARE xid
The third phase is the commit/rollback phase:
XA COMMIT xid [ONE PHASE]
XA ROLLBACK xid
View all transactions in the PREPARE phase:
XA RECOVER XA RECOVER [CONVERT XID]
Seata is an open-source distributed transaction solution released by Alibaba. Currently, Seata supports four transaction modes: AT, TCC, SAGA, and XA.
Seata XA mode is implemented using database support for the XA protocol in branch transactions. Let's take a look at the Seata official website: [1]
As shown in the figure above, the process of Seata XA mode is the same as that of other modes:
Here is an introduction to the UML class diagram associated with RM client initialization: [2]
In this figure, there’s an AbstractNettyRemotingClient
class. Its internal class ClientHandler
processes requests from TC and delegates them to the processMessage
method of the parent class AbstractNettyRemoting
. The processMessage
method calls the process method of the RmBranchCommitProcessor
class.
Note: The Seata XA mode optimizes traditional three-phase commit and changes to two-phase commit:
MySQL currently supports this two-phase optimization of the Seata XA mode.
However, this optimization is not supported by Oracle because Oracle implements the standard XA protocol. In other words, the coordinator sends a prepare request to the transaction participants after the XA end. Then, it sends the commit/rollback request. Thus, Seata XA mode is not supported by Oracle.
The Seata XA mode is implemented using a data source proxy. The data source proxy must be configured manually. The code is listed below:
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DruidDataSource druidDataSource() {
return new DruidDataSource();
}
@Bean("dataSourceProxy")
public DataSource dataSource(DruidDataSource druidDataSource) {
return new DataSourceProxyXA(druidDataSource);
}
XAConnection
based on a common DataSource
. However, this method may incur compatibility issues with other databases such as Oracle. Therefore, Seata allows developers to configure an XADataSource
by themselves.When RM receives the DML request, Seata will use ExecuteTemplateXA
for execution. The key of the execute method is to change the autocommit
attribute to false, which is true for MySQL by default. After committing the transaction, the autocommit attribute needs to be changed back.
The following part describes the main code during the first-phase commit of XA.
The [1] mark in the code above calls the setAutoCommit
method of the ConnectionProxyXA
class. In the source code of this method, XA start does three things:
xaResource.start(this.xaBranchXid, XAResource.TMNOFLAGS);
xaActive
to trueRM does not directly use the branchId
returned by TC as the branchId
of the XA data source but uses the global transaction ID (xid) and branchId
to rebuild one.
Call the execute method of PreparedStatementProxyXA
to execute the SQL statement
public void commit() throws SQLException {
// Part of the source code is omitted
try {
// XA End: Success
xaResource.end(xaBranchXid, XAResource.TMSUCCESS);
// XA Prepare
xaResource.prepare(xaBranchXid);
// Keep the Connection if necessary
keepIfNecessary();
} catch (XAException xe) {
try {
// Branch Report to TC: Failed
DefaultResourceManager.get().branchReport(BranchType.XA, xid, xaBranchXid.getBranchId(),
BranchStatus.PhaseOne_Failed, null);
} catch (TransactionException te) {
// Only a warn-level log is printed
}
throw new SQLException(
"Failed to end(TMSUCCESS)/prepare xa branch on " + xid + "-" + xaBranchXid.getBranchId() + " since " + xe
.getMessage(), xe);
} finally {
cleanXABranchContext();
}
}
This source code tells us that the commit statement does three things:
As we’ve discussed, Seata combines the first two phases of the XA protocol into one.
The call relationship is listed on the following sequence diagram:
Let’s take a look at the process method of the RmBranchCommitProcessor
class. The code is listed below:
@Override
public void process(ChannelHandlerContext ctx, RpcMessage rpcMessage) throws Exception {
String remoteAddress = NetUtil.toStringAddress(ctx.channel().remoteAddress());
Object msg = rpcMessage.getBody();
if (LOGGER.isInfoEnabled()) {
LOGGER.info("rm client handle branch commit process:" + msg);
}
handleBranchCommit(rpcMessage, remoteAddress, (BranchCommitRequest) msg);
}
As shown in the sequence diagram, this handleBranchCommit
method finally calls the handle method of AbstractRMHandler
and calls the finishBranch
method of the ResourceManagerXA
class using the branchCommit
method.
The ResourceManagerXA
class is a resource manager of XA mode. The following figure shows the UML classes of the resource manager (RM) in Seata:
This finishBranch
method calls the connectionProxyXA.xaCommit
method. Let's take a look at the xaCommit
method:
public void xaCommit(String xid, long branchId, String applicationData) throws XAException {
XAXid xaXid = XAXidBuilder.build(xid, branchId);
// Here, xaResource is MysqlXAConnection as MySQL is used.
xaResource.commit(xaXid, false);
releaseIfNecessary();
}
The commit method of the data source is called to commit the RM branch transaction.
At this point, the entire RM branch transaction has ended. The Rollback code logic is similar to that of commit.
This xaResource
is an instance of the MysqlXAConnection
class in the mysql-connector-java.jar
packet. It encapsulates the XA protocol interface provided by MySQL.
Seata XA mode is implemented using the data source proxy. The underlying layer uses the native support of the database for the XA protocol.
In the Java driver library of MySQL, the MysqlXAConnection
class encapsulates the underlying API of the XA protocol for external calls.
TCC and SAGA modes need to implement prepare, commit, and rollback logic in the business code. Compared with that, XA mode does not intrude into the business code.
[1] http://seata.io/en-us/docs/overview/what-is-seata.html
[2] https://github.com/seata/seata
506 posts | 48 followers
FollowAlibaba Cloud Native Community - February 7, 2023
Alibaba Cloud Native Community - June 25, 2021
Stone Doyle - January 28, 2021
ApsaraDB - August 29, 2024
ApsaraDB - November 1, 2022
Alibaba Cloud Native Community - September 14, 2023
506 posts | 48 followers
FollowMigrate your legacy Oracle databases to Alibaba Cloud to save on long-term costs and take advantage of improved scalability, reliability, robust security, high performance, and cloud-native features.
Learn MoreA ledger database that provides powerful data audit capabilities.
Learn MoreAlibaba Cloud PolarDB for Xscale (PolarDB-X) is a cloud-native high-performance distributed database service independently developed by Alibaba Cloud.
Learn MoreA financial-grade distributed relational database that features high stability, high scalability, and high performance.
Learn MoreMore Posts by Alibaba Cloud Native Community