• The foreword 0.
  • 1. Single-source transactions & multi-source transactions
  • 2. Common distributed transaction solutions
    • 2.1. Distributed transaction model
    • 2.2. Two general problems and idempotence
    • 2.3. Two-stage Commit (2PC) & Three-stage Commit (3PC) scenarios
    • 2.4. TCC scheme
    • 2.5. Transaction status table scheme
    • 2.6. Final consistency transaction scheme based on message-oriented middleware
  • 3. Implementation of Seata in AT Mode
    • 3.1. Overview of the workflow of Seata in AT Mode
    • 3.2. Detailed workflow of Seata in AT Mode
  • 4. The conclusion

The foreword 0.

From CPU to memory, to disk, to the operating system, to the network, the computer system is everywhere there are unreliable factors. Engineers and scientists try to combat this unreliability with hardware and software to ensure that data and instructions are processed correctly. TCP reliable transport protocol in the network domain, Raid5 and Raid6 algorithms in the storage domain, transaction mechanism based on ARIES algorithm theory in the database domain…

This article first introduces the ACID properties of stand-alone database transactions, and then pointed out that the plight of the distributed scenario operating multiple data sources, which leads to the distributed transaction solution is commonly used in distributed systems, these solutions can guarantee business code in the operation of multiple data sources, like operating a single data source, with ACID properties. AT the end of this paper, a mature distributed transaction framework, Seata global transaction implementation is presented.

1. Single-source transactions & multi-source transactions

If an application in a business flow through the connection driver and data source interface only connection and query, the query here is generalized, including add and check, etc.) instead of a specific database, the application can use the database provides the transaction mechanism, if database supports transactions, ensure the reliability of the record of operation in the library, There are four semantics of reliability:

  • Atomicity, A
  • Consistency, C
  • Isolation, I
  • Persistence, D

I will not explain these four semantics here; understanding single-source transactions and their ACID properties is a prerequisite for reading this article. For example, MySQL’s InnoDB engine uses the Undo Log + Redo Log + ARIES algorithm to implement transactions for a single database. This is a very big topic, beyond the scope of this article, readers are interested in their own research.

Single-source transactions can also be called stand-alone transactions, or local transactions.

In distributed scenarios, a system consists of multiple subsystems, each with independent data sources. Multiple subsystems call each other to compose more complex businesses. In the current popular microservice system architecture, each subsystem is called a microservice, and each microservice also maintains its own database to maintain independence.

For example, an e-commerce system may consist of shopping micro-services, inventory micro-services, order micro-services, etc. Shopping micro-service integrates shopping business by calling inventory micro-service and order micro-service. User request shopping service provider to complete the order, micro shopping service on the one hand, called micro service inventory deduct corresponding amount of inventory goods, on the other hand calls order micro service insertion order record (in order to convenient, after the distributed transaction is described solutions presented here is one of the most simple of electricity system micro service division and the most simple shopping business processes, Subsequent payment, logistics and other services are not considered). The e-commerce system model is shown in the figure below:

In the business scenario of user shopping, the business of shopping-service involves two databases: inventory database (REPO_DB) and order database (rePO_DB), that is, the shopping business is combined by invoking multiple data sources. As a consumer-oriented system, e-commerce system should ensure the high reliability of shopping business, and reliability here also has four meanings of ACID.

However, the local transaction mechanism of a database only works on its own query operations (here the query is generalized, including add, delete, change, query, etc.), and cannot interfere with the query operations of other databases. Therefore, the local transaction mechanism provided by the database itself cannot ensure the reliability of business for multi-data source global operations.

Based on this, the distributed transaction mechanism for multi-source operations emerged.

Distributed transactions can also be called global transactions.

2. Common distributed transaction solutions

2.1. Distributed transaction model

