Quick Start

Start Project

  1. Configure distributed-transaction.yaml in the configuration center with the corresponding environment information.

  2. Configure distributed-transaction-execution.json in the configuration center and fill in the relevant TCC execution configuration.

  3. Execute script/db/init.sql in the MySQL database to initialize the tables.

  4. Clone this project and replace the configuration center information in distributed-transaction-bootstrap/src/main/resources/environment/application.yaml, or configure the corresponding information in project environment variables.

  5. Start tech.finovy.transaction.bootstrap.DistributedTransactionApplication.

Configuration Reference

  1. distributed-transaction.yaml

    Purpose: Used for basic project configuration.

    spring: 
      # Database configuration
      datasource:
        url: url
        username: username
        password: password
    
    distributed:
      data-id:
        # TCC task execution list, default is distributed-transaction-execution.json
        execution: distributed-transaction-execution.json
    
  2. distributed-transaction-execution.json

    Purpose: Used for configuring the TCC execution task list.

    {
        "cache": false,
        "database": false,
        "debug": false,
        "taskFlow": [
            {
                "async": true,
                "call": [
                    {   
                        # lb indicates load balancing within the same service discovery namespace
                        "api": "lb://user/",
                        "prepare": "prepare",
                        "rollback": "rollback"
                    },
                    {
                        "api": "http://github.com/",
                        "prepare": "prepare",
                        "rollback": "rollback"
                    }
                ],
                "debug": true,
                "key": "tcc-analysis",
                "timeout": 10000
            }
        ]
    }
    

Configuration Details:

Parameter Description Data Type Default Value Required
cache Whether to use cache for logging records boolean false No
database Whether to use a database for logging records boolean false No
debug Whether to enable debug log output boolean false No
taskFlow[0].async Whether to execute the Prepare phase concurrently boolean false No
taskFlow[0].debug Whether to enable debug log output boolean false No
taskFlow[0].key Unique key to identify the task string true Yes
taskFlow[0].timeout Timeout for the call int 10000 No
taskFlow[0].call[0].api TCC client API string Yes
taskFlow[0].call[0].prepare Name of the prepare phase method string prepare No
taskFlow[0].call[0].commit Name of the commit phase method string commit No
taskFlow[0].call[0].rollback Name of the rollback phase method string rollback No

How to Integrate the Client

1. Add Dependency

<dependency>
      <groupId>tech.finovy</groupId>
      <artifactId>distributed-transaction-client-starter</artifactId>
      <version>0.1.0-SNAPSHOT</version>
</dependency>

2. Implement the Interface

(Must be at the database level) tech.finovy.transaction.client.api.TccClientService

package tech.finovy.transaction.client.api;

import com.alibaba.fastjson.TypeReference;

public interface TccClientService<T> {

    boolean prepare(T t);

    boolean commit(T t);

    boolean rollback(T t);

    String getTypeName();

    TypeReference<T> getType();

}

Note:

T: Represents the encapsulated business input parameter object.

typeName: Represents the fundamental business unit name, such as “deposit” for deposit transactions.

package tech.finovy.transaction.client;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import tech.finovy.transaction.client.api.TccClientService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class TccClientSingleServiceImpl implements TccClientService<TccInfo> {

    @Override
    public boolean prepare(TccInfo o) {
        log.info("{} prepare done {}", getTypeName(), JSON.toJSONString(o));
        return true;
    }

    @Override
    public boolean commit(TccInfo o) {
        log.info("{} commit done {}", getTypeName(), JSON.toJSONString(o));
        return true;
    }

    @Override
    public boolean rollback(TccInfo o) {
        log.info("{} rollback done {}", getTypeName(), JSON.toJSONString(o));
        return true;
    }

    @Override
    public String getTypeName() {
        return "single-test";
    }


    @Override
    public TypeReference<TccInfo> getType() {
        return new TypeReference<>() {
        };
    }
}

Important Note:

If the data access layer provides Dubbo interfaces for external access, which means accessing the data layer through HTTP interfaces via Dubbo, you need to add the following configuration in the data layer:

sharding-engine:  
  tcc:  
    client:  
      group: user

Indicating the current Dubbo interface’s group. The API format mentioned above will be: http://{baseurl}/tcc/{typeName}/{group}.

3. Initiating a Transaction

  • Inject Dependencies:

@Autowired
TccActionService tccActionService;
  • Business Invocation, Initiating a Transaction:

 <T> boolean launch(String typeName, Map<String, String> headers, T t);

Note:

  • typeName: Business process name.

  • headers: Request headers for multi-tenancy support.

  • T: Data passed throughout the entire transaction. Here, you need to pass a Map, and relevant data will be passed in according to the corresponding key at each stage.

Example:

final HashMap<String, Object> body = new HashMap<>();
// Use specific data for each stage
body.put("stepA", audits);
body.put("stepB", deposits);

final HashMap<String, String> headers = new HashMap<>(1);
// Multi-tenancy compatibility
headers.put("tenantKey", "UserA");

tccActionService.launch(typeName, headers, body);

4. Adding Execution Configuration

To add transaction execution tasks, locate the distributed-transaction-execution.json file.

{
    "call": [
        {
            "api": "https://finovy.tech/tcc/stepA/User"
        },
        {
            "api": "https://finovy.tech/tcc/stepB/Fund"
        }
    ]
}

5. Initializing the tcc_fence_log Table

Purpose: Recording the execution status of local transactions.

Procedure: Initialize the following table in the database corresponding to each client:

CREATE TABLE `tcc_fence_log` (
  `xid` varchar(128) NOT NULL COMMENT 'global id',
  `branch_id` bigint(20) NOT NULL COMMENT 'branch id',
  `action_name` varchar(64) NOT NULL COMMENT 'action name',
  `status` tinyint(4) NOT NULL COMMENT 'status(tried:1;committed:2;rollbacked:3;suspended:4)',
  `gmt_create` datetime(3) NOT NULL COMMENT 'create time',
  `gmt_modified` datetime(3) NOT NULL COMMENT 'update time',
  PRIMARY KEY (`xid`,`branch_id`),
  KEY `idx_gmt_modified` (`gmt_modified`),
  KEY `idx_status` (`status`),
  KEY `idx_normal` (`xid`,`branch_id`,`status`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

6. Adding tcc-server-clusters.json

Purpose: Identifying available clusters for the server.

Example:

[
    {
        "name": "DC-SZ",
        "order": 1,
        "url": "http://127.0.0.1:8080"
    },
    {
        "name": "DC-HK",
        "order": 2,
        "url": "http://127.0.0.1:8080"
    }
]
Parameter Description
name Unique identifier name
order Priority, lower values indicate higher priority (must be >0)
url Cluster connection URL