The author | | Zhu Jinjun source alibaba cloud native public number

XA protocol is a distributed transaction processing specification proposed by X/Open organization. It mainly defines the interface between transaction manager TM and local resource manager RM. At present, mainstream databases, such as Oracle and DB2, all support XA protocol.

Mysql from version 5.0, innoDB storage engine has supported XA protocol, today’s source code introduction experimental environment using mysql database.

Two-phase commit

The two-phase commit of a distributed transaction is divided into prepare and COMMIT phases. Taking 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 the transaction participant. After receiving the request, the transaction participant replies yes if it can commit the transaction, or no otherwise.

In the second phase, if all transaction participants respond with yes, the transaction coordinator sends a COMMIT request to all transaction participants, or a ROLLBACK request otherwise.

There are three problems with two-phase commit:

  • The local transaction will lock the resources in the prepare phase. If any other transaction is going to alter the account in Xiaoming, the local transaction will have to wait for the previous transaction to complete. This results in system performance degradation.
  • The coordination node has a single point of failure. If the coordination node in the first phase prepares successfully, but fails before issuing the commit command in the second phase, the data resources of all services are locked and the transaction will wait indefinitely.
  • Data is inconsistent. If the prepare command is executed successfully in phase 1, but the coordination node fails to send the COMMIT command to a node in phase 2, data is inconsistent.

Three-stage commit

In order to solve the problem of two-phase commit, three-phase commit has been improved:

  • Timeouts are introduced at both the coordination node and the transaction participant.
  • The prepare phase of the first phase is divided into two steps, canCommi and preCommit.

The diagram below:

After the preCommit phase is introduced, the coordination node rechecks the state of the individual transaction participants to ensure that they are consistent before committing. However, there is also a problem, that is, if the third phase sends a rollback request and some nodes do not receive it, the nodes that do not receive it will submit it after timeout, resulting in data inconsistency.

Introduction to XA transaction syntax

The syntax for xa transactions is as follows:

  1. The first of the three phases: start an XA transaction, where xID is the global transaction ID:
XA {START|BEGIN} xid [JOIN|RESUME]
Copy the code

End an XA transaction:

XA END xid [SUSPEND [FOR MIGRATE]]
Copy the code
  1. The second stage of the three stages, namely prepare:
XA PREPARE xid
Copy the code
  1. The third phase of the three phases, namely COMMIT /rollback:
XA COMMIT xid [ONE PHASE]
XA ROLLBACK xid
Copy the code
  1. View all transactions in the PREPARE phase:
XA RECOVER XA RECOVER [CONVERT XID]
Copy the code

Seata XA profile

Seata is an open source distributed transaction solution launched by Ali. Currently, it has four modes: AT, TCC, SAGA and XA.

Seata’s XA schema is implemented by leveraging database support for the XA protocol in branch transactions. Let’s take a look at the introduction on the SEATA website: [1]

As you can see from the figure above, the flow in SeATA XA mode is the same as in any other mode:

  1. TM enables global transactions
  2. RM registers branch transactions with the TC
  3. RM reports the branch transaction status to the TC
  4. The TC sent a commit or rollback request to RM. Procedure
  5. TM ends the global transaction

Here is the UML class diagram for the RM client initialization association: [2]

This figure has a class is a AbstractNettyRemotingClient, The inner ClientHandler class handles requests from TCS and delegates them to the AbstractNettyRemoting’s processMessage method. The processMessage method calls the process method of the RmBranchCommitProcessor class.

Note that “Seata’s XA mode optimizes the traditional three-phase commit to two-phase commit” :

  • Phase 1 Perform the following steps: START XA, execute SQL, and end XA. Then perform XA prepare.
  • In phase 2, perform XA Commit /rollback.

Mysql currently supports two-stage optimization of seata XA mode.

“However, this optimization does not support Oracle because oracle implements the standard XA protocol. After XA end, the coordinating node sends prepare to the transaction participant. This also leads to seata xa mode not supporting Oracle very well.”

Seata XA source

The XA pattern in SEATA is implemented using the data source proxy, which needs to be manually configured as follows:

@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DruidDataSource druidDataSource() {
    return new DruidDataSource();
}

@Bean("dataSourceProxy")
public DataSource dataSource(DruidDataSource druidDataSource) {
    return new DataSourceProxyXA(druidDataSource);
}
Copy the code
  • It is also possible to create an XAConnection from a normal DataSource, but this approach has compatibility issues (e.g. Oracle), so Seata uses developer configuration XADataSource.
  • The XA data source agent provided by SeATA requires the druID connection pool to be used in the code framework.

1. XA stage 1

When RM receives a DML request, Seata will execute it using ExecuteTemplateXA. The key part of the execute method is to change the autocommit property to false. Mysql defaults autocommit to true. After the transaction commits, change autoCOMMIT back to the default.

Let’s take a look at the main code committed in phase one of XA.

1) Start XA

ConnectionProxyXA’s setAutoCommit method is called at [1]. In the source code for this method, XA Start does three things:

  • Register branch transactions with TCS
  • Call XA Start for the data source
xaResource.start(this.xaBranchXid, XAResource.TMNOFLAGS);
Copy the code
  • Set xaActive to true

RM did not directly use the branchId returned by TC as the branchId of the XA data source, but rebuilt one using the global transaction ID (XID) and branchId.

2) Execute SQL

Execute SQL by calling PreparedStatementProxyXA’s Execute.

3) XA end/prepare

Public void commit() throws SQLException {// omit part of the source code 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); Throw new SQLException("Failed to end(TMSUCCESS)/prepare XA branch on " + xid + "-" + xaBranchXid.getBranchId() + " since " + xe .getMessage(), xe); } finally { cleanXABranchContext(); }}Copy the code

From the source code, we can see that the COMMIT does three things:

  • Call XA end of the data source
  • Call XA Prepare for the data source
  • Report branch transaction status to TC

Here we see that SEATA combines the first two phases of the XA protocol into one phase.

2. XA commit

The invocation relationship here is represented by a sequence diagram:

Take a look at the process method of the RmBranchCommitProcessor class as follows:

@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); }Copy the code

As you can see from the sequence diagram, the above handleBranchCommit method ends up calling the AbstractRMHandler handle method. Finally, the finishBranch method of the ResourceManagerXA class is called through the branchCommit method. The ResourceManagerXA class is the XA schema resource manager. Look at the following class diagram, which is the UML class diagram for resource Manager (RM) in SEATA:

The above finishBranch method calls the connectionProxyXA. XaCommit method, finally we see the xaCommit method:

public void xaCommit(String xid, long branchId, String applicationData) throws XAException { XAXid xaXid = XAXidBuilder.build(xid, branchId); MIT (xaXid, false); MysqlXAConnection xaResource.com MIT (xaXid, false); releaseIfNecessary(); }Copy the code

The commit method of the data source was called to commit the RM branch transaction.

At this point, the entire RM branch transaction ends. The code logic of Rollback is similar to that of COMMIT.

Finally, xaResource is an instance of the MysqlXAConnection class in the mysql-connector-java.jar package, which encapsulates the XA protocol interface provided by mysql.

conclusion

The XA schema in SEATA is implemented using a data source agent, with the underlying database’s native support for the XA protocol.

In mysql’s Java driver library, MysqlXAConnection class encapsulates the low-level interface of XA protocol for external calls.

Compared with TCC and SAGA modes, which require the prepare/ COMMIT/ROLLBACK logic to be implemented in the business code, XA mode is non-intrusive to the business code.

Reference

[1], [2] : http://seata.io/zh-cn/docs/overview/what-is-seata.html https://github.com/seata/seata