The following terms are often used to describe distributed transactions:

  • Transaction participant: For example, each database is a transaction participant
  • Transaction coordinator: A service that accesses multiple data sources, such as shopping- Service, is a transaction coordinator
  • Resource Manager (RM) : Usually synonymous with transaction participant
  • Transaction Manager (TM) : Usually synonymous with Transaction coordinator

In the distributed transaction model, a TM manages multiple RMS, that is, a service program accesses multiple data sources. TM is a global transaction manager that coordinates the progress of multiple local transactions to commit or roll back together, resulting in a global ACID feature.

2.2. Two general problems and idempotence

The two-general problem is a classic problem in the field of network, which is used to express the subtlety and complexity of interconnection protocol design in computer networks. Here’s a simplified version of the two Generals problem:

A white army was trapped in a valley flanked on either side by blue troops. There were more White men trapped in the valley than either blue army on either side, and fewer than two blue armies combined. If a blue army attacks a white army alone, it will be defeated. But if two blue teams attack at the same time, they can win. The commander of the two blues was on the left side of the valley. He wanted both blues to attack at the same time, so that the order was sent to the blues on the right side of the valley to tell them when to attack. Assuming they could only send soldiers across the valley where the White army was (the only communication channel) to deliver the message, it was possible that the soldiers would be captured while crossing the valley.

The commander in chief will be able to confirm victory only when the messenger soldier returns successfully (pictured above). Now the question arose, could the commander in chief of the blue army on the left decide to attack at the appointed time when the messenger had not returned?

The answer is no. If the soldier sent out doesn’t come back, he may encounter two things:

1) The order was captured before it was delivered (middle) and the blue army on the right did not know when to attack;

2) The order arrives, but is captured on the way back (below), at which point the right side knows when to attack, but the left side does not know if the right side knows when to attack.

Similar problems are common in computer networks, such as the sender sending an HTTP request to the receiver, or the MySQL client sending an insert statement to the MySQL server, and then timeout and no response. Did the server write successfully or failed? The answer is no, in the following ways:

1) The request may not be sent to the server at all due to a network failure, so the write fails;

2) The server may have received and written successfully, but the server is down before sending the response to the client;

3) It is possible that the server has received and written the data successfully, and has sent a response to the client. However, the response has not been sent to the client due to a network fault.

In either case, the client sees the same result: it makes a request that doesn’t get a response. To ensure that the server writes data successfully, the client can only resend the request until it receives a response from the server.

A similar problem problem is called the network two General problem.

The existence of the network two general problem makes the sender of the message often have to repeatedly send the message, until receiving the receiver’s confirmation is considered to be successful, but this often leads to the repeated message. For example, when the order module invokes the payment module for deduction in the e-commerce system, if the network failure leads to the second general problem, the deduction request will be sent repeatedly, and the result of repeated deduction will obviously not be accepted. Therefore, to ensure that no matter how many times the debit request is sent in a transaction, the receiver has and only performs one debit action, this guarantee mechanism is called the idempotency of the receiver.

2.3. Two-stage Commit (2PC) & Three-stage Commit (3PC) scenarios

2PC is a simple model for implementing distributed transactions. The two phases are:

1) Preparation stage: The transaction coordinator initiates a request to each transaction participant: “I am going to execute a global transaction, which involves resources distributed among your data sources, respectively… You have your own resources ready (that is, you each perform local transactions to the commit phase). Each participant coordinator replies with yes (indicating that the global transaction is ready and allowed to commit) or NO (indicating that the participant cannot get the local resource required by the global transaction because it is locked by another local transaction) or times out.

2) Commit phase: If all participants reply yes, the coordinator will initiate transaction commit operation to all participants, and then all participants will execute local transaction commit operation and send ACK to the coordinator. If any of the participants replies no or times out, the coordinator initiates a transaction rollback to all participants, who then perform their own local transaction rollback and send an ACK to the coordinator upon receipt.

The flow of 2PC is shown in the figure below:

