What is TCC? TCC is the abbreviation of Try, Confirm and Cancel. It was first proposed by Pat Helland in a paper entitled "Life beyond Distributed Transactions: an Apostate's Opinion" published in 2007.
TCC composition
TCC is divided into 3 stages
- Try phase: try to execute, complete all business checks (consistency), reserve necessary business resources (quasi-isolation)
- Confirm phase: If the Try of all branches is successful, go to the Confirm phase. Confirm actually executes the business without any business inspections, and only uses the business resources reserved during the Try phase
- Cancel phase: If one of the Try of all branches fails, go to the Cancel phase. Cancel releases the business resources reserved in the Try phase.
In TCC distributed transaction, there are 3 roles, the same as the classic XA distributed transaction:
- AP/application, initiate a global transaction, define which transaction branches the global transaction contains
- RM/resource manager, responsible for the management of various resources of branch affairs
- TM/transaction manager, responsible for coordinating the correct execution of global transactions, including the execution of Confirm and Cancel, and handling network exceptions
If we want to conduct a business similar to bank inter-bank transfer, the transfer out (TransOut) and transfer in (TransIn) are in different microservices. A typical sequence diagram of a successfully completed TCC transaction is as follows:
TCC practice
Below we carry out the specific development of a TCC transaction
The distributed transaction framework used in our example is dtm, which supports distributed transactions very elegantly. Let's explain the composition of TCC in detail below
Let's write a specific Try/Confirm/Cancel processing function
@RequestMapping("TransOutTry")
public Map<String, String> TransOutTry() {
logger.info("TransOutTry");
Map<String, String> result = new HashMap<>();
result.put("dtm_result", "SUCCESS");
return result;
}
@RequestMapping("TransOutConfirm")
public Map<String, String> TransOutConfirm(HttpServerResponse response) {
logger.info("TransOutConfirm");
Map<String, String> result = new HashMap<>();
result.put("dtm_result", "SUCCESS");
return result;
}
@RequestMapping("TransOutCancel")
public Map<String, String> TransOutCancel() {
logger.info("TransOutCancel");
Map<String, String> result = new HashMap<>();
result.put("dtm_result", "SUCCESS");
return result;
}
@RequestMapping("TransInTry")
public Map<String, String> TransInTry() {
logger.info("TransInTry");
Map<String, String> result = new HashMap<>();
result.put("dtm_result", "SUCCESS");
return result;
}
@RequestMapping("TransInConfirm")
public Map<String, String> TransInConfirm() {
logger.info("TransInConfirm");
Map<String, String> result = new HashMap<>();
result.put("dtm_result", "SUCCESS");
return result;
}
@RequestMapping("TransInCancel")
public Map<String, String> TransInCancel() {
logger.info("TransInCancel");
Map<String, String> result = new HashMap<>();
result.put("dtm_result", "SUCCESS");
return result;
}
At this point, the processing function of each sub-transaction has been OK, and then the TCC transaction is opened, and the branch call is made
@RequestMapping("fireTcc")
public String fireTcc() {
Function<Tcc, Boolean> function = TccController::tccTrans;
return tcc.tccGlobalTransaction(function);
}
public static Boolean tccTrans(Tcc tcc) {
try {
boolean a = tcc.callBranch("", svc + "/TransOutTry", svc + "/TransOutConfirm", svc + "/TransOutCancel");
boolean b = tcc.callBranch("", svc + "/TransInTry", svc + "/TransInConfirm", svc + "/TransInCancel");
return a && b;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
At this point, a complete TCC distributed transaction has been written.
If you want to run a successful sample completely, then refer to this example yedf/dtmcli-java-sample, it is very simple to run it
# 部署启动dtm
# 需要docker版本18以上
git clone https://github.com/yedf/dtm
cd dtm
docker-compose up
# 另起一个命令行
git clone https://github.com/yedf/dtmcli-java-sample.git
cd dtmcli-java-sample
# 编译运行例子 main/src/main/java/com/github/viticis/dtmclijavaexamples/DtmcliJavaSampleApplication
TCC rollback
What happens if the bank finds that the account of user 2 is abnormal when the bank is about to transfer the amount to user 2 and the return fails? We can make TransIn return failure to simulate this situation
@RequestMapping("TransInTry")
public Map<String, String> TransInTry() {
logger.info("TransInTry");
Map<String, String> result = new HashMap<>();
result.put("dtm_result", "FAILURE");
return result;
}
We give a sequence diagram of transaction failure interaction
The difference between this and a successful TCC is that when a sub-transaction returns to failure, the global transaction is subsequently rolled back, and the Cancel operation of each sub-transaction is called to ensure that all the global transactions are rolled back.
In the TCC transaction mode, many readers will ask, what happens if Confirm/Cancel fails? This is a good question, which means that you are thinking deeply about the TCC transaction model. The first case is a temporary failure, such as a network failure, application or database downtime. This type of error is retried and finally returns success; the other case is a business failure. According to the TCC agreement, the first phase locks resources to ensure Sufficient resources can allow Confirm/Cancel to be executed. That is to say, in the logic of the program, Confirm/Cancel is not allowed to return to business failure. If there is a business failure, it is a bug, and the developer needs to fix the bug manually.
summary
In this article, we introduced the theoretical knowledge of TCC, and through an example, gave a complete process of writing a TCC transaction, covering the normal successful completion and successful rollback. I believe that readers have an in-depth understanding of TCC through this article.
For more comprehensive knowledge of distributed transactions, please refer to "Seven Classic Solutions for Distributed Transactions"
The examples used in the article are excerpted from yedf/dtm , which supports multiple transaction modes: TCC, SAGA, XA, transaction message cross-language support, and has supported clients in languages such as golang, python, Java, PHP, nodejs, etc., refer to each language. Provide sub-transaction barrier function, elegantly solve the problems of idempotence, suspension, null compensation and so on.
After reading this dry goods, welcome everyone to visit the https://github.com/yedf/dtm project, give star support!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。