As you can see from the figure above, to implement 2PC, all participants implement three interfaces:

  • Prepare() : TM calls this interface to ask if each local transaction is ready
  • Commit() : TM calls this interface to require individual local transactions to Commit
  • Rollback() : TM calls this interface to require each local transaction to Rollback

These three interfaces can be understood simply (but loosely) as the XA protocol. XA protocol is a distributed transaction processing standard proposed by X/Open. Major databases such as MySQL, Oracle and DB2 all implement the XA protocol, so they can be used to implement the 2PC transaction model.

2PC is easy to understand, but it has the following problems:

1) Poor performance. In the preparation phase, it is necessary to wait for all participants to return before entering phase 2. During this phase, related resources on each participant are locked exclusively, and local transactions on the participant that intend to use these resources have to wait. Because of this synchronization blocking problem, the local transaction concurrency of each participant is affected.

2) After the preparation stage, if the coordinator goes down, all participants will not receive the commit or rollback instruction, resulting in all participants “at a loss”;

3) in the commit phase, the coordinator sent all of the participants commit directives, if a participant is not return ACK, so coordinator don’t know what happened inside the participants (two general problems due to network, the participants may not received submission instructions, always in a state of waiting for a command receiving submitted; Or it may have received and successfully executed a local commit, but the returned ACK was not sent to the coordinator due to a network failure), and it is not possible to decide whether to roll back all participants next.

2PC was followed by 3PC, which turned the two-phase process into a three-phase process: the ask phase, the prepare phase, and the commit or rollback phase, which I won’t detail here. 3PC uses the timeout mechanism to solve the synchronization blocking problem of 2PC and avoid resources being locked permanently, which further enhances the reliability of the whole transaction process. But 3PC can’t handle similar outages, but with less inconsistencies in multiple data sources.

In addition to performance and reliability problems, 2PC also has limited application scenarios. It requires participants to implement XA protocol. For example, participants can complete 2PC processes using databases that implement XA protocol. However, when multiple system services call each other using API interfaces, the XA protocol is not complied with, and 2PC is not applicable. Therefore, 2PC is rarely used in distributed application scenarios.

Therefore, 2PC cannot be used in the aforementioned emporiumscape, because shopping-service indirectly accesses repo_DB and order_DB by calling repo-service and order-service through RPC interface or Rest interface. Unless the shopping-service directly configures rePO_DB and order_DB as its own databases.

2.4. TCC scheme

Describe the e-commerce micro-service model used by TCC solution as shown in the figure below, in which shopping-service is the transaction coordinator and Repo-service and Order-service are the transaction participants.

As mentioned above, 2PC requires participants to implement the XA protocol, which is usually used to solve transactions between multiple databases, which is relatively limited. When multiple system services call each other using API interfaces, the XA protocol is not complied with and 2PC is not applicable. Modern enterprises mostly use distributed microservices, so it is more important to solve the problem of distributed transactions between multiple microservices.

TCC is a solution to the problem of distributed transactions between multiple microservices. TCC stands for Try, Confirm, and Cancel. In essence, TCC is an application layer 2PC, which is also divided into two stages:

1) Stage 1: Preparation stage. The coordinator calls all the try interfaces provided by each microservice to lock the resources involved in the entire global transaction. If the lock succeeds, the try interface returns yes to the coordinator.

2) Stage 2: Submission stage. If the try interface of all services returns yes in phase 1, then the commit phase is entered. The coordinator calls the Confirm interface of all services, and each service commits the transaction. If the try interface of any service returns no at phase one or times out, the coordinator calls the Cancel interface of all services.

The TCC process is shown below:

The key question here is, since TCC is a 2PC at the service level, how does it address 2PC’s inability to handle downtime? The answer is retry. Since the try operation locks all resources involved in the global transaction, ensuring that all preconditions for the business operation are met, Therefore, both confirm and Cancel phase failures can be retried until confirm or Cancel succeeds (success is when all services return an ACK for confirm or Cancel).

The key issue here is that it is possible to repeat confirm or Cancel while retrying confirm and Cancel (considering the existence of network two general issues), so you need to make sure that the confirm and Cancel operations are idempotent. That is, confirm or Cancel is performed only once per participant in the entire global transaction. There are many solutions to implement idempotency of confirm and cancel operations. For example, each participant can maintain a de-duplicate table (which can be implemented using database tables or in-memory KV components). Record whether confirm or Cancel has been performed for each global transaction (distinguished by the global transaction flag XID). If yes, the global transaction will not be performed again.

TCC was proposed by the Alipay team and is widely used in the financial system. When we use our bank account balance to buy a fund, we notice that the portion of our bank account used to buy the fund is frozen first, so we can guess that this process is probably the first stage of TCC.

2.5. Transaction status table scheme

Another tCC-like transaction solution is implemented with the aid of a transaction state table. Suppose you want to implement the two processes of calling repo-service to deduct inventory and calling order-service to generate an order in a distributed transaction. In this scenario, the coordinator shopping-service maintains a transaction state table as follows:

Distributed transaction ID Content of the transaction State of affairs
global_trx_id_1 Operation 1: Call the Repo-service to reduce the inventory

Operation 2: Call order-service to generate the order
State 1: Initial

Status 2: Operation 1 succeeded

Status 3: Operations 1 and 2 succeeded

The initial status is 1, and the status is updated every time a service is successfully invoked. Finally, all service invocations are successful, and the status is updated to 3.

With this table, you can start a background task that scans the state of transactions in the table. If a distributed transaction never reaches state 3 (set a transaction period threshold), the transaction is not successfully executed. You can then call repo-service to subtract inventory and order-service to generate orders. Until all calls succeed, the transaction state reaches 3.

If multiple retries do not bring the state to 3, you can set the transaction state to ERROR and intervene with human intervention.

Because of the service invocation retry, each service’s interface is idempotent according to the globally distributed transaction ID, the same idempotent implementation as in Section 2.4.

2.6. Final consistency transaction scheme based on message-oriented middleware

Both 2PC & 3PC, TCC and transaction state table basically follow the idea of XA protocol, that is, in essence, these schemes are transaction coordinators to coordinate the progress of local transactions of each transaction participant, so that all local transactions are committed or rolled back together, and finally achieve a global ACID feature. In the process of coordination, the coordinator needs to collect the current state of each local transaction and issue instructions for the next stage based on these states.

However, the global transaction concurrency of the whole distributed system is not too high due to the complicated operation, large time span, or the exclusive locking of related resources during the global transaction. This is difficult to meet the transaction throughput requirements of high concurrency scenarios such as e-commerce, so Internet service providers have explored many distributed transaction solutions that run counter to XA protocol. The final consistent global transaction implemented by message-oriented middleware is a classic scheme.

In order to show the essence of this scheme, I will use the following micro-service structure of e-commerce system to describe it:

In this model, users no longer request integrated shopping-service to place orders, but directly request order-service to place orders. On the one hand, order-service adds order records, and on the other hand, it calls Repo-service to reduce inventory.

This ultimate consistent transaction scheme based on message-oriented middleware is often misunderstood as:

The process of this implementation is as follows:

1) The ORder-service will send the deduction_msg message to the MQ server. The Repo-service subscribes to the destocking messages in the MQ Server and is responsible for the consumption messages.

2) After the user places the order, the ORder-service will first execute the query statement to insert the order record and then send rePO_deduction_MSG to the message-oriented middleware. These two processes will be carried out in a local transaction. Once “execute the query statement to insert the order record” fails, the transaction will roll back. “Send REPO_deduction_MSG to the message middleware” doesn’t happen; Similarly, if “send REPO_deduction_MSG to the message middleware” fails, an exception is thrown and the “execute the query that inserts the order record” operation is rolled back, and nothing happens.

3) After receiving rePO_deduction_MSG, the Repo-service will first execute the inventory deduction query statement, and then feed back the message consumption to MQ Sever to complete the ACK. These two processes will be carried out in a local transaction. Once the “execute the inventory deduction query statement” fails, the transaction will roll back. The “ACK to MQ Server completion” message will not happen. MQ Server, driven by Confirm mechanism, will continue to push this message to the Repo-service until the transaction is committed. Similarly, if an exception is thrown on the failure of an ACK to send a message to the MQ Server, the operation that caused the inventory deduction query to be executed is rolled back, and the MQ Server, driven by the Confirm mechanism, continues to push the message to the Repo-service until the entire transaction is committed.

This approach seems credible. But did not consider the existence of the network two general problem, there are the following defects:

1) There is a 2-general problem in the network. In step 2 above, the ORder-service fails to send the rePO_deduction_MSG message. For the sender, the order-Service, it may be that the message middleware has not received the message. It is also possible that the middleware receives the message, but the ACK that responds to the sender order-service is not received by the Order-service due to a network failure. So it would be wrong for the ORder-service to rush into a transaction rollback and undo “execute the query that inserted the order record” because the Repo-service might have received rePO_deduction_MSG and made a successful inventory deduction. This causes data inconsistency between the order-service and the Repo-service.

2) Repo-service and ORder-service put network calls (to communicate with MQ Server) in local database transactions, which may cause long database transactions due to network latency and affect the concurrency of local database transactions.

The above is the misunderstood implementation. Here is the correct implementation, as follows:

The scheme shown above uses messaging middleware such as rabbitMQ to achieve the ultimate consistency of distributed ordering and inventory reduction processes. Make the following remarks about the picture:

1) In order service,

Add the order record to the T_ORDER table && add the corresponding discount inventory message to t_LOCAL_msgCopy the code

These two processes must be completed in one transaction to ensure the atomicity of the process. Similarly, in a Repo-service,

Check whether the inventory deduction operation has been carried out && execute the inventory deduction operation. If the inventory deduction operation has not been carried out && write the weight judgment table && feedback message consumption completion ACK to MQ SeverCopy the code

These four processes must also be completed in one transaction to ensure atomicity of the process.

2) The order-service has a daemon that continuously sends messages from the message table to the message middleware, and deletes the corresponding messages from the message table after success. If it fails, retransmission attempts are made. Due to the network 2 general problem, when the message network sent by the order-service to the message middleware times out, the message middleware may receive the message but fail to respond to ACK, or may not receive it, and the order-service will send the message again. Until the message middleware responds to the ACK successfully, the message may be sent repeatedly, but that is ok, as long as the message is not lost, not out of order, then the Repo-service will do the reprocessing.

3) The message-oriented middleware pushes rePO_deduction_MSG to the Repo-service. After the successful processing, the Repo-Service will respond with an ACK to the middleware. The message-oriented middleware will consider that the Repo-Service has successfully processed the message only after receiving the ACK. Otherwise, the message will be pushed repeatedly. However, in this case, the repo-service successfully processes the message, and the ACK sent to the middleware is lost during network transmission due to a network fault. As a result, the middleware pushes the message again without receiving the ACK. This also depends on the message de-duplication feature of the Repo-service to avoid message re-consumption.

4) In 2) and 3), two reasons are mentioned for the repo-service to receive messages repeatedly: producer duplication and middleware retransmission. To achieve business idempotency, a replay table is maintained in the Repo-service, which records the ids of the messages that were successfully processed. Each time the repo-service receives a new message, it determines whether the message was successfully processed. If so, it does not process the message again.

Through this design, the message is not lost in the sender, the message is not repeated consumption in the receiver, together, the message is not leaked and not heavy, strict realization of the final consistency of data in the two databases of ORder-service and Repo-service.

The final consistency global transaction scheme based on message-oriented middleware is an innovative application mode explored by Internet companies in high concurrency scenarios. It uses MQ to realize asynchronous invocation, decoupling and traffic peak clipped-off between microservices, supports high concurrency of global transactions, and guarantees the final consistency of distributed data records.

3. Implementation of Seata in AT Mode

Chapter 2 presents a common centralized theoretical model for implementing distributed transactions. This chapter presents the implementation of Seata, an open source distributed transaction framework in the industry.

Seata provides users with AT, TCC, SAGA and XA transaction modes. Among them, AT mode is the transaction mode mainly promoted by Seata, so this chapter analyzes the implementation of Seata in AT Mode. One prerequisite for using AT is that the database used by the microservice must be a transactional relational database.

3.1. Overview of the workflow of Seata in AT Mode

Seata’s AT mode is based on the local transaction feature of relational databases. It intercepts and parses SQL executed by the database through the data source agent class, records custom rollback logs, and replay these custom rollback logs if rollback is required. Although the AT mode is evolved from XA transaction model (2PC), IT breaks the obstructive constraints of XA protocol and achieves a balance between consistency and performance.

The AT pattern evolved from the XA transaction model, and its overall mechanism is also an improved version of the two-phase commit protocol. The two basic phases of the AT pattern are:

1) Stage 1: First, obtain the local lock, execute the local transaction, commit the business data operation and record the rollback log in the same local transaction, and release the local lock at last;

2) Phase 2: Asynchronously delete the rollback log if global commit is required, and this process is quickly completed. If rollback is required, the rollback logs of the first phase are used to reverse compensation.

This chapter describes the working principle of Seata in AT Mode. The e-commerce micro-service model used is shown as the figure below:

In the figure above, the coordinator shopping-service first calls actor Repo-service to reduce inventory, and then calls actor Order-service to generate orders. The global transaction flow of this business flow using Seata in XA Mode is shown below:

The global transaction execution flow described in the figure above is:

1) Shopping -service registers global transactions with Seata and generates a global transaction ID XID

Repo_db; order-service.order_db; repo_db; The transaction contents include query operations on repo_db, order-service.order_DB and undo_log records written to each library

Repo_db and order-service. order_DB register branch transactions with Seata and bring them into the global transaction scope corresponding to the XID

4) Commit the local transaction of repo_db and order-service.order_db

5) Repo_DB and order-service. order_DB report the commit status of branch transactions to Seata

6) Seata summarizes the commit status of all DB branch transactions to determine whether the global transaction should be committed or rolled back

7) Seata tells repo_db and order-service. order_DB to commit/roll back the local transaction, and if it needs to be rolled back, it takes the compensation method

1) 2) 3) 4) 5) and 6) 7) belong to the second stage.

3.2. Detailed workflow of Seata in AT Mode

In the e-commerce business scenario above, the shopping service calls the inventory service to deduct the inventory and the order service to create the order. Obviously, the two calls need to be placed in one transaction. That is:

Start global_trx Inventory reduction interface for call order creation interface for call order service commit global_trxCopy the code

In the inventory service database, there is the following inventory table T_repO:

id production_code name count price
10001 20001 Xx keyboard 98 200.0
10002 20002 Yy mouse 199 100.0

In the order service database, there is the following order table T_ORDER:

id order_code user_id production_code count price
30001 2020102500001 40001 20002 1 100.0
30002 2020102500001 40001 20001 2 400.0

Now, the user with id 40002 wants to purchase a mouse with merchandise code 20002, and the contents of the entire distributed transaction are:

1) Will be recorded in the inventory table of the inventory service

id production_code name count price
10002 20002 Yy mouse 199 100.0

Modified to

id production_code name count price
10002 20002 Yy mouse 198 100.0

2) Add a record to the order table of the order service

id order_code user_id production_code count price
30003 2020102500002 40002 20002 1 100.0

The flow chart for the first phase of AT mode is as follows:

According to the flow of the first phase of AT mode, the local record locked by the local transaction will be released after the local transaction commits in the first phase. This is the biggest difference between THE AT mode and XA. In the two-phase commit of an XA transaction, locked records are not released until the end of the second phase. Therefore, AT mode reduces the locking record time and improves the processing efficiency of distributed transactions. The AT mode is able to release locked records upon completion of the first phase because Seata maintains an undo_log table in each service database that records mirror data before and after the operation on T_ORDER/t_repo. Even if an exception occurs in the second phase, Global rollback can be achieved by simply playing back the corresponding record in undo_log for each service.

Undo_log table structure:

id branch_id xid context rollback_info log_status log_created log_modified
Branch transaction ID Global transaction ID Records of branch transactions are mirrored before and after the transaction, namely beforeImage and afterImage

After the first phase, Seata receives the commit status of all branch transactions and decides whether to commit or roll back the global transaction.

1) If all branch transactions commit locally successfully, Seata decides to commit globally. Seata sends the branch commit message to each branch transaction, which, upon receiving the branch commit message, places it in a buffered queue and returns a successful commit directly to Seata. After that, each local transaction slowly processes the branch commit message by deleting the undo_log record for the corresponding branch transaction. The reason why you only need to delete the undo_log record of the branch transaction and do no more commit operations is because the commit operation is already done in the first phase (this is where AT differs from XA). This process is shown below:

The branch transaction is able to return success directly to Seata because the really critical commit has already been done in the first phase. Clearing undo_log is just finishing work, and even if the clearing fails, there is no real impact on the overall distributed transaction.

2) If any branch transaction fails to commit locally, Seata decides to roll back globally, sending the branch transaction rollback message to each branch transaction. Since unDO_log records are recorded on the database of each service in the first phase, the branch transaction rollback operation only needs to be compensated according to the UNDO_log records. The rollback process of global transactions is shown below:

Here is a further explanation of steps 2 and 3 in the figure:

1) As the undo_log table structure is given above, all undo_log records of the current branch transaction can be found by xID and branch_id;

2) After receiving the undo_log record of the current branch transaction, the data must be verified first. If the records in afterImage are inconsistent with the current table records, it means that another transaction has modified these records between the completion of the first phase and this time, which will cause the branch transaction to be unable to roll back and feedback to Seata that the rollback failed. If the records in afterImage are the same as the current table records, no other transactions have modified the records between the completion of the first phase and this point. Branch transactions can be rolled back, then compensating SQL is calculated based on beforeImage and afterImage, and the compensating SQL is executed for rollback. Then delete the corresponding undo_log and report back to Seata that the rollback is successful.

Transactions have ACID properties, and global transaction solutions try to implement these four properties. The above description of Seata in AT mode clearly reflects the atomicity, consistency and persistence of AT. The following focuses on how AT ensures isolation of multiple global transactions.

In AT, when multiple global transactions operate on the same table, the isolation of the transaction is guaranteed by a global lock. The following describes how global locks work in both read and write isolation scenarios:

1) Write isolation (if a global transaction is modifying/writing/deleting a record, the modification/writing/deleting of the same record by another global transaction shall be isolated, that is, write mutually exclusive) : Write isolation is designed to prevent data from being modified by other global transactions when multiple global transactions update the same field in the same table. The basic principle of write isolation is to ensure that a global lock is obtained before committing a phase 1 local transaction (which locks the records involved when a local transaction is enabled). If the global lock cannot be obtained, the transaction cannot be committed and attempts to obtain the global lock continue until the number of retries exceeds. The transaction is aborted, the local transaction is rolled back, and the local lock is released.

Suppose there are two global transactions, GTRx_1 and GTRx_2, concurrently operating the inventory service with the intention of deducting the inventory quantity recorded as follows:

id production_code name count price
10002 20002 Yy mouse 198 100.0

The sequence diagram of the write isolation process implemented by AT is as follows:

In the figure, 1, 2, 3 and 4 belong to the first stage, while 5 belongs to the second stage.

In the figure above, both GTRx_1 and GTRx_2 commit successfully. If GTRx_1 performs rollback in phase 2, then GTRx_1 needs to re-initiate the local transaction to acquire the local lock, and then compensate for the rollback of the record with ID =10002 according to undo_log. In this case, GTRX_2 is still waiting for the global lock and has the local lock for the record whose ID is 10002. Therefore, GTRX_1 will fail to roll back (GTRX_1 needs to hold both the global lock and the local lock for the record whose ID is 10002). Gtrx_1 that fails to roll back will try rolling back again. Until the number of attempts of gTRx_2 to obtain the global lock exceeds the threshold, GTRx_2 will give up obtaining the global lock and initiate a local rollback. When the local rollback is complete, the local lock added to the record whose ID is 10002 will be released. At this point, gTRx_1 can successfully add the local lock to the record with id=10002. Gtrx_1 can successfully roll back the record with local lock and global lock. The global lock is always in the hands of GTRX_1 and there is no dirty write problem. The flow chart of the whole process is as follows:

2) Read isolation (if a global transaction is modifying/writing/deleting a record, the reads of the same record by another global transaction are isolated, i.e. reads and writes are mutually exclusive) : In the local database transaction isolation level for the read committed and repeatable read, serialization (what can’t afford to read uncommitted isolation effect, generally do not use), Seata AT global transaction model have read uncommitted isolation level, that is a global transaction will see another global transaction is not submitted data, global produce dirty reads, You can also see this in the flowcharts for phase 1 and Phase 2 above. This is acceptable in an ultimately consistent distributed transaction model.

If the AT model is required to implement read committed transaction isolation levels, you can use Seata’s SelectForUpdateExecutor executor to proxy SELECT FOR UPDATE statements. The SELECT FOR UPDATE statement applies FOR a global lock during execution. If the global lock is already occupied by another global transaction, the execution of the SELECT FOR UPDATE statement is rolled back, the local lock is released, and the SELECT FOR UPDATE statement is retry. In this process, the query request is blocked until the global lock is acquired (that is, the record to be read is committed by another global transaction) and the data that has been committed by the global transaction is read. This process is shown below:

4. The conclusion

XA protocol is a distributed transaction processing standard proposed by X/Open. The essence of 2PC, 3PC, TCC, local transaction table, and Seata in AT Mode mentioned in this article is that the transaction coordinator coordinates the progress of local transactions of each transaction participant, so that all local transactions commit or roll back together, and finally achieve a global ACID feature. In the process of coordination, the coordinator needs to collect the current state of each local transaction and issue instructions for the next stage based on these states. This idea is at the heart of the XA protocol, and we can say that these transaction models comply, or roughly comply, with the XA protocol.

The final consistency transaction scheme based on message-oriented middleware is an innovative application mode explored by Internet companies in high concurrency scenarios. MQ is used to realize asynchronous invocation, decoupling and traffic peak cutting between microservices to ensure the final consistency of distributed data records. It clearly does not comply with XA.

For a certain technology, there may be industry standards or protocols, but for the needs of specific application scenarios or for the sake of simplicity, practitioners will give implementation that is not completely consistent with the standard, or even completely inconsistent with the implementation, which is a common phenomenon in the engineering field. This is true for the TCC scheme, for the ultimate consistency transaction scheme based on message-oriented middleware, and for the Seata in AT Mode mode. New standards often emerge from these innovations.

. . . . . .

Did you really not see the business holes in the correct scenario given in Section 2.6 (The Ultimate Consistent Transaction Scenario based on Message-Oriented Middleware)? Please take a look at this image and take a closer look at each of the two microservices and leave your thoughts in the comments 🙂