diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 0000000..06ea3c7
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,70 @@
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
+#
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
+#
+# ******** NOTE ********
+# We have attempted to detect the languages in your repository. Please check
+# the `language` matrix defined below to confirm you have the correct set of
+# supported CodeQL languages.
+#
+name: "CodeQL"
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ # The branches below must be a subset of the branches above
+ branches: [ main ]
+ schedule:
+ - cron: '25 19 * * 2'
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'go' ]
+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
+ # Learn more about CodeQL language support at https://git.io/codeql-language-support
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v1
+ with:
+ languages: ${{ matrix.language }}
+ # If you wish to specify custom queries, you can do so here or in a config file.
+ # By default, queries listed here will override any specified in a config file.
+ # Prefix the list here with "+" to use these queries and those in the config file.
+ # queries: ./path/to/local/query, your-org/your-repo/queries@main
+
+ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
+ # If this step fails, then you should remove it and run the build manually (see below)
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v1
+
+ # ℹ️ Command-line programs to run using the OS shell.
+ # 📚 https://git.io/JvXDl
+
+ # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
+ # and modify them (or add more) to build your code if your project
+ # uses a compiled language
+
+ #- run: |
+ # make bootstrap
+ # make release
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v1
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 0000000..f0495e2
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,48 @@
+name: Tests
+on:
+ push:
+ branches-ignore:
+ - 'tmp-*'
+ pull_request:
+ branches-ignore:
+ - 'tmp-*'
+
+jobs:
+ tests:
+ name: CI
+ runs-on: ubuntu-latest
+ services:
+ mysql:
+ image: 'mysql:5.7'
+ env:
+ MYSQL_ALLOW_EMPTY_PASSWORD: 1
+ volumes:
+ - /etc/localtime:/etc/localtime:ro
+ - /etc/timezone:/etc/timezone:ro
+ ports:
+ - 3306:3306
+ reids:
+ image: 'redis'
+ volumes:
+ - /etc/localtime:/etc/localtime:ro
+ - /etc/timezone:/etc/timezone:ro
+ ports:
+ - 6379:6379
+ steps:
+ - name: Set up Go 1.16
+ uses: actions/setup-go@v2
+ with:
+ go-version: '1.16'
+
+ - name: Check out code
+ uses: actions/checkout@v2
+
+ - name: Install dependencies
+ run: |
+ go mod download
+
+ - name: Run CI lint
+ run: make lint
+
+ - name: Run test cover
+ run: sh helper/test-cover.sh
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index ca6b347..34e44f0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,10 +5,14 @@ conf.yml
main
dist
.idea/**
-.vscode/*.json
+.vscode
default.etcd
*/**/*.bolt
# Output file of unit test coverage
-coverage.out
-coverage.out.tmp
\ No newline at end of file
+coverage.*
+profile.*
+test.sh
+dtm
+dtm-*
+dtm.*
diff --git a/.golangci.yml b/.golangci.yml
index 78a6813..58479dc 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -2,7 +2,7 @@ run:
deadline: 5m
skip-dirs:
- test
- - examples
+# - bench
linter-settings:
goconst:
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 5fde38f..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-language: go
-go:
- - 1.15.x
-env:
- - GO111MODULE=on
-branches:
- only:
- - master
- - main
- - alpha
-services:
- - mysql
- - redis-server
-before_install:
- - go get -t -v ./...
- - go get github.com/yedf2/goveralls
-script:
- - $GOPATH/bin/goveralls -envs=TEST_STORE=redis,TEST_STORE=mysql,TEST_STORE=boltdb -flags '-gcflags=-l' -service=travis-ci -ignore="examples/*,dtmgrpc/dtmgimp/*.pb.go,bench/*,test/*"
diff --git a/.vscode/launch.json.sample b/.vscode/launch.json.sample
deleted file mode 100644
index 7cf371c..0000000
--- a/.vscode/launch.json.sample
+++ /dev/null
@@ -1,33 +0,0 @@
-{
- // 使用 IntelliSense 了解相关属性。
- // 悬停以查看现有属性的描述。
- // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
- "version": "0.2.0",
- "configurations": [
- {
- "name": "Launch",
- "type": "go",
- "request": "launch",
- "mode": "debug",
- "program": "${workspaceFolder}/app/main.go",
- "cwd": "${workspaceFolder}",
- "env": {
- // "GIN_MODE": "release"
- },
- "args": ["grpc_saga"]
- },
- {
- "name": "Test",
- "type": "go",
- "request": "launch",
- "mode": "test",
- "port": 2345,
- "host": "127.0.0.1",
- "program": "${file}",
- "env": {
- // "GIN_MODE": "release"
- },
- "args": []
- }
- ]
-}
\ No newline at end of file
diff --git a/.vscode/settings.json.sample b/.vscode/settings.json.sample
deleted file mode 100644
index 079a897..0000000
--- a/.vscode/settings.json.sample
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "go.formatTool": "gofmt"
-}
\ No newline at end of file
diff --git a/README-cn.md b/README-cn.md
index 8f866fc..b616b87 100644
--- a/README-cn.md
+++ b/README-cn.md
@@ -1,8 +1,8 @@
-
-[](https://travis-ci.com/yedf/dtm)
-[](https://coveralls.io/github/yedf/dtm?branch=main)
-[](https://goreportcard.com/report/github.com/yedf/dtm)
-[](https://pkg.go.dev/github.com/yedf/dtm)
+
+
+[](https://codecov.io/gh/dtm-labs/dtm)
+[](https://goreportcard.com/report/github.com/dtm-labs/dtm)
+[](https://pkg.go.dev/github.com/dtm-labs/dtm)
[](https://github.com/avelino/awesome-go#database)
# [English Docs](https://en.dtm.pub)
@@ -12,87 +12,78 @@ DTM是一款golang开发的分布式事务管理器,解决了跨数据库、
他优雅的解决了幂等、空补偿、悬挂等分布式事务难题,提供了简单易用、高性能、易水平扩展的解决方案。
-作者受邀参加中国数据库大会分享[多语言环境下分布式事务实践](http://dtcc.it168.com/yicheng.html#b9)
+## 谁在使用DTM(仅列出部分)
+[Tencent 腾讯](https://dtm.pub/other/using.html#tencent)
-## 谁在使用dtm
-[Tencent 腾讯](https://www.tencent.com/)
+[Ivydad 常青藤爸爸](https://dtm.pub/other/using.html#ivydad)
-[Ivydad 常青藤爸爸](https://ivydad.com)
+[Eglass 视咖镜小二](https://dtm.pub/other/using.html)
-[Eglass 视咖镜小二](https://epeijing.cn)
-
-[极欧科技](http://jiou.me)
-
-[金数智联]()
+[极欧科技](https://dtm.pub/other/using.html)
## 亮点
* 极易接入
- - 支持HTTP,提供非常简单的接口,极大降低上手分布式事务的难度,新手也能快速接入
-* 使用简单
- - 开发者不再担心悬挂、空补偿、幂等各类问题,框架层代为处理
+ - 零配置启动服务,提供非常简单的HTTP接口,极大降低上手分布式事务的难度,新手也能快速接入
* 跨语言
- 可适合多语言栈的公司使用。方便go、python、php、nodejs、ruby、c# 各类语言使用。
+* 使用简单
+ - 开发者不再担心悬挂、空补偿、幂等各类问题,首创子事务屏障技术代为处理
* 易部署、易扩展
- - 仅依赖mysql,部署简单,易集群化,易水平扩展
+ - 依赖mysql|redis,部署简单,易集群化,易水平扩展
* 多种分布式事务协议支持
- - TCC、SAGA、XA、事务消息
+ - TCC、SAGA、XA、二阶段消息,一站式解决所有分布式事务问题
## 与其他框架对比
-目前开源的分布式事务框架,Java的框架较多,有大厂开源的SEATA、ServiceComb-Pack,shardingsphere,以及个人开源的himly,tcc-transaction,ByteTCC等等,其中以Seata的应用最为广泛。
-
-非Java语言类的,暂未看到除dtm之外的成熟框架,因此这里仅将DTM和Java中最成熟的Seata对比:
+非Java语言类的,暂未看到除dtm之外的成熟框架,因此这里将DTM和Java中最成熟的Seata对比:
| 特性| DTM | SEATA |备注|
|:-----:|:----:|:----:|:----:|
-| 支持语言 |Go、Java、python、php、c#...|Java|dtm可轻松接入一门新语言|
-|异常处理| [子事务屏障自动处理](https://zhuanlan.zhihu.com/p/388444465) |手动处理 |dtm解决了幂等、悬挂、空补偿|
-| TCC事务| ✓|✓||
-| XA事务|✓|✓||
-|AT事务|建议使用XA|✓|AT与XA类似,性能更好,但有脏回滚|
-| SAGA事务 |支持并发 |状态机模式 ||
-|事务消息|✓|✗|dtm提供类似rocketmq的事务消息|
-|单服务多数据源|✓|✗||
-|通信协议|HTTP、gRPC、go-zero|dubbo等协议|dtm对云原生更加友好|
-|star数量|
|
|dtm从20210604发布0.1,发展快|
+| [支持语言](https://dtm.pub/other/opensource.html#lang) |Go、Java、python、php、c#...|Java|dtm可轻松接入一门新语言|
+|[异常处理](https://dtm.pub/other/opensource.html#exception)| 子事务屏障自动处理 |手动处理 |dtm解决了幂等、悬挂、空补偿|
+| [TCC事务](https://dtm.pub/other/opensource.html#tcc)| ✓|✓||
+| [XA事务](https://dtm.pub/other/opensource.html#xa)|✓|✓||
+|[AT事务](https://dtm.pub/other/opensource.html#at)|建议使用XA|✓|AT与XA类似,性能更好,但有脏回滚|
+| [SAGA事务](https://dtm.pub/other/opensource.html#saga) |支持并发 |状态机模式 ||
+|[二阶段消息](https://dtm.pub/other/opensource.html#msg)|✓|✗|dtm提供类似rocketmq的事务消息|
+|[单服务多数据源](https://dtm.pub/other/opensource.html#multidb)|✓|✗||
+|[通信协议](https://dtm.pub/other/opensource.html#protocol)|HTTP、gRPC、go-zero|dubbo等协议|dtm对云原生更加友好|
+|[star数量](https://dtm.pub/other/opensource.html#star)|
|
|dtm从20210604发布0.1,发展快|
从上面对比的特性来看,如果您的语言栈包含了Java之外的语言,那么dtm是您的首选。如果您的语言栈是Java,您也可以选择接入dtm,使用子事务屏障技术,简化您的业务编写。
+详细的对比可以点击特性中的链接,跳到相关文档
## [性能测试报告](https://dtm.pub/other/performance.html)
## [教程与文档](https://dtm.pub)
-## [各语言客户端及示例](https://dtm.pub/summary/code.html#go)
+## [各语言客户端及示例](https://dtm.pub/ref/sdk.html#go)
## 微服务框架支持
- [go-zero](https://github.com/zeromicro/go-zero):一开源就非常火爆的微服务框架,首家接入dtm的微服务框架。感谢go-zero作者[kevwan](https://github.com/kevwan)的大力支持
- [polaris](https://github.com/polarismesh/polaris): 腾讯开源的注册发现组件,以及在其上构建的微服务框架。感谢腾讯同学[ychensha](https://github.com/ychensha)的PR
-- 其他:看用户需求量,择机接入
+- 其他:看用户需求量,择机接入,参见[微服务支持](https://dtm.pub/ref/proto.html)
-具体微服务接入使用,参见[微服务支持](https://dtm.pub/protocol/intro.html)
## 快速开始
-### 获取代码
-
-`git clone https://github.com/yedf/dtm && cd dtm`
+如果您不是Go语言,可以跳转[各语言客户端及示例](https://dtm.pub/ref/sdk.html#go),里面有相关的快速开始示例
-### dtm依赖于mysql
+### 运行dtm
-安装[docker 20.04+](https://docs.docker.com/get-docker/)之后
-
-`docker-compose -f helper/compose.mysql.yml up`
+``` bash
+git clone https://github.com/dtm-labs/dtm && cd dtm
+go run main.go
+```
-> 您也可以配置使用现有的mysql,需要高级权限,允许dtm创建数据库
->
-> `cp conf.sample.yml conf.yml # 修改conf.yml`
+### 启动并运行一个saga示例
+下面运行一个类似跨行转账的示例,包括两个事务分支:资金转出(TransOut)、资金转入(TransIn)。DTM保证TransIn和TransOut要么全部成功,要么全部回滚,保证最终金额的正确性。
-### 启动并运行saga示例
-`go run app/main.go qs`
+`go run qs/main.go`
-## 开始使用
+## 接入详解
-### 使用
+### 接入代码
``` GO
// 具体业务微服务地址
const qsBusi = "http://localhost:8081/api/busi_saga"
@@ -116,23 +107,34 @@ DTM是一款golang开发的分布式事务管理器,解决了跨数据库、
-### 完整示例
-参考[examples/quick_start.go](./examples/quick_start.go)
+### 失败情况
+在实际的业务中,子事务可能出现失败,例如转入的子账号被冻结导致转账失败。我们对业务代码进行修改,让TransIn的正向操作失败,然后看看结果
-## 公众号
-您可以关注公众号:分布式事务,及时跟踪dtm的最新内容
-## 交流群
+``` go
+ app.POST(qsBusiAPI+"/TransIn", common.WrapHandler2(func(c *gin.Context) interface{} {
+ return dtmcli.ErrFailure
+ }))
+```
+
+再运行这个例子,整个事务最终失败,时序图如下:
+
+
+
+在转入操作失败的情况下,TransIn和TransOut的补偿操作被执行,保证了最终的余额和转账前是一样的。
+
+### 更多示例
+参考[dtm-labs/dtm-examples](https://github.com/dtm-labs/dtm-examples)
+
+## 联系我们
+### 公众号
+dtm官方公众号:分布式事务,大量干货分享,以及dtm的最新消息
+### 交流群
请加 yedf2008 好友或者扫码加好友,验证回复 dtm 按照指引进群

-欢迎使用[dtm](https://github.com/yedf/dtm),或者通过dtm学习实践分布式事务相关知识,欢迎star支持我们
+### github
+作者github: [https://github.com/yedf2](https://github.com/yedf2)
+
+欢迎使用[dtm](https://github.com/dtm-labs/dtm),或者通过dtm学习实践分布式事务相关知识,欢迎star支持我们
-## 谁在使用
-
diff --git a/README-en.md b/README-en.md
index 4f547b9..a83160f 100644
--- a/README-en.md
+++ b/README-en.md
@@ -1,8 +1,8 @@
-
-[](https://travis-ci.com/yedf/dtm)
-[](https://coveralls.io/github/yedf/dtm?branch=main)
-[](https://goreportcard.com/report/github.com/yedf/dtm)
-[](https://pkg.go.dev/github.com/yedf/dtm)
+
+
+[](https://codecov.io/gh/dtm-labs/dtm)
+[](https://goreportcard.com/report/github.com/dtm-labs/dtm)
+[](https://pkg.go.dev/github.com/dtm-labs/dtm)
[](https://github.com/avelino/awesome-go#database)
# [中文文档](http://dtm.pub)
@@ -62,7 +62,7 @@ The following is a comparison of the main features of dtm and Seata.
| Transactional Messaging | ✓ | ✗ | dtm provides Transactional Messaging similar to RocketMQ |
| Multiple DBs in a service |✓|✗||
| Communication protocols | HTTP, gRPC | Dubbo, no HTTP | |
-| Star count |
|
| dtm 0.1 is released from 20210604 and under fast development |
+| Star count |
|
| dtm 0.1 is released from 20210604 and under fast development |
From the features' comparison above, if your language stack includes languages other than Java, then dtm is the one for you.
If your language stack is Java, you can also choose to access dtm and use sub-transaction barrier technology to simplify your business development.
@@ -73,7 +73,7 @@ If your language stack is Java, you can also choose to access dtm and use sub-tr
### Install
-`git clone https://github.com/yedf/dtm`
+`git clone https://github.com/dtm-labs/dtm`
### Configure Mysql
diff --git a/README.md b/README.md
index 4f547b9..b616b87 100644
--- a/README.md
+++ b/README.md
@@ -1,128 +1,140 @@
-
-[](https://travis-ci.com/yedf/dtm)
-[](https://coveralls.io/github/yedf/dtm?branch=main)
-[](https://goreportcard.com/report/github.com/yedf/dtm)
-[](https://pkg.go.dev/github.com/yedf/dtm)
+
+
+[](https://codecov.io/gh/dtm-labs/dtm)
+[](https://goreportcard.com/report/github.com/dtm-labs/dtm)
+[](https://pkg.go.dev/github.com/dtm-labs/dtm)
[](https://github.com/avelino/awesome-go#database)
-# [中文文档](http://dtm.pub)
+# [English Docs](https://en.dtm.pub)
+# 跨语言分布式事务管理器
-# A Cross Language Distributed Transaction Manager
+DTM是一款golang开发的分布式事务管理器,解决了跨数据库、跨服务、跨语言栈更新数据的一致性问题。
-## Who's using DTM
+他优雅的解决了幂等、空补偿、悬挂等分布式事务难题,提供了简单易用、高性能、易水平扩展的解决方案。
-[Tencent](https://www.tencent.com/)
+## 谁在使用DTM(仅列出部分)
+[Tencent 腾讯](https://dtm.pub/other/using.html#tencent)
-[Ivydad](https://ivydad.com)
+[Ivydad 常青藤爸爸](https://dtm.pub/other/using.html#ivydad)
-[Eglass](https://epeijing.cn)
+[Eglass 视咖镜小二](https://dtm.pub/other/using.html)
-[Jiou](http://jiou.me)
+[极欧科技](https://dtm.pub/other/using.html)
-[GoldenData]()
+## 亮点
-## What is DTM
+* 极易接入
+ - 零配置启动服务,提供非常简单的HTTP接口,极大降低上手分布式事务的难度,新手也能快速接入
+* 跨语言
+ - 可适合多语言栈的公司使用。方便go、python、php、nodejs、ruby、c# 各类语言使用。
+* 使用简单
+ - 开发者不再担心悬挂、空补偿、幂等各类问题,首创子事务屏障技术代为处理
+* 易部署、易扩展
+ - 依赖mysql|redis,部署简单,易集群化,易水平扩展
+* 多种分布式事务协议支持
+ - TCC、SAGA、XA、二阶段消息,一站式解决所有分布式事务问题
-DTM is the first distributed transaction management framework in Golang. Unlike other frameworks, DTM provides extremely easy access interfaces of HTTP and gRPC, supports multiple language bindings, and handles tricky problems of unordered sub-transactions at the framework level.
+## 与其他框架对比
-## Features
+非Java语言类的,暂未看到除dtm之外的成熟框架,因此这里将DTM和Java中最成熟的Seata对比:
-* Extremely easy to adapt
- - Support HTTP and gRPC, provide easy-to-use programming interfaces, lower substantially the barrier of getting started with distributed transactions. Newcomers can adapt quickly.
+| 特性| DTM | SEATA |备注|
+|:-----:|:----:|:----:|:----:|
+| [支持语言](https://dtm.pub/other/opensource.html#lang) |Go、Java、python、php、c#...|Java|dtm可轻松接入一门新语言|
+|[异常处理](https://dtm.pub/other/opensource.html#exception)| 子事务屏障自动处理 |手动处理 |dtm解决了幂等、悬挂、空补偿|
+| [TCC事务](https://dtm.pub/other/opensource.html#tcc)| ✓|✓||
+| [XA事务](https://dtm.pub/other/opensource.html#xa)|✓|✓||
+|[AT事务](https://dtm.pub/other/opensource.html#at)|建议使用XA|✓|AT与XA类似,性能更好,但有脏回滚|
+| [SAGA事务](https://dtm.pub/other/opensource.html#saga) |支持并发 |状态机模式 ||
+|[二阶段消息](https://dtm.pub/other/opensource.html#msg)|✓|✗|dtm提供类似rocketmq的事务消息|
+|[单服务多数据源](https://dtm.pub/other/opensource.html#multidb)|✓|✗||
+|[通信协议](https://dtm.pub/other/opensource.html#protocol)|HTTP、gRPC、go-zero|dubbo等协议|dtm对云原生更加友好|
+|[star数量](https://dtm.pub/other/opensource.html#star)|
|
|dtm从20210604发布0.1,发展快|
-* Easy to use
- - Relieving developers from worrying about suspension, null compensation, idempotent transaction, and other tricky problems, the framework layer handles them all.
+从上面对比的特性来看,如果您的语言栈包含了Java之外的语言,那么dtm是您的首选。如果您的语言栈是Java,您也可以选择接入dtm,使用子事务屏障技术,简化您的业务编写。
-* Language-agnostic
- - Suit for companies with multiple-language stacks.
- Easy to write bindings for Go, Python, PHP, Node.js, Ruby, and other languages.
+详细的对比可以点击特性中的链接,跳到相关文档
+## [性能测试报告](https://dtm.pub/other/performance.html)
-* Easy to deploy, easy to extend
- - DTM depends only on MySQL, easy to deploy, cluster, and scale horizontally.
+## [教程与文档](https://dtm.pub)
-* Support for multiple distributed transaction protocol
- - TCC, SAGA, XA, Transactional messages.
+## [各语言客户端及示例](https://dtm.pub/ref/sdk.html#go)
-## DTM vs. others
+## 微服务框架支持
+- [go-zero](https://github.com/zeromicro/go-zero):一开源就非常火爆的微服务框架,首家接入dtm的微服务框架。感谢go-zero作者[kevwan](https://github.com/kevwan)的大力支持
+- [polaris](https://github.com/polarismesh/polaris): 腾讯开源的注册发现组件,以及在其上构建的微服务框架。感谢腾讯同学[ychensha](https://github.com/ychensha)的PR
+- 其他:看用户需求量,择机接入,参见[微服务支持](https://dtm.pub/ref/proto.html)
-There is no mature open-source distributed transaction framework for non-Java languages.
-Mature open-source distributed transaction frameworks for Java language include Ali's Seata, Huawei's ServiceComb-Pack, Jingdong's shardingsphere, himly, tcc-transaction, ByteTCC, and so on, of which Seata is most widely used.
+## 快速开始
-The following is a comparison of the main features of dtm and Seata.
+如果您不是Go语言,可以跳转[各语言客户端及示例](https://dtm.pub/ref/sdk.html#go),里面有相关的快速开始示例
+### 运行dtm
-| Features | DTM | Seata | Remarks |
-| :-----: | :----: | :----: | :----: |
-| Supported languages | Golang, Python, PHP, and others | Java | dtm allows easy access from a new language |
-| Exception handling | [Sub-transaction barrier](https://zhuanlan.zhihu.com/p/388444465) | manual | dtm solves idempotent transaction, hanging, null compensation |
-| TCC | ✓ | ✓ | |
-| XA | ✓ | ✓ | |
-| AT | suggest XA | ✓ | AT is similar to XA with better performance but with dirty rollback |
-| SAGA | support concurrency | complicated state-machine mode | dtm's state-machine mode is being planned |
-| Transactional Messaging | ✓ | ✗ | dtm provides Transactional Messaging similar to RocketMQ |
-| Multiple DBs in a service |✓|✗||
-| Communication protocols | HTTP, gRPC | Dubbo, no HTTP | |
-| Star count |
|
| dtm 0.1 is released from 20210604 and under fast development |
+``` bash
+git clone https://github.com/dtm-labs/dtm && cd dtm
+go run main.go
+```
-From the features' comparison above, if your language stack includes languages other than Java, then dtm is the one for you.
-If your language stack is Java, you can also choose to access dtm and use sub-transaction barrier technology to simplify your business development.
+### 启动并运行一个saga示例
+下面运行一个类似跨行转账的示例,包括两个事务分支:资金转出(TransOut)、资金转入(TransIn)。DTM保证TransIn和TransOut要么全部成功,要么全部回滚,保证最终金额的正确性。
-## [Cook Book](https://en.dtm.pub)
+`go run qs/main.go`
-# Quick start
+## 接入详解
-### Install
+### 接入代码
+``` GO
+ // 具体业务微服务地址
+ const qsBusi = "http://localhost:8081/api/busi_saga"
+ req := &gin.H{"amount": 30} // 微服务的载荷
+ // DtmServer为DTM服务的地址,是一个url
+ DtmServer := "http://localhost:36789/api/dtmsvr"
+ saga := dtmcli.NewSaga(DtmServer, dtmcli.MustGenGid(DtmServer)).
+ // 添加一个TransOut的子事务,正向操作为url: qsBusi+"/TransOut", 补偿操作为url: qsBusi+"/TransOutCompensate"
+ Add(qsBusi+"/TransOut", qsBusi+"/TransOutCompensate", req).
+ // 添加一个TransIn的子事务,正向操作为url: qsBusi+"/TransIn", 补偿操作为url: qsBusi+"/TransInCompensate"
+ Add(qsBusi+"/TransIn", qsBusi+"/TransInCompensate", req)
+ // 提交saga事务,dtm会完成所有的子事务/回滚所有的子事务
+ err := saga.Submit()
+```
-`git clone https://github.com/yedf/dtm`
+成功运行后,可以看到TransOut、TransIn依次被调用,完成了整个分布式事务
-### Configure Mysql
+### 时序图
-`cp conf.sample.yml conf.yml # Modify conf.yml`
+上述saga分布式事务的时序图如下:
-### Start the example
-`go run app/main.go`
+
-# Code
+### 失败情况
+在实际的业务中,子事务可能出现失败,例如转入的子账号被冻结导致转账失败。我们对业务代码进行修改,让TransIn的正向操作失败,然后看看结果
-### Use
``` go
- // business micro-service address
- const qsBusi = "http://localhost:8081/api/busi_saga"
- // The address where DtmServer serves DTM, which is a url
- DtmServer := "http://localhost:36789/api/dtmsvr"
- req := &gin.H{"amount": 30} // micro-service payload
- // DtmServer is the address of DTM micro-service
- saga := dtmcli.NewSaga(DtmServer, dtmcli.MustGenGid(DtmServer)).
- // add a TransOut subtraction,forward operation with url: qsBusi+"/TransOut", reverse compensation operation with url: qsBusi+"/TransOutCompensate"
- Add(qsBusi+"/TransOut", qsBusi+"/TransOutCompensate", req).
- // add a TransIn subtraction, forward operation with url: qsBusi+"/TransIn", reverse compensation operation with url: qsBusi+"/TransInCompensate"
- Add(qsBusi+"/TransIn", qsBusi+"/TransInCompensate", req)
- // submit the created saga transaction,dtm ensures all subtractions either complete or get revoked
- err := saga.Submit()
+ app.POST(qsBusiAPI+"/TransIn", common.WrapHandler2(func(c *gin.Context) interface{} {
+ return dtmcli.ErrFailure
+ }))
```
-### Complete example
-Refer to [examples/quick_start.go](./examples/quick_start.go).
+再运行这个例子,整个事务最终失败,时序图如下:
-### Slack
+
-You can join the [DTM slack channel here](https://join.slack.com/t/dtm-w6k9662/shared_invite/zt-vkrph4k1-eFqEFnMkbmlXqfUo5GWHWw).
+在转入操作失败的情况下,TransIn和TransOut的补偿操作被执行,保证了最终的余额和转账前是一样的。
-### Wechat
+### 更多示例
+参考[dtm-labs/dtm-examples](https://github.com/dtm-labs/dtm-examples)
-Add wechat friend with id yedf2008, or scan the OR code. Fill in dtm as verification.
+## 联系我们
+### 公众号
+dtm官方公众号:分布式事务,大量干货分享,以及dtm的最新消息
+### 交流群
+请加 yedf2008 好友或者扫码加好友,验证回复 dtm 按照指引进群

-### Give a star! ⭐
+### github
+作者github: [https://github.com/yedf2](https://github.com/yedf2)
-If you think this project is good, or helpful to you, please give a star!
+欢迎使用[dtm](https://github.com/dtm-labs/dtm),或者通过dtm学习实践分布式事务相关知识,欢迎star支持我们
-### Who is using
-
diff --git a/app/main.go b/app/main.go
deleted file mode 100644
index aa87722..0000000
--- a/app/main.go
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package main
-
-import (
- "fmt"
- "os"
- "strings"
-
- _ "go.uber.org/automaxprocs"
-
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmsvr"
- "github.com/yedf/dtm/dtmsvr/storage/registry"
- "github.com/yedf/dtm/examples"
-)
-
-var Version, Commit, Date string
-
-var usage = `dtm is a lightweight distributed transaction manager.
-
-usage:
- dtm [command]
-
-Available commands:
- version print dtm version
- dtmsvr run dtm as a server
- dev create all needed table and run dtm as a server
- bench start bench server
-
- quick_start run quick start example (dtm will create needed table)
- qs same as quick_start
-`
-
-func main() {
- if len(os.Args) == 1 {
- fmt.Println(usage)
- for name := range examples.Samples {
- fmt.Printf("%4s%-18srun a sample includes %s\n", "", name, strings.ReplaceAll(name, "_", " "))
- }
- return
- }
- if os.Args[1] == "version" {
- fmt.Printf("version: %s commit: %s built at: %s\n", Version, Commit, Date)
- return
- }
- dtmimp.Logf("starting dtm....")
- common.MustLoadConfig()
- if common.Config.ExamplesDB.Driver != "" {
- dtmcli.SetCurrentDBType(common.Config.ExamplesDB.Driver)
- }
- if os.Args[1] != "dtmsvr" { // 实际线上运行,只启动dtmsvr,不准备table相关的数据
- registry.WaitStoreUp()
- dtmsvr.PopulateDB(true)
- examples.PopulateDB(true)
- }
- dtmsvr.StartSvr() // 启动dtmsvr的api服务
- go dtmsvr.CronExpiredTrans(-1) // 启动dtmsvr的定时过期查询
-
- switch os.Args[1] {
- case "quick_start", "qs":
- // quick_start 比较独立,单独作为一个例子运行,方便新人上手
- examples.QsStartSvr()
- examples.QsFireRequest()
- case "dev", "dtmsvr":
- default:
- // 下面是各类的例子
- examples.GrpcStartup()
- examples.BaseAppStartup()
-
- sample := examples.Samples[os.Args[1]]
- dtmimp.LogIfFatalf(sample == nil, "no sample name for %s", os.Args[1])
- sample.Action()
- }
- select {}
-}
diff --git a/bench/Makefile b/bench/Makefile
new file mode 100644
index 0000000..e819a4b
--- /dev/null
+++ b/bench/Makefile
@@ -0,0 +1,28 @@
+# All targets.
+default: bench
+
+bench: /usr/local/bin/go /etc/redis/redis.conf /usr/local/bin/docker-compose main.go
+ rm -f ../conf.sample.yml
+ go build -o bench
+
+go: /usr/local/bin/go
+
+redis: /etc/redis/redis.conf
+
+mysql: /usr/local/bin/docker-compose
+
+/usr/local/bin/go:
+ wget https://golang.org/dl/go1.17.1.linux-amd64.tar.gz
+ rm -rf /usr/local/go && tar -C /usr/local -xzf go1.17.1.linux-amd64.tar.gz && cp -f /usr/local/go/bin/go /usr/local/bin/go && rm go1.*
+
+/etc/redis/redis.conf:
+ apt update
+ apt install -y redis redis-tools
+
+/usr/local/bin/docker-compose:
+ apt update
+ apt install -y sysbench apache2-utils mysql-client-core-8.0
+ curl -fsSL https://get.docker.com | sh
+ curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
+ chmod +x /usr/local/bin/docker-compose
+ cd .. && docker-compose -f helper/compose.mysql.yml up -d && cd bench
diff --git a/bench/main.go b/bench/main.go
index f757c8c..50e488b 100644
--- a/bench/main.go
+++ b/bench/main.go
@@ -4,38 +4,51 @@ import (
"fmt"
"os"
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmsvr"
- "github.com/yedf/dtm/dtmsvr/storage/registry"
- "github.com/yedf/dtm/examples"
+ "github.com/dtm-labs/dtm/bench/svr"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/dtmsvr"
+ "github.com/dtm-labs/dtm/dtmsvr/config"
+ "github.com/dtm-labs/dtm/dtmsvr/storage/registry"
+ "github.com/dtm-labs/dtm/test/busi"
)
-var hint = `To start the bench server, you need to specify the parameters:
-
-Available commands:
- http start bench server
+var usage = `bench is a bench test server for dtmf
+usage:
+ redis prepare for redis bench test
+ db prepare for mysql|postgres bench test
+ boltdb prepare for boltdb bench test
`
+func hintAndExit() {
+ fmt.Print(usage)
+ os.Exit(0)
+}
+
+var conf = &config.Config
+
func main() {
if len(os.Args) <= 1 {
- fmt.Printf(hint)
- return
+ hintAndExit()
+ }
+ logger.Infof("starting bench server")
+ config.MustLoadConfig("")
+ logger.InitLog(conf.Log.Level)
+ if busi.BusiConf.Driver != "" {
+ dtmcli.SetCurrentDBType(busi.BusiConf.Driver)
+ svr.PrepareBenchDB()
}
- dtmimp.Logf("starting dtm....")
- if os.Args[1] == "http" {
- fmt.Println("start bench server")
- common.MustLoadConfig()
- dtmcli.SetCurrentDBType(common.Config.ExamplesDB.Driver)
- registry.WaitStoreUp()
- dtmsvr.PopulateDB(true)
- examples.PopulateDB(true)
- dtmsvr.StartSvr() // 启动dtmsvr的api服务
- go dtmsvr.CronExpiredTrans(-1) // 启动dtmsvr的定时过期查询
- StartSvr()
- select {}
+ registry.WaitStoreUp()
+ dtmsvr.PopulateDB(false)
+ if os.Args[1] == "db" {
+ busi.PopulateDB(false)
+ } else if os.Args[1] == "redis" || os.Args[1] == "boltdb" {
+
} else {
- fmt.Printf(hint)
+ hintAndExit()
}
+ dtmsvr.StartSvr() // 启动dtmsvr的api服务
+ go dtmsvr.CronExpiredTrans(-1) // 启动dtmsvr的定时过期查询
+ svr.StartSvr() // 启动bench服务
+ select {}
}
diff --git a/bench/prepare.sh b/bench/prepare.sh
new file mode 100755
index 0000000..2ec3ad1
--- /dev/null
+++ b/bench/prepare.sh
@@ -0,0 +1,10 @@
+# !/bin/bash
+apt update
+apt install -y git
+git clone https://github.com/dtm-labs/dtm.git && cd dtm && git checkout alpha && cd bench && make
+
+
+echo 'all prepared. you shoud run following commands to test in different terminal'
+echo
+echo 'cd dtm && go run bench/main.go redis|boltdb|db'
+echo 'cd dtm && bench/run-redis|boltdb|mysql.sh'
diff --git a/bench/run-mysql.sh b/bench/run-mysql.sh
deleted file mode 100755
index 8db27b4..0000000
--- a/bench/run-mysql.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-# !/bin/bash
-
-cd /usr/share/sysbench/
-echo 'create database sbtest;' > mysql -h 127.0.0.1 -uroot
-
-sysbench oltp_write_only.lua --time=60 --mysql-host=127.0.0.1 --mysql-port=3306 --mysql-user=root --mysql-password= --mysql-db=sbtest --table-size=1000000 --tables=10 --threads=10 --events=999999999 --report-interval=10 prepare
-
-sysbench oltp_write_only.lua --time=60 --mysql-host=127.0.0.1 --mysql-port=3306 --mysql-user=root --mysql-password= --mysql-db=sbtest --table-size=1000000 --tables=10 --threads=10 --events=999999999 --report-interval=10 run
diff --git a/bench/run-services.sh b/bench/run-services.sh
deleted file mode 100644
index a95f9a3..0000000
--- a/bench/run-services.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-# !/bin/bash
-
-# start all services
-docker-compose -f helper/compose.mysql.yml up -d
-go run app/main.go bench > /dev/nul
\ No newline at end of file
diff --git a/bench/setup-redis6.sh b/bench/setup-redis6.sh
new file mode 100755
index 0000000..ed180f4
--- /dev/null
+++ b/bench/setup-redis6.sh
@@ -0,0 +1,6 @@
+# !/bin/bash
+apt update
+apt install -y software-properties-common
+add-apt-repository -y ppa:redislabs/redis
+apt install -y redis redis-tools
+
diff --git a/bench/setup.sh b/bench/setup.sh
index fe47bd0..985aaa3 100755
--- a/bench/setup.sh
+++ b/bench/setup.sh
@@ -1,8 +1,9 @@
# !/bin/bash
# install all commands needed
+
apt update
-apt install -y git sysbench apache2-utils mysql-client-core-8.0
+apt install -y sysbench apache2-utils mysql-client-core-8.0 redis redis-tools
# install docker and docker-compose
curl -fsSL https://get.docker.com -o get-docker.sh
@@ -12,4 +13,4 @@ chmod +x /usr/local/bin/docker-compose
# install go
wget https://golang.org/dl/go1.17.1.linux-amd64.tar.gz
-rm -rf /usr/local/go && tar -C /usr/local -xzf go1.17.1.linux-amd64.tar.gz && cp -f /usr/local/bin/go /usr/local/go/bin/go
+rm -rf /usr/local/go && tar -C /usr/local -xzf go1.17.1.linux-amd64.tar.gz && cp -f /usr/local/go/bin/go /usr/local/bin/go
diff --git a/bench/http.go b/bench/svr/http.go
similarity index 59%
rename from bench/http.go
rename to bench/svr/http.go
index 8a6a470..6e8d7bc 100644
--- a/bench/http.go
+++ b/bench/svr/http.go
@@ -4,53 +4,56 @@
* license that can be found in the LICENSE file.
*/
-package main
+package svr
import (
"database/sql"
"fmt"
+ "os"
"strings"
"sync/atomic"
"time"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/dtmsvr"
+ "github.com/dtm-labs/dtm/dtmutil"
+ "github.com/dtm-labs/dtm/test/busi"
"github.com/gin-gonic/gin"
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmsvr"
- "github.com/yedf/dtm/examples"
+ "github.com/lithammer/shortuuid"
)
// launch command:go run app/main.go qs
// service address of the transcation
const benchAPI = "/api/busi_bench"
-const benchPort = 8083
const total = 200000
-var benchBusi = fmt.Sprintf("http://localhost:%d%s", benchPort, benchAPI)
+var benchPort = dtmimp.If(os.Getenv("BENCH_PORT") == "", "8083", os.Getenv("BENCH_PORT")).(string)
+var benchBusi = fmt.Sprintf("http://localhost:%s%s", benchPort, benchAPI)
-func sdbGet() *sql.DB {
- db, err := dtmimp.PooledDB(common.Config.Store.GetDBConf())
- dtmimp.FatalIfError(err)
+func pdbGet() *sql.DB {
+ db, err := dtmimp.PooledDB(busi.BusiConf)
+ logger.FatalIfError(err)
return db
}
func txGet() *sql.Tx {
- db := sdbGet()
+ db := pdbGet()
tx, err := db.Begin()
- dtmimp.FatalIfError(err)
+ logger.FatalIfError(err)
return tx
}
func reloadData() {
time.Sleep(dtmsvr.UpdateBranchAsyncInterval * 2)
began := time.Now()
- db := sdbGet()
+ db := pdbGet()
tables := []string{"dtm_busi.user_account", "dtm_busi.user_account_log", "dtm.trans_global", "dtm.trans_branch_op", "dtm_barrier.barrier"}
for _, t := range tables {
_, err := dtmimp.DBExec(db, fmt.Sprintf("truncate %s", t))
- dtmimp.FatalIfError(err)
+ logger.FatalIfError(err)
}
s := "insert ignore into dtm_busi.user_account(user_id, balance) values "
ss := []string{}
@@ -58,23 +61,19 @@ func reloadData() {
ss = append(ss, fmt.Sprintf("(%d, 1000000)", i))
}
_, err := dtmimp.DBExec(db, s+strings.Join(ss, ","))
- dtmimp.FatalIfError(err)
- dtmimp.Logf("%d users inserted. used: %dms", total, time.Since(began).Milliseconds())
+ logger.FatalIfError(err)
+ logger.Debugf("%d users inserted. used: %dms", total, time.Since(began).Milliseconds())
}
-var uidCounter int32 = 0
-var mode string = ""
-var sqls int = 1
+var uidCounter int32
+var mode string
+var sqls = 1
-// StartSvr 1
-func StartSvr() {
- app := common.GetGinApp()
- benchAddRoute(app)
- dtmimp.Logf("bench listening at %d", benchPort)
- go app.Run(fmt.Sprintf(":%d", benchPort))
- db := sdbGet()
+// PrepareBenchDB prepares db data for bench
+func PrepareBenchDB() {
+ db := pdbGet()
_, err := dtmimp.DBExec(db, "drop table if exists dtm_busi.user_account_log")
- dtmimp.FatalIfError(err)
+ logger.FatalIfError(err)
_, err = dtmimp.DBExec(db, `create table if not exists dtm_busi.user_account_log (
id INT(11) AUTO_INCREMENT PRIMARY KEY,
user_id INT(11) NOT NULL,
@@ -89,70 +88,82 @@ func StartSvr() {
key(create_time)
)
`)
- dtmimp.FatalIfError(err)
+ logger.FatalIfError(err)
}
-func qsAdjustBalance(uid int, amount int, c *gin.Context) (interface{}, error) {
+// StartSvr 1
+func StartSvr() {
+ app := dtmutil.GetGinApp()
+ benchAddRoute(app)
+ logger.Debugf("bench listening at %d", benchPort)
+ go func() {
+ _ = app.Run(fmt.Sprintf(":%s", benchPort))
+ }()
+}
+
+func qsAdjustBalance(uid int, amount int, c *gin.Context) error { // nolint: unparam
if strings.Contains(mode, "empty") || sqls == 0 {
- return dtmcli.MapSuccess, nil
+ return nil
}
tb := dtmimp.TransBaseFromQuery(c.Request.URL.Query())
f := func(tx *sql.Tx) error {
for i := 0; i < sqls; i++ {
_, err := dtmimp.DBExec(tx, "insert into dtm_busi.user_account_log(user_id, delta, gid, branch_id, op, reason) values(?,?,?,?,?,?)",
uid, amount, tb.Gid, c.Query("branch_id"), tb.TransType, fmt.Sprintf("inserted by dtm transaction %s %s", tb.Gid, c.Query("branch_id")))
- dtmimp.FatalIfError(err)
+ logger.FatalIfError(err)
_, err = dtmimp.DBExec(tx, "update dtm_busi.user_account set balance = balance + ?, update_time = now() where user_id = ?", amount, uid)
- dtmimp.FatalIfError(err)
+ logger.FatalIfError(err)
}
return nil
}
if strings.Contains(mode, "barrier") {
barrier, err := dtmcli.BarrierFromQuery(c.Request.URL.Query())
- dtmimp.FatalIfError(err)
- barrier.Call(txGet(), f)
+ logger.FatalIfError(err)
+ err = barrier.Call(txGet(), f)
+ logger.FatalIfError(err)
} else {
tx := txGet()
- f(tx)
- err := tx.Commit()
- dtmimp.FatalIfError(err)
+ err := f(tx)
+ logger.FatalIfError(err)
+ err = tx.Commit()
+ logger.FatalIfError(err)
}
- return dtmcli.MapSuccess, nil
+ return nil
}
func benchAddRoute(app *gin.Engine) {
- app.POST(benchAPI+"/TransIn", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
+ app.POST(benchAPI+"/TransIn", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
return qsAdjustBalance(dtmimp.MustAtoi(c.Query("uid")), 1, c)
}))
- app.POST(benchAPI+"/TransInCompensate", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
+ app.POST(benchAPI+"/TransInCompensate", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
return qsAdjustBalance(dtmimp.MustAtoi(c.Query("uid")), -1, c)
}))
- app.POST(benchAPI+"/TransOut", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
+ app.POST(benchAPI+"/TransOut", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
return qsAdjustBalance(dtmimp.MustAtoi(c.Query("uid")), -1, c)
}))
- app.POST(benchAPI+"/TransOutCompensate", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
+ app.POST(benchAPI+"/TransOutCompensate", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
return qsAdjustBalance(dtmimp.MustAtoi(c.Query("uid")), 30, c)
}))
- app.Any(benchAPI+"/reloadData", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
+ app.Any(benchAPI+"/reloadData", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
reloadData()
mode = c.Query("m")
s := c.Query("sqls")
if s != "" {
sqls = dtmimp.MustAtoi(s)
}
- return nil, nil
+ return nil
}))
- app.Any(benchAPI+"/bench", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
+ app.Any(benchAPI+"/bench", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
uid := (atomic.AddInt32(&uidCounter, 1)-1)%total + 1
suid := fmt.Sprintf("%d", uid)
suid2 := fmt.Sprintf("%d", total+1-uid)
req := gin.H{}
params := fmt.Sprintf("?uid=%s", suid)
params2 := fmt.Sprintf("?uid=%s", suid2)
- dtmimp.Logf("mode: %s contains dtm: %t", mode, strings.Contains(mode, "dtm"))
+ logger.Debugf("mode: %s contains dtm: %t", mode, strings.Contains(mode, "dtm"))
if strings.Contains(mode, "dtm") {
- saga := dtmcli.NewSaga(examples.DtmHttpServer, fmt.Sprintf("bench-%d", uid)).
+ saga := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, fmt.Sprintf("bench-%d", uid)).
Add(benchBusi+"/TransOut"+params, benchBusi+"/TransOutCompensate"+params, req).
Add(benchBusi+"/TransIn"+params2, benchBusi+"/TransInCompensate"+params2, req)
saga.WaitResult = true
@@ -164,6 +175,15 @@ func benchAddRoute(app *gin.Engine) {
_, err = dtmimp.RestyClient.R().SetBody(gin.H{}).SetQueryParam("uid", suid).Post(benchBusi + "/TransIn")
dtmimp.E2P(err)
}
- return nil, nil
+ return nil
+ }))
+ app.Any(benchAPI+"/benchEmptyUrl", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ gid := shortuuid.New()
+ req := gin.H{}
+ saga := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, gid).
+ Add("", "", req).
+ Add("", "", req)
+ saga.WaitResult = true
+ return saga.Submit()
}))
}
diff --git a/bench/test-boltdb.sh b/bench/test-boltdb.sh
new file mode 100755
index 0000000..ddd2807
--- /dev/null
+++ b/bench/test-boltdb.sh
@@ -0,0 +1,5 @@
+# !/bin/bash
+
+set -x
+
+ab -n 50000 -c 10 "http://127.0.0.1:8083/api/busi_bench/benchEmptyUrl"
diff --git a/bench/run-dtm.sh b/bench/test-mysql.sh
similarity index 69%
rename from bench/run-dtm.sh
rename to bench/test-mysql.sh
index 5bc8e39..9a7f259 100755
--- a/bench/run-dtm.sh
+++ b/bench/test-mysql.sh
@@ -1,7 +1,14 @@
# !/bin/bash
-# go run ../app/main.go
set -x
+
+cd /usr/share/sysbench/
+echo 'create database sbtest;' > mysql -h 127.0.0.1 -uroot
+
+sysbench oltp_write_only.lua --time=60 --mysql-host=127.0.0.1 --mysql-port=3306 --mysql-user=root --mysql-password= --mysql-db=sbtest --table-size=1000000 --tables=10 --threads=10 --events=999999999 --report-interval=10 prepare
+
+sysbench oltp_write_only.lua --time=60 --mysql-host=127.0.0.1 --mysql-port=3306 --mysql-user=root --mysql-password= --mysql-db=sbtest --table-size=1000000 --tables=10 --threads=10 --events=999999999 --report-interval=10 run
+
export TIME=10
export CONCURRENT=20
curl "http://127.0.0.1:8083/api/busi_bench/reloadData?m=dtm_tx&sqls=0" && ab -t $TIME -c $CONCURRENT "http://127.0.0.1:8083/api/busi_bench/bench"
@@ -13,7 +20,3 @@ curl "http://127.0.0.1:8083/api/busi_bench/reloadData?m=dtm_barrier&sqls=1" && a
curl "http://127.0.0.1:8083/api/busi_bench/reloadData?m=raw_tx&sqls=1" && ab -t $TIME -c $CONCURRENT "http://127.0.0.1:8083/api/busi_bench/bench"
curl "http://127.0.0.1:8083/api/busi_bench/reloadData?m=raw_empty" && ab -t $TIME -c $CONCURRENT "http://127.0.0.1:8083/api/busi_bench/bench"
-# curl "http://127.0.0.1:8083/api/busi_bench/reloadData?m=raw_empty" && curl "http://127.0.0.1:8083/api/busi_bench/bench"
-# curl "http://127.0.0.1:8083/api/busi_bench/reloadData?m=raw_tx" && curl "http://127.0.0.1:8083/api/busi_bench/bench"
-# curl "http://127.0.0.1:8083/api/busi_bench/reloadData?m=dtm_tx" && curl "http://127.0.0.1:8083/api/busi_bench/bench"
-# curl "http://127.0.0.1:8083/api/busi_bench/reloadData?m=dtm_barrier" && curl "http://127.0.0.1:8083/api/busi_bench/bench"
diff --git a/bench/test-redis.sh b/bench/test-redis.sh
new file mode 100755
index 0000000..403c1c5
--- /dev/null
+++ b/bench/test-redis.sh
@@ -0,0 +1,27 @@
+# !/bin/bash
+
+set -x
+
+export LOG_LEVEL=warn
+export STORE_DRIVER=redis
+export STORE_HOST=localhost
+export STORE_PORT=6379
+cd .. && bench/bench redis &
+echo 'sleeping 3s for dtm bench to run up.' && sleep 3
+ab -n 1000000 -c 10 "http://127.0.0.1:8083/api/busi_bench/benchEmptyUrl"
+pkill bench
+
+redis-benchmark -n 300000 SET 'abcdefg' 'ddddddd'
+
+redis-benchmark -n 300000 EVAL "redis.call('SET', 'abcdedf', 'ddddddd')" 0
+
+redis-benchmark -n 300000 EVAL "redis.call('SET', KEYS[1], ARGV[1])" 1 'aaaaaaaaa' 'bbbbbbbbbb'
+
+redis-benchmark -n 3000000 -P 50 SET 'abcdefg' 'ddddddd'
+
+redis-benchmark -n 300000 EVAL "for k=1, 10 do; redis.call('SET', KEYS[1], ARGV[1]);end" 1 'aaaaaaaaa' 'bbbbbbbbbb'
+
+redis-benchmark -n 300000 -P 50 EVAL "redis.call('SET', KEYS[1], ARGV[1])" 1 'aaaaaaaaa' 'bbbbbbbbbb'
+
+redis-benchmark -n 300000 EVAL "for k=1,10 do;local c = cjson.decode(ARGV[1]);end" 1 'aaaaaaaaa' '{"aaaaa":"bbbbb","b":1,"t":"2012-01-01 14:00:00"}'
+
diff --git a/common/config.go b/common/config.go
deleted file mode 100644
index f39eaa0..0000000
--- a/common/config.go
+++ /dev/null
@@ -1,120 +0,0 @@
-package common
-
-import (
- "encoding/json"
- "errors"
- "io/ioutil"
- "path/filepath"
-
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "gopkg.in/yaml.v2"
-)
-
-const (
- DtmMetricsPort = 8889
- Mysql = "mysql"
- Redis = "redis"
- BoltDb = "boltdb"
-)
-
-// MicroService config type for micro service
-type MicroService struct {
- Driver string `yaml:"Driver" default:"default"`
- Target string `yaml:"Target"`
- EndPoint string `yaml:"EndPoint"`
-}
-
-type Store struct {
- Driver string `yaml:"Driver" default:"boltdb"`
- Host string `yaml:"Host"`
- Port int64 `yaml:"Port"`
- User string `yaml:"User"`
- Password string `yaml:"Password"`
- MaxOpenConns int64 `yaml:"MaxOpenConns" default:"500"`
- MaxIdleConns int64 `yaml:"MaxIdleConns" default:"500"`
- ConnMaxLifeTime int64 `yaml:"ConnMaxLifeTime" default:"5"`
- DataExpire int64 `yaml:"DataExpire" default:"604800"` // Trans data will expire in 7 days. only for redis/boltdb.
- RedisPrefix string `yaml:"RedisPrefix" default:"{a}"` // Redis storage prefix. store data to only one slot in cluster
-}
-
-func (s *Store) IsDB() bool {
- return s.Driver == dtmcli.DBTypeMysql || s.Driver == dtmcli.DBTypePostgres
-}
-
-func (s *Store) GetDBConf() dtmcli.DBConf {
- return dtmcli.DBConf{
- Driver: s.Driver,
- Host: s.Host,
- Port: s.Port,
- User: s.User,
- Passwrod: s.Password,
- }
-}
-
-type configType struct {
- Store Store `yaml:"Store"`
- TransCronInterval int64 `yaml:"TransCronInterval" default:"3"`
- TimeoutToFail int64 `yaml:"TimeoutToFail" default:"35"`
- RetryInterval int64 `yaml:"RetryInterval" default:"10"`
- HttpPort int64 `yaml:"HttpPort" default:"36789"`
- GrpcPort int64 `yaml:"GrpcPort" default:"36790"`
- MicroService MicroService `yaml:"MicroService"`
- UpdateBranchSync int64 `yaml:"UpdateBranchSync"`
- ExamplesDB dtmcli.DBConf `yaml:"ExamplesDB"`
-}
-
-// Config 配置
-var Config = configType{}
-
-func MustLoadConfig() {
- loadFromEnv("", &Config)
- cont := []byte{}
- for d := MustGetwd(); d != "" && d != "/"; d = filepath.Dir(d) {
- cont1, err := ioutil.ReadFile(d + "/conf.yml")
- if err != nil {
- cont1, err = ioutil.ReadFile(d + "/conf.sample.yml")
- }
- if cont1 != nil {
- cont = cont1
- break
- }
- }
- if len(cont) != 0 {
- err := yaml.UnmarshalStrict(cont, &Config)
- dtmimp.FatalIfError(err)
- }
- scont, err := json.MarshalIndent(&Config, "", " ")
- dtmimp.FatalIfError(err)
- dtmimp.Logf("config is: \n%s", scont)
- err = checkConfig()
- dtmimp.LogIfFatalf(err != nil, `config error: '%v'.
- check you env, and conf.yml/conf.sample.yml in current and parent path: %s.
- please visit http://d.dtm.pub to see the config document.
- loaded config is:
- %v`, err, MustGetwd(), Config)
-}
-
-func checkConfig() error {
- if Config.RetryInterval < 10 {
- return errors.New("RetryInterval should not be less than 10")
- }
- if Config.TimeoutToFail < Config.RetryInterval {
- return errors.New("TimeoutToFail should not be less than RetryInterval")
- }
- if Config.Store.Driver == BoltDb {
- return nil
- }
- if Config.Store.Driver == Mysql {
- if Config.Store.Host == "" {
- return errors.New("Db host not valid ")
- }
- if Config.Store.Port == 0 {
- return errors.New("Db port not valid ")
- }
- if Config.Store.User == "" {
- return errors.New("Db user not valid ")
- }
- }
- return nil
-}
diff --git a/common/config_test.go b/common/config_test.go
deleted file mode 100644
index b3ea8dc..0000000
--- a/common/config_test.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package common
-
-import (
- "errors"
- "os"
- "testing"
-
- "github.com/go-playground/assert/v2"
-)
-
-func TestLoadFromEnv(t *testing.T) {
- assert.Equal(t, "MICRO_SERVICE_DRIVER", toUnderscoreUpper("MicroService_Driver"))
-
- ms := MicroService{}
- os.Setenv("T_DRIVER", "d1")
- loadFromEnv("T", &ms)
- assert.Equal(t, "d1", ms.Driver)
-}
-
-func TestCheckConfig(t *testing.T) {
- config := &Config
- retryIntervalErr := checkConfig()
- retryIntervalExpect := errors.New("RetryInterval should not be less than 10")
- assert.Equal(t, retryIntervalErr, retryIntervalExpect)
-
- config.RetryInterval = 10
- timeoutToFailErr := checkConfig()
- timeoutToFailExpect := errors.New("TimeoutToFail should not be less than RetryInterval")
- assert.Equal(t, timeoutToFailErr, timeoutToFailExpect)
-
- config.TimeoutToFail = 20
- driverErr := checkConfig()
- assert.Equal(t, driverErr, nil)
-
- config.Store = Store{Driver: Mysql}
- hostErr := checkConfig()
- hostExpect := errors.New("Db host not valid ")
- assert.Equal(t, hostErr, hostExpect)
-
- config.Store = Store{Driver: Mysql, Host: "127.0.0.1"}
- portErr := checkConfig()
- portExpect := errors.New("Db port not valid ")
- assert.Equal(t, portErr, portExpect)
-
- config.Store = Store{Driver: Mysql, Host: "127.0.0.1", Port: 8686}
- userErr := checkConfig()
- userExpect := errors.New("Db user not valid ")
- assert.Equal(t, userErr, userExpect)
-}
diff --git a/common/types.go b/common/types.go
deleted file mode 100644
index 358dd80..0000000
--- a/common/types.go
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package common
-
-import (
- "fmt"
- "sync"
-
- "github.com/go-redis/redis/v8"
- "github.com/yedf/dtm/dtmcli/dtmimp"
-)
-
-var rdb *redis.Client
-var once sync.Once
-
-func RedisGet() *redis.Client {
- once.Do(func() {
- dtmimp.Logf("connecting to redis: %v", Config.Store)
- rdb = redis.NewClient(&redis.Options{
- Addr: fmt.Sprintf("%s:%d", Config.Store.Host, Config.Store.Port),
- Username: Config.Store.User,
- Password: Config.Store.Password,
- })
- })
- return rdb
-}
diff --git a/common/types_test.go b/common/types_test.go
deleted file mode 100644
index 8aea076..0000000
--- a/common/types_test.go
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package common
-
-import (
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli/dtmimp"
-)
-
-func TestGeneralDB(t *testing.T) {
- MustLoadConfig()
- if Config.Store.IsDB() {
- testSql(t)
- testDbAlone(t)
- }
-}
-func testSql(t *testing.T) {
- db := DbGet(Config.Store.GetDBConf())
- err := func() (rerr error) {
- defer dtmimp.P2E(&rerr)
- db.Must().Exec("select a")
- return nil
- }()
- assert.NotEqual(t, nil, err)
-}
-
-func testDbAlone(t *testing.T) {
- db, err := dtmimp.StandaloneDB(Config.Store.GetDBConf())
- assert.Nil(t, err)
- _, err = dtmimp.DBExec(db, "select 1")
- assert.Equal(t, nil, err)
- _, err = dtmimp.DBExec(db, "")
- assert.Equal(t, nil, err)
- db.Close()
- _, err = dtmimp.DBExec(db, "select 1")
- assert.NotEqual(t, nil, err)
-}
-
-func TestConfig(t *testing.T) {
- testConfigStringField(&Config.Store.Driver, "", t)
- testConfigStringField(&Config.Store.User, "", t)
- testConfigIntField(&Config.RetryInterval, 9, t)
- testConfigIntField(&Config.TimeoutToFail, 9, t)
-}
-
-func testConfigStringField(fd *string, val string, t *testing.T) {
- old := *fd
- *fd = val
- str := checkConfig()
- assert.NotEqual(t, "", str)
- *fd = old
-}
-
-func testConfigIntField(fd *int64, val int64, t *testing.T) {
- old := *fd
- *fd = val
- str := checkConfig()
- assert.NotEqual(t, "", str)
- *fd = old
-}
diff --git a/common/utils.go b/common/utils.go
deleted file mode 100644
index 9465a6c..0000000
--- a/common/utils.go
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package common
-
-import (
- "bytes"
- "encoding/json"
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
- "time"
-
- "github.com/gin-gonic/gin"
- "github.com/go-resty/resty/v2"
-
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
-)
-
-// GetGinApp init and return gin
-func GetGinApp() *gin.Engine {
- gin.SetMode(gin.ReleaseMode)
- app := gin.Default()
- app.Use(func(c *gin.Context) {
- body := ""
- if c.Request.Body != nil {
- rb, err := c.GetRawData()
- dtmimp.E2P(err)
- if len(rb) > 0 {
- body = string(rb)
- c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(rb))
- }
- }
- began := time.Now()
- dtmimp.Logf("begin %s %s query: %s body: %s", c.Request.Method, c.FullPath(), c.Request.URL.RawQuery, body)
- c.Next()
- dtmimp.Logf("used %d ms %s %s query: %s body: %s", time.Since(began).Milliseconds(), c.Request.Method, c.FullPath(), c.Request.URL.RawQuery, body)
-
- })
- app.Any("/api/ping", func(c *gin.Context) { c.JSON(200, map[string]interface{}{"msg": "pong"}) })
- return app
-}
-
-// WrapHandler name is clear
-func WrapHandler(fn func(*gin.Context) (interface{}, error)) gin.HandlerFunc {
- return func(c *gin.Context) {
- r, err := func() (r interface{}, rerr error) {
- defer dtmimp.P2E(&rerr)
- return fn(c)
- }()
- var b = []byte{}
- if resp, ok := r.(*resty.Response); ok { // 如果是response,则取出body直接处理
- b = resp.Body()
- } else if err == nil {
- b, err = json.Marshal(r)
- }
- if err != nil {
- dtmimp.Logf("status: 500, code: 500 message: %s", err.Error())
- c.JSON(500, map[string]interface{}{"code": 500, "message": err.Error()})
- } else {
- dtmimp.Logf("status: 200, content: %s", string(b))
- c.Status(200)
- c.Writer.Header().Add("Content-Type", "application/json")
- _, err = c.Writer.Write(b)
- dtmimp.E2P(err)
- }
- }
-}
-
-// MustGetwd must version of os.Getwd
-func MustGetwd() string {
- wd, err := os.Getwd()
- dtmimp.E2P(err)
- return wd
-}
-
-// GetSqlDir 获取调用该函数的caller源代码的目录,主要用于测试时,查找相关文件
-func GetSqlDir() string {
- wd := MustGetwd()
- if filepath.Base(wd) == "test" {
- wd = filepath.Dir(wd)
- }
- return wd + "/sqls"
-}
-
-func RecoverPanic(err *error) {
- if x := recover(); x != nil {
- e := dtmimp.AsError(x)
- if err != nil {
- *err = e
- }
- }
-}
-
-func GetNextTime(second int64) *time.Time {
- next := time.Now().Add(time.Duration(second) * time.Second)
- return &next
-}
-
-// RunSQLScript 1
-func RunSQLScript(conf dtmcli.DBConf, script string, skipDrop bool) {
- con, err := dtmimp.StandaloneDB(conf)
- dtmimp.FatalIfError(err)
- defer func() { con.Close() }()
- content, err := ioutil.ReadFile(script)
- dtmimp.FatalIfError(err)
- sqls := strings.Split(string(content), ";")
- for _, sql := range sqls {
- s := strings.TrimSpace(sql)
- if s == "" || (skipDrop && strings.Contains(s, "drop")) {
- continue
- }
- _, err = dtmimp.DBExec(con, s)
- dtmimp.FatalIfError(err)
- }
-}
diff --git a/conf.sample.yml b/conf.sample.yml
index 3d698c0..5c046ed 100644
--- a/conf.sample.yml
+++ b/conf.sample.yml
@@ -1,4 +1,11 @@
-Store: # specify which engine to store trans status
+#####################################################################
+### dtm can be run without any config.
+### all config in this file is optional. the default value is as specified in each line
+### all configs can be specified from env. for example:
+### MicroService.EndPoint => MICRO_SERVICE_END_POINT
+#####################################################################
+
+# Store: # specify which engine to store trans status
# Driver: 'boltdb' # default store engine
# Driver: 'redis'
@@ -7,11 +14,11 @@ Store: # specify which engine to store trans status
# Password: ''
# Port: 6379
- Driver: 'mysql'
- Host: 'localhost'
- User: 'root'
- Password: ''
- Port: 3306
+# Driver: 'mysql'
+# Host: 'localhost'
+# User: 'root'
+# Password: ''
+# Port: 3306
# Driver: 'postgres'
# Host: 'localhost'
@@ -19,10 +26,12 @@ Store: # specify which engine to store trans status
# Password: 'mysecretpassword'
# Port: '5432'
-### following connection config is for only Driver postgres/mysql
+### following config is for only Driver postgres/mysql
# MaxOpenConns: 500
# MaxIdleConns: 500
# ConnMaxLifeTime 5 # default value is 5 (minutes)
+# TransGlobalTable: 'dtm.trans_global'
+# TransBranchOpTable: 'dtm.trans_branch_op'
### flollowing config is only for some Driver
# DataExpire: 604800 # Trans data will expire in 7 days. only for redis/boltdb.
@@ -33,16 +42,23 @@ Store: # specify which engine to store trans status
# Target: 'etcd://localhost:2379/dtmservice' # register dtm server to this url
# EndPoint: 'localhost:36790'
-# the unit of following configurations is second
-
+### the unit of following configurations is second
# TransCronInterval: 3 # the interval to poll unfinished global transaction for every dtm process
# TimeoutToFail: 35 # timeout for XA, TCC to fail. saga's timeout default to infinite, which can be overwritten in saga options
# RetryInterval: 10 # the subtrans branch will be retried after this interval
+# RequestTimeout: 3 # the timeout of HTTP/gRPC request in dtm
+
+# Log:
+# Level: 'info' # default: info. can be debug|info|warn|error
+# Output: 'console' # default: console. can be console|file
+# FileName: '/tmp/dtm.log' # default: /tmp/dtm.log.
+# FileMaxSize: 10 # default: 10, unit: MB.
+# FileMaxBackups: 5 # default: 5.
+# FileMaxAge: 30 # default: 30, unit: days.
+# FileCompress: 0 # default: 0. can by 0|1, means false|true
+
+# HttpPort: 36789
+# GrpcPort: 36790
-### dtm can run examples, and examples will use following config to connect db
-ExamplesDB:
- Driver: 'mysql'
- Host: 'localhost'
- User: 'root'
- Password: ''
- Port: 3306
+### advanced options
+# UpdateBranchAsyncGoroutineNum: 1 # num of async goroutine to update branch status
diff --git a/dtmcli/barrier.go b/dtmcli/barrier.go
index 480ae17..c2b66d1 100644
--- a/dtmcli/barrier.go
+++ b/dtmcli/barrier.go
@@ -11,7 +11,9 @@ import (
"fmt"
"net/url"
- "github.com/yedf/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/go-redis/redis/v8"
)
// BarrierBusiFunc type for busi func
@@ -44,7 +46,7 @@ func BarrierFrom(transType, gid, branchID, op string) (*BranchBarrier, error) {
Op: op,
}
if ti.TransType == "" || ti.Gid == "" || ti.BranchID == "" || ti.Op == "" {
- return nil, fmt.Errorf("invlid trans info: %v", ti)
+ return nil, fmt.Errorf("invalid trans info: %v", ti)
}
return ti, nil
}
@@ -53,7 +55,7 @@ func insertBarrier(tx DB, transType string, gid string, branchID string, op stri
if op == "" {
return 0, nil
}
- sql := dtmimp.GetDBSpecial().GetInsertIgnoreTemplate("dtm_barrier.barrier(trans_type, gid, branch_id, op, barrier_id, reason) values(?,?,?,?,?,?)", "uniq_barrier")
+ sql := dtmimp.GetDBSpecial().GetInsertIgnoreTemplate(dtmimp.BarrierTableName+"(trans_type, gid, branch_id, op, barrier_id, reason) values(?,?,?,?,?,?)", "uniq_barrier")
return dtmimp.DBExec(tx, sql, transType, gid, branchID, op, barrierID, reason)
}
@@ -66,23 +68,23 @@ func (bb *BranchBarrier) Call(tx *sql.Tx, busiCall BarrierBusiFunc) (rerr error)
defer func() {
// Logf("barrier call error is %v", rerr)
if x := recover(); x != nil {
- tx.Rollback()
+ _ = tx.Rollback()
panic(x)
} else if rerr != nil {
- tx.Rollback()
+ _ = tx.Rollback()
} else {
- tx.Commit()
+ rerr = tx.Commit()
}
}()
ti := bb
- originType := map[string]string{
+ originOp := map[string]string{
BranchCancel: BranchTry,
BranchCompensate: BranchAction,
}[ti.Op]
- originAffected, _ := insertBarrier(tx, ti.TransType, ti.Gid, ti.BranchID, originType, bid, ti.Op)
+ originAffected, _ := insertBarrier(tx, ti.TransType, ti.Gid, ti.BranchID, originOp, bid, ti.Op)
currentAffected, rerr := insertBarrier(tx, ti.TransType, ti.Gid, ti.BranchID, ti.Op, bid, ti.Op)
- dtmimp.Logf("originAffected: %d currentAffected: %d", originAffected, currentAffected)
+ logger.Debugf("originAffected: %d currentAffected: %d", originAffected, currentAffected)
if (ti.Op == BranchCancel || ti.Op == BranchCompensate) && originAffected > 0 || // 这个是空补偿
currentAffected == 0 { // 这个是重复请求或者悬挂
return
@@ -94,8 +96,63 @@ func (bb *BranchBarrier) Call(tx *sql.Tx, busiCall BarrierBusiFunc) (rerr error)
// CallWithDB the same as Call, but with *sql.DB
func (bb *BranchBarrier) CallWithDB(db *sql.DB, busiCall BarrierBusiFunc) error {
tx, err := db.Begin()
- if err != nil {
- return err
+ if err == nil {
+ err = bb.Call(tx, busiCall)
}
- return bb.Call(tx, busiCall)
+ return err
+}
+
+// QueryPrepared queries prepared data
+func (bb *BranchBarrier) QueryPrepared(db *sql.DB) error {
+ _, err := insertBarrier(db, bb.TransType, bb.Gid, "00", "msg", "01", "rollback")
+ var reason string
+ if err == nil {
+ sql := fmt.Sprintf("select reason from %s where gid=? and branch_id=? and op=? and barrier_id=?", dtmimp.BarrierTableName)
+ err = db.QueryRow(sql, bb.Gid, "00", "msg", "01").Scan(&reason)
+ }
+ if reason == "rollback" {
+ return ErrFailure
+ }
+ return err
+}
+
+// RedisCheckAdjustAmount check the value of key is valid and >= amount. then adjust the amount
+func (bb *BranchBarrier) RedisCheckAdjustAmount(rd *redis.Client, key string, amount int, barrierExpire int) error {
+ bkey1 := fmt.Sprintf("%s-%s-%s-%s-%02d", key, bb.Gid, bb.BranchID, bb.Op, bb.BarrierID)
+ originOp := map[string]string{
+ BranchCancel: BranchTry,
+ BranchCompensate: BranchAction,
+ }[bb.Op]
+ bkey2 := fmt.Sprintf("%s-%s-%s-%s-%02d", key, bb.Gid, bb.BranchID, originOp, bb.BarrierID)
+ v, err := rd.Eval(rd.Context(), ` -- RedisCheckAdjustAmount
+local v = redis.call('GET', KEYS[1])
+local e1 = redis.call('GET', KEYS[2])
+
+if v == false or v + ARGV[1] < 0 then
+ return 'FAILURE'
+end
+
+if e1 ~= false then
+ return
+end
+
+redis.call('SET', KEYS[2], 'op', 'EX', ARGV[3])
+
+if ARGV[2] ~= '' then
+ local e2 = redis.call('GET', KEYS[3])
+ if e2 == false then
+ redis.call('SET', KEYS[3], 'rollback', 'EX', ARGV[3])
+ return
+ end
+end
+redis.call('INCRBY', KEYS[1], ARGV[1])
+`, []string{key, bkey1, bkey2}, amount, originOp, barrierExpire).Result()
+ logger.Debugf("lua return v: %v err: %v", v, err)
+ if err == redis.Nil {
+ err = nil
+ }
+ if err == nil && v == ResultFailure {
+ err = ErrFailure
+ }
+ return err
}
diff --git a/dtmcli/consts.go b/dtmcli/consts.go
index 5b538a7..b27b90a 100644
--- a/dtmcli/consts.go
+++ b/dtmcli/consts.go
@@ -7,7 +7,7 @@
package dtmcli
import (
- "github.com/yedf/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
)
const (
diff --git a/dtmcli/dtmimp/README.md b/dtmcli/dtmimp/README.md
new file mode 100644
index 0000000..ba4fb25
--- /dev/null
+++ b/dtmcli/dtmimp/README.md
@@ -0,0 +1,2 @@
+## 注意
+此包带imp后缀,主要被dtm内部使用,相关接口可能会发生变更,请勿使用这里的接口
\ No newline at end of file
diff --git a/dtmcli/dtmimp/trans_base.go b/dtmcli/dtmimp/trans_base.go
index b050064..ad0b9d1 100644
--- a/dtmcli/dtmimp/trans_base.go
+++ b/dtmcli/dtmimp/trans_base.go
@@ -9,6 +9,7 @@ package dtmimp
import (
"errors"
"fmt"
+ "net/http"
"net/url"
"strings"
@@ -40,9 +41,11 @@ func (g *BranchIDGen) CurrentSubBranchID() string {
// TransOptions transaction options
type TransOptions struct {
- WaitResult bool `json:"wait_result,omitempty" gorm:"-"`
- TimeoutToFail int64 `json:"timeout_to_fail,omitempty" gorm:"-"` // for trans type: xa, tcc
- RetryInterval int64 `json:"retry_interval,omitempty" gorm:"-"` // for trans type: msg saga xa tcc
+ WaitResult bool `json:"wait_result,omitempty" gorm:"-"`
+ TimeoutToFail int64 `json:"timeout_to_fail,omitempty" gorm:"-"` // for trans type: xa, tcc
+ RetryInterval int64 `json:"retry_interval,omitempty" gorm:"-"` // for trans type: msg saga xa tcc
+ PassthroughHeaders []string `json:"passthrough_headers,omitempty" gorm:"-"`
+ BranchHeaders map[string]string `json:"branch_headers,omitempty" gorm:"-"`
}
// TransBase base for all trans
@@ -62,18 +65,14 @@ type TransBase struct {
QueryPrepared string `json:"query_prepared,omitempty"` // used in MSG
}
-// SetOptions set options
-func (tb *TransBase) SetOptions(options *TransOptions) {
- tb.TransOptions = *options
-}
-
// NewTransBase new a TransBase
func NewTransBase(gid string, transType string, dtm string, branchID string) *TransBase {
return &TransBase{
- Gid: gid,
- TransType: transType,
- BranchIDGen: BranchIDGen{BranchID: branchID},
- Dtm: dtm,
+ Gid: gid,
+ TransType: transType,
+ BranchIDGen: BranchIDGen{BranchID: branchID},
+ Dtm: dtm,
+ TransOptions: TransOptions{PassthroughHeaders: PassthroughHeaders},
}
}
@@ -89,7 +88,7 @@ func TransCallDtm(tb *TransBase, body interface{}, operation string) error {
if err != nil {
return err
}
- if !strings.Contains(resp.String(), ResultSuccess) {
+ if resp.StatusCode() != http.StatusOK || strings.Contains(resp.String(), ResultFailure) {
return errors.New(resp.String())
}
return nil
@@ -118,6 +117,10 @@ func TransRequestBranch(t *TransBase, body interface{}, branchID string, op stri
"trans_type": t.TransType,
"op": op,
}).
+ SetHeaders(t.BranchHeaders).
Post(url)
- return resp, CheckResponse(resp, err)
+ if err == nil {
+ err = RespAsErrorCompatible(resp)
+ }
+ return resp, err
}
diff --git a/dtmcli/dtmimp/trans_xa_base.go b/dtmcli/dtmimp/trans_xa_base.go
index bcdb960..591aaf4 100644
--- a/dtmcli/dtmimp/trans_xa_base.go
+++ b/dtmcli/dtmimp/trans_xa_base.go
@@ -11,7 +11,7 @@ import (
"strings"
)
-// XaClientBase XaClient/XaGrpcClient base
+// XaClientBase XaClient/XaGrpcClient base. shared by http and grpc
type XaClientBase struct {
Server string
Conf DBConf
@@ -24,24 +24,26 @@ func (xc *XaClientBase) HandleCallback(gid string, branchID string, action strin
if err != nil {
return err
}
- defer db.Close()
+ defer func() {
+ _ = db.Close()
+ }()
xaID := gid + "-" + branchID
_, err = DBExec(db, GetDBSpecial().GetXaSQL(action, xaID))
if err != nil &&
- (strings.Contains(err.Error(), "XAER_NOTA") || strings.Contains(err.Error(), "does not exist")) { // 重复commit/rollback同一个id,报这个错误,忽略
+ (strings.Contains(err.Error(), "XAER_NOTA") || strings.Contains(err.Error(), "does not exist")) { // Repeat commit/rollback with the same id, report this error, ignore
err = nil
}
return err
}
-// HandleLocalTrans http/grpc 处理LocalTransaction的公共方法
+// HandleLocalTrans public handler of LocalTransaction via http/grpc
func (xc *XaClientBase) HandleLocalTrans(xa *TransBase, cb func(*sql.DB) error) (rerr error) {
xaBranch := xa.Gid + "-" + xa.BranchID
db, rerr := StandaloneDB(xc.Conf)
if rerr != nil {
return
}
- defer func() { db.Close() }()
+ defer func() { _ = db.Close() }()
defer func() {
x := recover()
_, err := DBExec(db, GetDBSpecial().GetXaSQL("end", xaBranch))
diff --git a/dtmcli/dtmimp/types.go b/dtmcli/dtmimp/types.go
index a092295..a0738f8 100644
--- a/dtmcli/dtmimp/types.go
+++ b/dtmcli/dtmimp/types.go
@@ -14,10 +14,11 @@ type DB interface {
QueryRow(query string, args ...interface{}) *sql.Row
}
+// DBConf defines db config
type DBConf struct {
Driver string `yaml:"Driver"`
Host string `yaml:"Host"`
Port int64 `yaml:"Port"`
User string `yaml:"User"`
- Passwrod string `yaml:"Password"`
+ Password string `yaml:"Password"`
}
diff --git a/dtmcli/dtmimp/types_test.go b/dtmcli/dtmimp/types_test.go
index 4da1654..6d58f4a 100644
--- a/dtmcli/dtmimp/types_test.go
+++ b/dtmcli/dtmimp/types_test.go
@@ -22,4 +22,5 @@ func TestTypes(t *testing.T) {
idGen := BranchIDGen{subBranchID: 99}
idGen.NewSubBranchID()
})
+ assert.Error(t, err)
}
diff --git a/dtmcli/dtmimp/utils.go b/dtmcli/dtmimp/utils.go
index 693f08d..d4fb44e 100644
--- a/dtmcli/dtmimp/utils.go
+++ b/dtmcli/dtmimp/utils.go
@@ -11,23 +11,37 @@ import (
"encoding/json"
"errors"
"fmt"
- "log"
+ "net/http"
"os"
"runtime"
- "runtime/debug"
"strconv"
"strings"
"sync"
"time"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
"github.com/go-resty/resty/v2"
- "go.uber.org/zap"
- "go.uber.org/zap/zapcore"
)
+// Logf an alias of Infof
+// Deprecated: use logger.Errorf
+var Logf = logger.Infof
+
+// LogRedf an alias of Errorf
+// Deprecated: use logger.Errorf
+var LogRedf = logger.Errorf
+
+// FatalIfError fatal if error is not nil
+// Deprecated: use logger.FatalIfError
+var FatalIfError = logger.FatalIfError
+
+// LogIfFatalf fatal if cond is true
+// Deprecated: use logger.FatalfIf
+var LogIfFatalf = logger.FatalfIf
+
// AsError wrap a panic value as an error
func AsError(x interface{}) error {
- LogRedf("panic wrapped to error: '%v'", x)
+ logger.Errorf("panic wrapped to error: '%v'", x)
if e, ok := x.(error); ok {
return e
}
@@ -120,59 +134,6 @@ func MustRemarshal(from interface{}, to interface{}) {
E2P(err)
}
-var logger *zap.SugaredLogger = nil
-
-func init() {
- InitLog()
-}
-
-// InitLog is a initialization for a logger
-func InitLog() {
- config := zap.NewProductionConfig()
- config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
- if os.Getenv("DTM_DEBUG") != "" {
- config.Encoding = "console"
- config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
- }
- p, err := config.Build(zap.AddCallerSkip(1))
- if err != nil {
- log.Fatal("create logger failed: ", err)
- }
- logger = p.Sugar()
-}
-
-// Logf is log stdout
-func Logf(fmt string, args ...interface{}) {
- logger.Infof(fmt, args...)
-}
-
-// LogRedf is print error message with red color
-func LogRedf(fmt string, args ...interface{}) {
- logger.Errorf(fmt, args...)
-}
-
-// FatalExitFunc is a Fatal exit function ,it will be replaced when testing
-var FatalExitFunc = func() { os.Exit(1) }
-
-// LogFatalf is print error message with red color, and execute FatalExitFunc
-func LogFatalf(fmt string, args ...interface{}) {
- fmt += "\n" + string(debug.Stack())
- LogRedf(fmt, args...)
- FatalExitFunc()
-}
-
-// LogIfFatalf is print error message with red color, and execute LogFatalf, when condition is true
-func LogIfFatalf(condition bool, fmt string, args ...interface{}) {
- if condition {
- LogFatalf(fmt, args...)
- }
-}
-
-// FatalIfError is print error message with red color, and execute LogIfFatalf.
-func FatalIfError(err error) {
- LogIfFatalf(err != nil, "Fatal error: %v", err)
-}
-
// GetFuncName get current call func name
func GetFuncName() string {
pc, _, _, _ := runtime.Caller(1)
@@ -208,7 +169,7 @@ func PooledDB(conf DBConf) (*sql.DB, error) {
// StandaloneDB get a standalone db instance
func StandaloneDB(conf DBConf) (*sql.DB, error) {
dsn := GetDsn(conf)
- Logf("opening standalone %s: %s", conf.Driver, strings.Replace(dsn, conf.Passwrod, "****", 1))
+ logger.Infof("opening standalone %s: %s", conf.Driver, strings.Replace(dsn, conf.Password, "****", 1))
return sql.Open(conf.Driver, dsn)
}
@@ -223,9 +184,9 @@ func DBExec(db DB, sql string, values ...interface{}) (affected int64, rerr erro
used := time.Since(began) / time.Millisecond
if rerr == nil {
affected, rerr = r.RowsAffected()
- Logf("used: %d ms affected: %d for %s %v", used, affected, sql, values)
+ logger.Debugf("used: %d ms affected: %d for %s %v", used, affected, sql, values)
} else {
- LogRedf("used: %d ms exec error: %v for %s %v", used, rerr, sql, values)
+ logger.Errorf("used: %d ms exec error: %v for %s %v", used, rerr, sql, values)
}
return
}
@@ -235,46 +196,26 @@ func GetDsn(conf DBConf) string {
host := MayReplaceLocalhost(conf.Host)
driver := conf.Driver
dsn := map[string]string{
- "mysql": fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=true&loc=Local",
- conf.User, conf.Passwrod, host, conf.Port, ""),
+ "mysql": fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=true&loc=Local&interpolateParams=true",
+ conf.User, conf.Password, host, conf.Port, ""),
"postgres": fmt.Sprintf("host=%s user=%s password=%s dbname='%s' port=%d sslmode=disable",
- host, conf.User, conf.Passwrod, "", conf.Port),
+ host, conf.User, conf.Password, "", conf.Port),
}[driver]
PanicIf(dsn == "", fmt.Errorf("unknow driver: %s", driver))
return dsn
}
-// CheckResponse is check response, and return corresponding error by the condition of resp when err is nil. Otherwise, return err directly.
-func CheckResponse(resp *resty.Response, err error) error {
- if err == nil && resp != nil {
- if resp.IsError() {
- return errors.New(resp.String())
- } else if strings.Contains(resp.String(), ResultFailure) {
- return ErrFailure
- } else if strings.Contains(resp.String(), ResultOngoing) {
- return ErrOngoing
- }
+// RespAsErrorCompatible translate a resty response to error
+// compatible with version < v1.10
+func RespAsErrorCompatible(resp *resty.Response) error {
+ code := resp.StatusCode()
+ str := resp.String()
+ if code == http.StatusTooEarly || strings.Contains(str, ResultOngoing) {
+ return fmt.Errorf("%s. %w", str, ErrOngoing)
+ } else if code == http.StatusConflict || strings.Contains(str, ResultFailure) {
+ return fmt.Errorf("%s. %w", str, ErrFailure)
+ } else if code != http.StatusOK {
+ return errors.New(str)
}
- return err
-}
-
-// CheckResult is check result. Return err directly if err is not nil. And return corresponding error by calling CheckResponse if resp is the type of *resty.Response.
-// Otherwise, return error by value of str, the string after marshal.
-func CheckResult(res interface{}, err error) error {
- if err != nil {
- return err
- }
- resp, ok := res.(*resty.Response)
- if ok {
- return CheckResponse(resp, err)
- }
- if res != nil {
- str := MustMarshalString(res)
- if strings.Contains(str, ResultFailure) {
- return ErrFailure
- } else if strings.Contains(str, ResultOngoing) {
- return ErrOngoing
- }
- }
- return err
+ return nil
}
diff --git a/dtmcli/dtmimp/utils_test.go b/dtmcli/dtmimp/utils_test.go
index 1625ced..167560c 100644
--- a/dtmcli/dtmimp/utils_test.go
+++ b/dtmcli/dtmimp/utils_test.go
@@ -8,7 +8,6 @@ package dtmimp
import (
"errors"
- "fmt"
"os"
"strings"
"testing"
@@ -80,20 +79,3 @@ func TestSome(t *testing.T) {
s2 := MayReplaceLocalhost("http://localhost")
assert.Equal(t, "http://localhost", s2)
}
-
-func TestFatal(t *testing.T) {
- old := FatalExitFunc
- defer func() {
- FatalExitFunc = old
- }()
- FatalExitFunc = func() { panic(fmt.Errorf("fatal")) }
- err := CatchP(func() {
- LogIfFatalf(true, "")
- })
- assert.Error(t, err, fmt.Errorf("fatal"))
-}
-
-func TestInitLog(t *testing.T) {
- os.Setenv("DTM_DEBUG", "1")
- InitLog()
-}
diff --git a/dtmcli/dtmimp/vars.go b/dtmcli/dtmimp/vars.go
index ee9bad6..3cf31d3 100644
--- a/dtmcli/dtmimp/vars.go
+++ b/dtmcli/dtmimp/vars.go
@@ -9,6 +9,7 @@ package dtmimp
import (
"errors"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
"github.com/go-resty/resty/v2"
)
@@ -18,8 +19,8 @@ var ErrFailure = errors.New("FAILURE")
// ErrOngoing error of ONGOING
var ErrOngoing = errors.New("ONGOING")
-// XaSqlTimeoutMs milliseconds for Xa sql to timeout
-var XaSqlTimeoutMs = 15000
+// XaSQLTimeoutMs milliseconds for Xa sql to timeout
+var XaSQLTimeoutMs = 15000
// MapSuccess HTTP result of SUCCESS
var MapSuccess = map[string]interface{}{"dtm_result": ResultSuccess}
@@ -30,18 +31,21 @@ var MapFailure = map[string]interface{}{"dtm_result": ResultFailure}
// RestyClient the resty object
var RestyClient = resty.New()
+// PassthroughHeaders will be passed to every sub-trans call
+var PassthroughHeaders = []string{}
+
+// BarrierTableName the table name of barrier table
+var BarrierTableName = "dtm_barrier.barrier"
+
func init() {
- // RestyClient.SetTimeout(3 * time.Second)
- // RestyClient.SetRetryCount(2)
- // RestyClient.SetRetryWaitTime(1 * time.Second)
RestyClient.OnBeforeRequest(func(c *resty.Client, r *resty.Request) error {
r.URL = MayReplaceLocalhost(r.URL)
- Logf("requesting: %s %s %v %v", r.Method, r.URL, r.Body, r.QueryParam)
+ logger.Debugf("requesting: %s %s %s", r.Method, r.URL, MustMarshalString(r.Body))
return nil
})
RestyClient.OnAfterResponse(func(c *resty.Client, resp *resty.Response) error {
r := resp.Request
- Logf("requested: %s %s %s", r.Method, r.URL, resp.String())
+ logger.Debugf("requested: %s %s %s", r.Method, r.URL, resp.String())
return nil
})
}
diff --git a/dtmcli/logger/log.go b/dtmcli/logger/log.go
new file mode 100644
index 0000000..e2ef110
--- /dev/null
+++ b/dtmcli/logger/log.go
@@ -0,0 +1,111 @@
+package logger
+
+import (
+ "fmt"
+ "log"
+ "net/url"
+ "os"
+
+ "github.com/natefinch/lumberjack"
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+)
+
+//var logger *zap.SugaredLogger = nil
+
+var logger Logger
+
+func init() {
+ InitLog(os.Getenv("LOG_LEVEL"))
+}
+
+// Logger logger interface
+type Logger interface {
+ Debugf(format string, args ...interface{})
+ Infof(format string, args ...interface{})
+ Warnf(format string, args ...interface{})
+ Errorf(format string, args ...interface{})
+}
+
+type lumberjackSink struct {
+ *lumberjack.Logger
+}
+
+func (lumberjackSink) Sync() error {
+ return nil
+}
+
+// WithLogger replaces default logger
+func WithLogger(log Logger) {
+ logger = log
+}
+
+// InitLog is an initialization for a logger
+// level can be: debug info warn error
+func InitLog(level string) {
+ config := loadConfig(level)
+ p, err := config.Build(zap.AddCallerSkip(1))
+ FatalIfError(err)
+ logger = p.Sugar()
+}
+
+// InitRotateLog is an initialization for a rotated logger by lumberjack
+func InitRotateLog(logLevel string, ll *lumberjack.Logger) {
+ config := loadConfig(logLevel)
+ config.OutputPaths = []string{fmt.Sprintf("lumberjack:%s", ll.Filename), "stdout"}
+ err := zap.RegisterSink("lumberjack", func(*url.URL) (zap.Sink, error) {
+ return lumberjackSink{
+ Logger: ll,
+ }, nil
+ })
+ FatalIfError(err)
+
+ p, err := config.Build(zap.AddCallerSkip(1))
+ FatalIfError(err)
+ logger = p.Sugar()
+}
+
+func loadConfig(logLevel string) zap.Config {
+ config := zap.NewProductionConfig()
+ err := config.Level.UnmarshalText([]byte(logLevel))
+ FatalIfError(err)
+ config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
+ if os.Getenv("DTM_DEBUG") != "" {
+ config.Encoding = "console"
+ config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
+ }
+ return config
+}
+
+// Debugf log to level debug
+func Debugf(fmt string, args ...interface{}) {
+ logger.Debugf(fmt, args...)
+}
+
+// Infof log to level info
+func Infof(fmt string, args ...interface{}) {
+ logger.Infof(fmt, args...)
+}
+
+// Warnf log to level warn
+func Warnf(fmt string, args ...interface{}) {
+ logger.Warnf(fmt, args...)
+}
+
+// Errorf log to level error
+func Errorf(fmt string, args ...interface{}) {
+ logger.Errorf(fmt, args...)
+}
+
+// FatalfIf log to level error
+func FatalfIf(cond bool, fmt string, args ...interface{}) {
+ if !cond {
+ return
+ }
+ log.Fatalf(fmt, args...)
+}
+
+// FatalIfError if err is not nil, then log to level fatal and call os.Exit
+func FatalIfError(err error) {
+ FatalfIf(err != nil, "fatal error: %v", err)
+}
diff --git a/dtmcli/logger/logger_test.go b/dtmcli/logger/logger_test.go
new file mode 100644
index 0000000..a63920f
--- /dev/null
+++ b/dtmcli/logger/logger_test.go
@@ -0,0 +1,52 @@
+package logger
+
+import (
+ "os"
+ "testing"
+
+ "github.com/natefinch/lumberjack"
+ "go.uber.org/zap"
+)
+
+func TestInitLog(t *testing.T) {
+ os.Setenv("DTM_DEBUG", "1")
+ InitLog("debug")
+ Debugf("a debug msg")
+ Infof("a info msg")
+ Warnf("a warn msg")
+ Errorf("a error msg")
+ FatalfIf(false, "nothing")
+ FatalIfError(nil)
+}
+
+func TestWithLogger(t *testing.T) {
+ logger := zap.NewExample().Sugar()
+ WithLogger(logger)
+ Debugf("a debug msg")
+ Infof("a info msg")
+ Warnf("a warn msg")
+ Errorf("a error msg")
+ FatalfIf(false, "nothing")
+ FatalIfError(nil)
+}
+
+func TestInitRotateLog(t *testing.T) {
+ os.Setenv("DTM_DEBUG", "1")
+ ll := lumberjack.Logger{
+ Filename: "test.log",
+ MaxSize: 1,
+ MaxBackups: 1,
+ MaxAge: 1,
+ Compress: false,
+ }
+ InitRotateLog("debug", &ll)
+ Debugf("a debug msg")
+ Infof("a info msg")
+ Warnf("a warn msg")
+ Errorf("a error msg")
+ FatalfIf(false, "nothing")
+ FatalIfError(nil)
+ s := lumberjackSink{&ll}
+ _ = s.Sync()
+ _ = os.Remove("test.log")
+}
diff --git a/dtmcli/msg.go b/dtmcli/msg.go
index e7dc97c..6fba805 100644
--- a/dtmcli/msg.go
+++ b/dtmcli/msg.go
@@ -6,7 +6,11 @@
package dtmcli
-import "github.com/yedf/dtm/dtmcli/dtmimp"
+import (
+ "database/sql"
+
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+)
// Msg reliable msg type
type Msg struct {
@@ -35,3 +39,23 @@ func (s *Msg) Prepare(queryPrepared string) error {
func (s *Msg) Submit() error {
return dtmimp.TransCallDtm(&s.TransBase, s, "submit")
}
+
+// PrepareAndSubmit one method for the entire busi->prepare->submit
+func (s *Msg) PrepareAndSubmit(queryPrepared string, db *sql.DB, busiCall BarrierBusiFunc) error {
+ bb, err := BarrierFrom(s.TransType, s.Gid, "00", "msg") // a special barrier for msg QueryPrepared
+ if err == nil {
+ err = s.Prepare(queryPrepared)
+ }
+ if err == nil {
+ defer func() {
+ if err != nil && bb.QueryPrepared(db) == ErrFailure {
+ _ = dtmimp.TransCallDtm(&s.TransBase, s, "abort")
+ }
+ }()
+ err = bb.CallWithDB(db, busiCall)
+ }
+ if err == nil {
+ err = s.Submit()
+ }
+ return err
+}
diff --git a/dtmcli/saga.go b/dtmcli/saga.go
index 05ad6f9..2ac9c1c 100644
--- a/dtmcli/saga.go
+++ b/dtmcli/saga.go
@@ -7,7 +7,7 @@
package dtmcli
import (
- "github.com/yedf/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
)
// Saga struct of saga
diff --git a/dtmcli/tcc.go b/dtmcli/tcc.go
index d1f85b0..d61a7fe 100644
--- a/dtmcli/tcc.go
+++ b/dtmcli/tcc.go
@@ -10,8 +10,8 @@ import (
"fmt"
"net/url"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/go-resty/resty/v2"
- "github.com/yedf/dtm/dtmcli/dtmimp"
)
// Tcc struct of tcc
@@ -24,10 +24,16 @@ type TccGlobalFunc func(tcc *Tcc) (*resty.Response, error)
// TccGlobalTransaction begin a tcc global transaction
// dtm dtm server address
-// gid 全局事务id
+// gid global transaction ID
// tccFunc tcc事务函数,里面会定义全局事务的分支
func TccGlobalTransaction(dtm string, gid string, tccFunc TccGlobalFunc) (rerr error) {
+ return TccGlobalTransaction2(dtm, gid, func(t *Tcc) {}, tccFunc)
+}
+
+// TccGlobalTransaction2 new version of TccGlobalTransaction, add custom param
+func TccGlobalTransaction2(dtm string, gid string, custom func(*Tcc), tccFunc TccGlobalFunc) (rerr error) {
tcc := &Tcc{TransBase: *dtmimp.NewTransBase(gid, "tcc", dtm, "")}
+ custom(tcc)
rerr = dtmimp.TransCallDtm(&tcc.TransBase, tcc, "prepare")
if rerr != nil {
return rerr
diff --git a/dtmcli/types.go b/dtmcli/types.go
index 3726e56..f48e30f 100644
--- a/dtmcli/types.go
+++ b/dtmcli/types.go
@@ -9,7 +9,8 @@ package dtmcli
import (
"fmt"
- "github.com/yedf/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/go-resty/resty/v2"
)
// MustGenGid generate a new gid
@@ -28,8 +29,19 @@ type DB = dtmimp.DB
// TransOptions transaction option
type TransOptions = dtmimp.TransOptions
+// DBConf declares db configuration
type DBConf = dtmimp.DBConf
+// String2DtmError translate string to dtm error
+func String2DtmError(str string) error {
+ return map[string]error{
+ ResultFailure: ErrFailure,
+ ResultOngoing: ErrOngoing,
+ ResultSuccess: nil,
+ "": nil,
+ }[str]
+}
+
// SetCurrentDBType set currentDBType
func SetCurrentDBType(dbType string) {
dtmimp.SetCurrentDBType(dbType)
@@ -40,12 +52,30 @@ func GetCurrentDBType() string {
return dtmimp.GetCurrentDBType()
}
-// SetXaSqlTimeoutMs set XaSqlTimeoutMs
-func SetXaSqlTimeoutMs(ms int) {
- dtmimp.XaSqlTimeoutMs = ms
+// SetXaSQLTimeoutMs set XaSQLTimeoutMs
+func SetXaSQLTimeoutMs(ms int) {
+ dtmimp.XaSQLTimeoutMs = ms
+}
+
+// GetXaSQLTimeoutMs get XaSQLTimeoutMs
+func GetXaSQLTimeoutMs() int {
+ return dtmimp.XaSQLTimeoutMs
+}
+
+// SetBarrierTableName sets barrier table name
+func SetBarrierTableName(tablename string) {
+ dtmimp.BarrierTableName = tablename
+}
+
+// GetRestyClient get the resty.Client for http request
+func GetRestyClient() *resty.Client {
+ return dtmimp.RestyClient
}
-// GetXaSqlTimeoutMs get XaSqlTimeoutMs
-func GetXaSqlTimeoutMs() int {
- return dtmimp.XaSqlTimeoutMs
+// SetPassthroughHeaders experimental.
+// apply to http header and grpc metadata
+// dtm server will save these headers in trans creating request.
+// and then passthrough them to sub-trans
+func SetPassthroughHeaders(headers []string) {
+ dtmimp.PassthroughHeaders = headers
}
diff --git a/dtmcli/types_test.go b/dtmcli/types_test.go
index 8128e7d..213d9a3 100644
--- a/dtmcli/types_test.go
+++ b/dtmcli/types_test.go
@@ -10,8 +10,8 @@ import (
"net/url"
"testing"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli/dtmimp"
)
func TestTypes(t *testing.T) {
@@ -26,6 +26,7 @@ func TestTypes(t *testing.T) {
}
func TestXaSqlTimeout(t *testing.T) {
- old := GetXaSqlTimeoutMs()
- SetXaSqlTimeoutMs(old)
+ old := GetXaSQLTimeoutMs()
+ SetXaSQLTimeoutMs(old)
+ SetBarrierTableName(dtmimp.BarrierTableName) // just cover this func
}
diff --git a/dtmcli/xa.go b/dtmcli/xa.go
index cd9ca48..2b48ecb 100644
--- a/dtmcli/xa.go
+++ b/dtmcli/xa.go
@@ -11,8 +11,8 @@ import (
"fmt"
"net/url"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/go-resty/resty/v2"
- "github.com/yedf/dtm/dtmcli/dtmimp"
)
// XaGlobalFunc type of xa global function
@@ -59,8 +59,8 @@ func NewXaClient(server string, mysqlConf DBConf, notifyURL string, register XaR
}
// HandleCallback 处理commit/rollback的回调
-func (xc *XaClient) HandleCallback(gid string, branchID string, action string) (interface{}, error) {
- return MapSuccess, xc.XaClientBase.HandleCallback(gid, branchID, action)
+func (xc *XaClient) HandleCallback(gid string, branchID string, action string) interface{} {
+ return xc.XaClientBase.HandleCallback(gid, branchID, action)
}
// XaLocalTransaction start a xa local transaction
@@ -83,11 +83,17 @@ func (xc *XaClient) XaLocalTransaction(qs url.Values, xaFunc XaLocalFunc) error
// XaGlobalTransaction start a xa global transaction
func (xc *XaClient) XaGlobalTransaction(gid string, xaFunc XaGlobalFunc) (rerr error) {
- xa := Xa{TransBase: *dtmimp.NewTransBase(gid, "xa", xc.XaClientBase.Server, "")}
+ return xc.XaGlobalTransaction2(gid, func(x *Xa) {}, xaFunc)
+}
+
+// XaGlobalTransaction2 start a xa global transaction
+func (xc *XaClient) XaGlobalTransaction2(gid string, custom func(*Xa), xaFunc XaGlobalFunc) (rerr error) {
+ xa := &Xa{TransBase: *dtmimp.NewTransBase(gid, "xa", xc.XaClientBase.Server, "")}
+ custom(xa)
return xc.HandleGlobalTrans(&xa.TransBase, func(action string) error {
- return dtmimp.TransCallDtm(&xa.TransBase, &xa, action)
+ return dtmimp.TransCallDtm(&xa.TransBase, xa, action)
}, func() error {
- _, rerr := xaFunc(&xa)
+ _, rerr := xaFunc(xa)
return rerr
})
}
diff --git a/dtmgrpc/barrier.go b/dtmgrpc/barrier.go
index 05cf5ee..973c14a 100644
--- a/dtmgrpc/barrier.go
+++ b/dtmgrpc/barrier.go
@@ -9,8 +9,8 @@ package dtmgrpc
import (
"context"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmgrpc/dtmgimp"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmgrpc/dtmgimp"
)
// BarrierFromGrpc generate a Barrier from grpc context
diff --git a/dtmgrpc/dtmgimp/README.md b/dtmgrpc/dtmgimp/README.md
new file mode 100644
index 0000000..ba4fb25
--- /dev/null
+++ b/dtmgrpc/dtmgimp/README.md
@@ -0,0 +1,2 @@
+## 注意
+此包带imp后缀,主要被dtm内部使用,相关接口可能会发生变更,请勿使用这里的接口
\ No newline at end of file
diff --git a/dtmgrpc/dtmgimp/dtmgimp.pb.go b/dtmgrpc/dtmgimp/dtmgimp.pb.go
deleted file mode 100644
index 3b28c14..0000000
--- a/dtmgrpc/dtmgimp/dtmgimp.pb.go
+++ /dev/null
@@ -1,510 +0,0 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-// Code generated by protoc-gen-go. DO NOT EDIT.
-// versions:
-// protoc-gen-go v1.27.1
-// protoc v3.17.3
-// source: dtmgrpc/dtmgimp/dtmgimp.proto
-
-package dtmgimp
-
-import (
- protoreflect "google.golang.org/protobuf/reflect/protoreflect"
- protoimpl "google.golang.org/protobuf/runtime/protoimpl"
- emptypb "google.golang.org/protobuf/types/known/emptypb"
- reflect "reflect"
- sync "sync"
-)
-
-const (
- // Verify that this generated code is sufficiently up-to-date.
- _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
- // Verify that runtime/protoimpl is sufficiently up-to-date.
- _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
-)
-
-type DtmTransOptions struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- WaitResult bool `protobuf:"varint,1,opt,name=WaitResult,proto3" json:"WaitResult,omitempty"`
- TimeoutToFail int64 `protobuf:"varint,2,opt,name=TimeoutToFail,proto3" json:"TimeoutToFail,omitempty"`
- RetryInterval int64 `protobuf:"varint,3,opt,name=RetryInterval,proto3" json:"RetryInterval,omitempty"`
-}
-
-func (x *DtmTransOptions) Reset() {
- *x = DtmTransOptions{}
- if protoimpl.UnsafeEnabled {
- mi := &file_dtmgrpc_dtmgimp_dtmgimp_proto_msgTypes[0]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
-}
-
-func (x *DtmTransOptions) String() string {
- return protoimpl.X.MessageStringOf(x)
-}
-
-func (*DtmTransOptions) ProtoMessage() {}
-
-func (x *DtmTransOptions) ProtoReflect() protoreflect.Message {
- mi := &file_dtmgrpc_dtmgimp_dtmgimp_proto_msgTypes[0]
- if protoimpl.UnsafeEnabled && x != nil {
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- if ms.LoadMessageInfo() == nil {
- ms.StoreMessageInfo(mi)
- }
- return ms
- }
- return mi.MessageOf(x)
-}
-
-// Deprecated: Use DtmTransOptions.ProtoReflect.Descriptor instead.
-func (*DtmTransOptions) Descriptor() ([]byte, []int) {
- return file_dtmgrpc_dtmgimp_dtmgimp_proto_rawDescGZIP(), []int{0}
-}
-
-func (x *DtmTransOptions) GetWaitResult() bool {
- if x != nil {
- return x.WaitResult
- }
- return false
-}
-
-func (x *DtmTransOptions) GetTimeoutToFail() int64 {
- if x != nil {
- return x.TimeoutToFail
- }
- return 0
-}
-
-func (x *DtmTransOptions) GetRetryInterval() int64 {
- if x != nil {
- return x.RetryInterval
- }
- return 0
-}
-
-// DtmRequest request sent to dtm server
-type DtmRequest struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- Gid string `protobuf:"bytes,1,opt,name=Gid,proto3" json:"Gid,omitempty"`
- TransType string `protobuf:"bytes,2,opt,name=TransType,proto3" json:"TransType,omitempty"`
- TransOptions *DtmTransOptions `protobuf:"bytes,3,opt,name=TransOptions,proto3" json:"TransOptions,omitempty"`
- CustomedData string `protobuf:"bytes,4,opt,name=CustomedData,proto3" json:"CustomedData,omitempty"`
- BinPayloads [][]byte `protobuf:"bytes,5,rep,name=BinPayloads,proto3" json:"BinPayloads,omitempty"` // for MSG/SAGA branch payloads
- QueryPrepared string `protobuf:"bytes,6,opt,name=QueryPrepared,proto3" json:"QueryPrepared,omitempty"` // for MSG
- Steps string `protobuf:"bytes,7,opt,name=Steps,proto3" json:"Steps,omitempty"`
-}
-
-func (x *DtmRequest) Reset() {
- *x = DtmRequest{}
- if protoimpl.UnsafeEnabled {
- mi := &file_dtmgrpc_dtmgimp_dtmgimp_proto_msgTypes[1]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
-}
-
-func (x *DtmRequest) String() string {
- return protoimpl.X.MessageStringOf(x)
-}
-
-func (*DtmRequest) ProtoMessage() {}
-
-func (x *DtmRequest) ProtoReflect() protoreflect.Message {
- mi := &file_dtmgrpc_dtmgimp_dtmgimp_proto_msgTypes[1]
- if protoimpl.UnsafeEnabled && x != nil {
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- if ms.LoadMessageInfo() == nil {
- ms.StoreMessageInfo(mi)
- }
- return ms
- }
- return mi.MessageOf(x)
-}
-
-// Deprecated: Use DtmRequest.ProtoReflect.Descriptor instead.
-func (*DtmRequest) Descriptor() ([]byte, []int) {
- return file_dtmgrpc_dtmgimp_dtmgimp_proto_rawDescGZIP(), []int{1}
-}
-
-func (x *DtmRequest) GetGid() string {
- if x != nil {
- return x.Gid
- }
- return ""
-}
-
-func (x *DtmRequest) GetTransType() string {
- if x != nil {
- return x.TransType
- }
- return ""
-}
-
-func (x *DtmRequest) GetTransOptions() *DtmTransOptions {
- if x != nil {
- return x.TransOptions
- }
- return nil
-}
-
-func (x *DtmRequest) GetCustomedData() string {
- if x != nil {
- return x.CustomedData
- }
- return ""
-}
-
-func (x *DtmRequest) GetBinPayloads() [][]byte {
- if x != nil {
- return x.BinPayloads
- }
- return nil
-}
-
-func (x *DtmRequest) GetQueryPrepared() string {
- if x != nil {
- return x.QueryPrepared
- }
- return ""
-}
-
-func (x *DtmRequest) GetSteps() string {
- if x != nil {
- return x.Steps
- }
- return ""
-}
-
-type DtmGidReply struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- Gid string `protobuf:"bytes,1,opt,name=Gid,proto3" json:"Gid,omitempty"`
-}
-
-func (x *DtmGidReply) Reset() {
- *x = DtmGidReply{}
- if protoimpl.UnsafeEnabled {
- mi := &file_dtmgrpc_dtmgimp_dtmgimp_proto_msgTypes[2]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
-}
-
-func (x *DtmGidReply) String() string {
- return protoimpl.X.MessageStringOf(x)
-}
-
-func (*DtmGidReply) ProtoMessage() {}
-
-func (x *DtmGidReply) ProtoReflect() protoreflect.Message {
- mi := &file_dtmgrpc_dtmgimp_dtmgimp_proto_msgTypes[2]
- if protoimpl.UnsafeEnabled && x != nil {
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- if ms.LoadMessageInfo() == nil {
- ms.StoreMessageInfo(mi)
- }
- return ms
- }
- return mi.MessageOf(x)
-}
-
-// Deprecated: Use DtmGidReply.ProtoReflect.Descriptor instead.
-func (*DtmGidReply) Descriptor() ([]byte, []int) {
- return file_dtmgrpc_dtmgimp_dtmgimp_proto_rawDescGZIP(), []int{2}
-}
-
-func (x *DtmGidReply) GetGid() string {
- if x != nil {
- return x.Gid
- }
- return ""
-}
-
-type DtmBranchRequest struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- Gid string `protobuf:"bytes,1,opt,name=Gid,proto3" json:"Gid,omitempty"`
- TransType string `protobuf:"bytes,2,opt,name=TransType,proto3" json:"TransType,omitempty"`
- BranchID string `protobuf:"bytes,3,opt,name=BranchID,proto3" json:"BranchID,omitempty"`
- Op string `protobuf:"bytes,4,opt,name=Op,proto3" json:"Op,omitempty"`
- Data map[string]string `protobuf:"bytes,5,rep,name=Data,proto3" json:"Data,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
- BusiPayload []byte `protobuf:"bytes,6,opt,name=BusiPayload,proto3" json:"BusiPayload,omitempty"`
-}
-
-func (x *DtmBranchRequest) Reset() {
- *x = DtmBranchRequest{}
- if protoimpl.UnsafeEnabled {
- mi := &file_dtmgrpc_dtmgimp_dtmgimp_proto_msgTypes[3]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
-}
-
-func (x *DtmBranchRequest) String() string {
- return protoimpl.X.MessageStringOf(x)
-}
-
-func (*DtmBranchRequest) ProtoMessage() {}
-
-func (x *DtmBranchRequest) ProtoReflect() protoreflect.Message {
- mi := &file_dtmgrpc_dtmgimp_dtmgimp_proto_msgTypes[3]
- if protoimpl.UnsafeEnabled && x != nil {
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- if ms.LoadMessageInfo() == nil {
- ms.StoreMessageInfo(mi)
- }
- return ms
- }
- return mi.MessageOf(x)
-}
-
-// Deprecated: Use DtmBranchRequest.ProtoReflect.Descriptor instead.
-func (*DtmBranchRequest) Descriptor() ([]byte, []int) {
- return file_dtmgrpc_dtmgimp_dtmgimp_proto_rawDescGZIP(), []int{3}
-}
-
-func (x *DtmBranchRequest) GetGid() string {
- if x != nil {
- return x.Gid
- }
- return ""
-}
-
-func (x *DtmBranchRequest) GetTransType() string {
- if x != nil {
- return x.TransType
- }
- return ""
-}
-
-func (x *DtmBranchRequest) GetBranchID() string {
- if x != nil {
- return x.BranchID
- }
- return ""
-}
-
-func (x *DtmBranchRequest) GetOp() string {
- if x != nil {
- return x.Op
- }
- return ""
-}
-
-func (x *DtmBranchRequest) GetData() map[string]string {
- if x != nil {
- return x.Data
- }
- return nil
-}
-
-func (x *DtmBranchRequest) GetBusiPayload() []byte {
- if x != nil {
- return x.BusiPayload
- }
- return nil
-}
-
-var File_dtmgrpc_dtmgimp_dtmgimp_proto protoreflect.FileDescriptor
-
-var file_dtmgrpc_dtmgimp_dtmgimp_proto_rawDesc = []byte{
- 0x0a, 0x1d, 0x64, 0x74, 0x6d, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d,
- 0x70, 0x2f, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
- 0x07, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
- 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e,
- 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x7d, 0x0a, 0x0f, 0x44, 0x74, 0x6d, 0x54, 0x72, 0x61, 0x6e,
- 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x57, 0x61, 0x69, 0x74,
- 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x57, 0x61,
- 0x69, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x69, 0x6d, 0x65,
- 0x6f, 0x75, 0x74, 0x54, 0x6f, 0x46, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52,
- 0x0d, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x54, 0x6f, 0x46, 0x61, 0x69, 0x6c, 0x12, 0x24,
- 0x0a, 0x0d, 0x52, 0x65, 0x74, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18,
- 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x52, 0x65, 0x74, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65,
- 0x72, 0x76, 0x61, 0x6c, 0x22, 0xfc, 0x01, 0x0a, 0x0a, 0x44, 0x74, 0x6d, 0x52, 0x65, 0x71, 0x75,
- 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x47, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x03, 0x47, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x54, 0x79,
- 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x54,
- 0x79, 0x70, 0x65, 0x12, 0x3c, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x70, 0x74, 0x69,
- 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x64, 0x74, 0x6d, 0x67,
- 0x69, 0x6d, 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x70, 0x74, 0x69,
- 0x6f, 0x6e, 0x73, 0x52, 0x0c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
- 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65, 0x64, 0x44, 0x61, 0x74,
- 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65,
- 0x64, 0x44, 0x61, 0x74, 0x61, 0x12, 0x20, 0x0a, 0x0b, 0x42, 0x69, 0x6e, 0x50, 0x61, 0x79, 0x6c,
- 0x6f, 0x61, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0b, 0x42, 0x69, 0x6e, 0x50,
- 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x79,
- 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d,
- 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x64, 0x12, 0x14, 0x0a,
- 0x05, 0x53, 0x74, 0x65, 0x70, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x53, 0x74,
- 0x65, 0x70, 0x73, 0x22, 0x1f, 0x0a, 0x0b, 0x44, 0x74, 0x6d, 0x47, 0x69, 0x64, 0x52, 0x65, 0x70,
- 0x6c, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x47, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
- 0x03, 0x47, 0x69, 0x64, 0x22, 0x82, 0x02, 0x0a, 0x10, 0x44, 0x74, 0x6d, 0x42, 0x72, 0x61, 0x6e,
- 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x47, 0x69, 0x64,
- 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x47, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x54,
- 0x72, 0x61, 0x6e, 0x73, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
- 0x54, 0x72, 0x61, 0x6e, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x42, 0x72, 0x61,
- 0x6e, 0x63, 0x68, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x42, 0x72, 0x61,
- 0x6e, 0x63, 0x68, 0x49, 0x44, 0x12, 0x0e, 0x0a, 0x02, 0x4f, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x02, 0x4f, 0x70, 0x12, 0x37, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20,
- 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x44, 0x74,
- 0x6d, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x44,
- 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x20,
- 0x0a, 0x0b, 0x42, 0x75, 0x73, 0x69, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x06, 0x20,
- 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x42, 0x75, 0x73, 0x69, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
- 0x1a, 0x37, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
- 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
- 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
- 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0xb1, 0x02, 0x0a, 0x03, 0x44, 0x74,
- 0x6d, 0x12, 0x38, 0x0a, 0x06, 0x4e, 0x65, 0x77, 0x47, 0x69, 0x64, 0x12, 0x16, 0x2e, 0x67, 0x6f,
- 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
- 0x70, 0x74, 0x79, 0x1a, 0x14, 0x2e, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x44, 0x74,
- 0x6d, 0x47, 0x69, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x37, 0x0a, 0x06, 0x53,
- 0x75, 0x62, 0x6d, 0x69, 0x74, 0x12, 0x13, 0x2e, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e,
- 0x44, 0x74, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f,
- 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70,
- 0x74, 0x79, 0x22, 0x00, 0x12, 0x38, 0x0a, 0x07, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x12,
- 0x13, 0x2e, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x52, 0x65, 0x71,
- 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
- 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x36,
- 0x0a, 0x05, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x12, 0x13, 0x2e, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d,
- 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67,
- 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,
- 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74,
- 0x65, 0x72, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x19, 0x2e, 0x64, 0x74, 0x6d, 0x67, 0x69,
- 0x6d, 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75,
- 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
- 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x42, 0x0b, 0x5a,
- 0x09, 0x2e, 0x2f, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
- 0x6f, 0x33,
-}
-
-var (
- file_dtmgrpc_dtmgimp_dtmgimp_proto_rawDescOnce sync.Once
- file_dtmgrpc_dtmgimp_dtmgimp_proto_rawDescData = file_dtmgrpc_dtmgimp_dtmgimp_proto_rawDesc
-)
-
-func file_dtmgrpc_dtmgimp_dtmgimp_proto_rawDescGZIP() []byte {
- file_dtmgrpc_dtmgimp_dtmgimp_proto_rawDescOnce.Do(func() {
- file_dtmgrpc_dtmgimp_dtmgimp_proto_rawDescData = protoimpl.X.CompressGZIP(file_dtmgrpc_dtmgimp_dtmgimp_proto_rawDescData)
- })
- return file_dtmgrpc_dtmgimp_dtmgimp_proto_rawDescData
-}
-
-var file_dtmgrpc_dtmgimp_dtmgimp_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
-var file_dtmgrpc_dtmgimp_dtmgimp_proto_goTypes = []interface{}{
- (*DtmTransOptions)(nil), // 0: dtmgimp.DtmTransOptions
- (*DtmRequest)(nil), // 1: dtmgimp.DtmRequest
- (*DtmGidReply)(nil), // 2: dtmgimp.DtmGidReply
- (*DtmBranchRequest)(nil), // 3: dtmgimp.DtmBranchRequest
- nil, // 4: dtmgimp.DtmBranchRequest.DataEntry
- (*emptypb.Empty)(nil), // 5: google.protobuf.Empty
-}
-var file_dtmgrpc_dtmgimp_dtmgimp_proto_depIdxs = []int32{
- 0, // 0: dtmgimp.DtmRequest.TransOptions:type_name -> dtmgimp.DtmTransOptions
- 4, // 1: dtmgimp.DtmBranchRequest.Data:type_name -> dtmgimp.DtmBranchRequest.DataEntry
- 5, // 2: dtmgimp.Dtm.NewGid:input_type -> google.protobuf.Empty
- 1, // 3: dtmgimp.Dtm.Submit:input_type -> dtmgimp.DtmRequest
- 1, // 4: dtmgimp.Dtm.Prepare:input_type -> dtmgimp.DtmRequest
- 1, // 5: dtmgimp.Dtm.Abort:input_type -> dtmgimp.DtmRequest
- 3, // 6: dtmgimp.Dtm.RegisterBranch:input_type -> dtmgimp.DtmBranchRequest
- 2, // 7: dtmgimp.Dtm.NewGid:output_type -> dtmgimp.DtmGidReply
- 5, // 8: dtmgimp.Dtm.Submit:output_type -> google.protobuf.Empty
- 5, // 9: dtmgimp.Dtm.Prepare:output_type -> google.protobuf.Empty
- 5, // 10: dtmgimp.Dtm.Abort:output_type -> google.protobuf.Empty
- 5, // 11: dtmgimp.Dtm.RegisterBranch:output_type -> google.protobuf.Empty
- 7, // [7:12] is the sub-list for method output_type
- 2, // [2:7] is the sub-list for method input_type
- 2, // [2:2] is the sub-list for extension type_name
- 2, // [2:2] is the sub-list for extension extendee
- 0, // [0:2] is the sub-list for field type_name
-}
-
-func init() { file_dtmgrpc_dtmgimp_dtmgimp_proto_init() }
-func file_dtmgrpc_dtmgimp_dtmgimp_proto_init() {
- if File_dtmgrpc_dtmgimp_dtmgimp_proto != nil {
- return
- }
- if !protoimpl.UnsafeEnabled {
- file_dtmgrpc_dtmgimp_dtmgimp_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*DtmTransOptions); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_dtmgrpc_dtmgimp_dtmgimp_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*DtmRequest); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_dtmgrpc_dtmgimp_dtmgimp_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*DtmGidReply); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_dtmgrpc_dtmgimp_dtmgimp_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*DtmBranchRequest); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- }
- type x struct{}
- out := protoimpl.TypeBuilder{
- File: protoimpl.DescBuilder{
- GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
- RawDescriptor: file_dtmgrpc_dtmgimp_dtmgimp_proto_rawDesc,
- NumEnums: 0,
- NumMessages: 5,
- NumExtensions: 0,
- NumServices: 1,
- },
- GoTypes: file_dtmgrpc_dtmgimp_dtmgimp_proto_goTypes,
- DependencyIndexes: file_dtmgrpc_dtmgimp_dtmgimp_proto_depIdxs,
- MessageInfos: file_dtmgrpc_dtmgimp_dtmgimp_proto_msgTypes,
- }.Build()
- File_dtmgrpc_dtmgimp_dtmgimp_proto = out.File
- file_dtmgrpc_dtmgimp_dtmgimp_proto_rawDesc = nil
- file_dtmgrpc_dtmgimp_dtmgimp_proto_goTypes = nil
- file_dtmgrpc_dtmgimp_dtmgimp_proto_depIdxs = nil
-}
diff --git a/dtmgrpc/dtmgimp/grpc_clients.go b/dtmgrpc/dtmgimp/grpc_clients.go
index 917f322..44e755b 100644
--- a/dtmgrpc/dtmgimp/grpc_clients.go
+++ b/dtmgrpc/dtmgimp/grpc_clients.go
@@ -8,9 +8,13 @@ package dtmgimp
import (
"fmt"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- grpc "google.golang.org/grpc"
"sync"
+
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/dtmgrpc/dtmgpb"
+ grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
+ grpc "google.golang.org/grpc"
)
type rawCodec struct{}
@@ -22,9 +26,8 @@ func (cb rawCodec) Marshal(v interface{}) ([]byte, error) {
func (cb rawCodec) Unmarshal(data []byte, v interface{}) error {
ba, ok := v.(*[]byte)
dtmimp.PanicIf(!ok, fmt.Errorf("please pass in *[]byte"))
- for _, byte := range data {
- *ba = append(*ba, byte)
- }
+ *ba = append(*ba, data...)
+
return nil
}
@@ -32,14 +35,12 @@ func (cb rawCodec) Name() string { return "dtm_raw" }
var normalClients, rawClients sync.Map
-// MustGetDtmClient 1
-func MustGetDtmClient(grpcServer string) DtmClient {
- return NewDtmClient(MustGetGrpcConn(grpcServer, false))
-}
+// ClientInterceptors declares grpc.UnaryClientInterceptors slice
+var ClientInterceptors = []grpc.UnaryClientInterceptor{}
-// MustGetRawDtmClient must get raw codec grpc conn
-func MustGetRawDtmClient(grpcServer string) DtmClient {
- return NewDtmClient(MustGetGrpcConn(grpcServer, true))
+// MustGetDtmClient 1
+func MustGetDtmClient(grpcServer string) dtmgpb.DtmClient {
+ return dtmgpb.NewDtmClient(MustGetGrpcConn(grpcServer, false))
}
// GetGrpcConn 1
@@ -55,12 +56,14 @@ func GetGrpcConn(grpcServer string, isRaw bool) (conn *grpc.ClientConn, rerr err
if isRaw {
opts = grpc.WithDefaultCallOptions(grpc.ForceCodec(rawCodec{}))
}
- dtmimp.Logf("grpc client connecting %s", grpcServer)
- conn, rerr := grpc.Dial(grpcServer, grpc.WithInsecure(), grpc.WithUnaryInterceptor(GrpcClientLog), opts)
+ logger.Debugf("grpc client connecting %s", grpcServer)
+ interceptors := append(ClientInterceptors, GrpcClientLog)
+ inOpt := grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(interceptors...))
+ conn, rerr := grpc.Dial(grpcServer, inOpt, grpc.WithInsecure(), opts)
if rerr == nil {
clients.Store(grpcServer, conn)
v = conn
- dtmimp.Logf("grpc client inited for %s", grpcServer)
+ logger.Debugf("grpc client inited for %s", grpcServer)
}
}
return v.(*grpc.ClientConn), rerr
diff --git a/dtmgrpc/dtmgimp/types.go b/dtmgrpc/dtmgimp/types.go
index a188f23..535e924 100644
--- a/dtmgrpc/dtmgimp/types.go
+++ b/dtmgrpc/dtmgimp/types.go
@@ -9,50 +9,40 @@ package dtmgimp
import (
"context"
"fmt"
+ "time"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
"google.golang.org/grpc"
- "google.golang.org/grpc/codes"
- "google.golang.org/grpc/status"
)
// GrpcServerLog 打印grpc服务端的日志
func GrpcServerLog(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
- dtmimp.Logf("grpc server handling: %s %v", info.FullMethod, req)
+ began := time.Now()
+ logger.Debugf("grpc server handling: %s %s", info.FullMethod, dtmimp.MustMarshalString(req))
LogDtmCtx(ctx)
m, err := handler(ctx, req)
- res := fmt.Sprintf("grpc server handled: %s %v result: %v err: %v", info.FullMethod, req, m, err)
+ res := fmt.Sprintf("%2dms %v %s %s %s",
+ time.Since(began).Milliseconds(), err, info.FullMethod, dtmimp.MustMarshalString(m), dtmimp.MustMarshalString(req))
if err != nil {
- dtmimp.LogRedf("%s", res)
+ logger.Errorf("%s", res)
} else {
- dtmimp.Logf("%s", res)
+ logger.Infof("%s", res)
}
return m, err
}
-// GrpcClientLog 打印grpc服务端的日志
+// GrpcClientLog 打印grpc调用的日志
func GrpcClientLog(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
- dtmimp.Logf("grpc client calling: %s%s %v", cc.Target(), method, req)
+ logger.Debugf("grpc client calling: %s%s %v", cc.Target(), method, dtmimp.MustMarshalString(req))
LogDtmCtx(ctx)
err := invoker(ctx, method, req, reply, cc, opts...)
- res := fmt.Sprintf("grpc client called: %s%s %v result: %v err: %v", cc.Target(), method, req, reply, err)
+ res := fmt.Sprintf("grpc client called: %s%s %s result: %s err: %v",
+ cc.Target(), method, dtmimp.MustMarshalString(req), dtmimp.MustMarshalString(reply), err)
if err != nil {
- dtmimp.LogRedf("%s", res)
+ logger.Errorf("%s", res)
} else {
- dtmimp.Logf("%s", res)
+ logger.Debugf("%s", res)
}
return err
}
-
-// Result2Error 将通用的result转成grpc的error
-func Result2Error(res interface{}, err error) error {
- e := dtmimp.CheckResult(res, err)
- if e == dtmimp.ErrFailure {
- dtmimp.LogRedf("failure: res: %v, err: %v", res, e)
- return status.New(codes.Aborted, dtmcli.ResultFailure).Err()
- } else if e == dtmimp.ErrOngoing {
- return status.New(codes.Aborted, dtmcli.ResultOngoing).Err()
- }
- return e
-}
diff --git a/dtmgrpc/dtmgimp/utils.go b/dtmgrpc/dtmgimp/utils.go
index ba2e08e..fb3eadf 100644
--- a/dtmgrpc/dtmgimp/utils.go
+++ b/dtmgrpc/dtmgimp/utils.go
@@ -9,7 +9,9 @@ package dtmgimp
import (
context "context"
- "github.com/yedf/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/dtmgrpc/dtmgpb"
"google.golang.org/grpc/metadata"
"google.golang.org/protobuf/proto"
emptypb "google.golang.org/protobuf/types/known/emptypb"
@@ -25,13 +27,15 @@ func MustProtoMarshal(msg proto.Message) []byte {
// DtmGrpcCall make a convenient call to dtm
func DtmGrpcCall(s *dtmimp.TransBase, operation string) error {
reply := emptypb.Empty{}
- return MustGetGrpcConn(s.Dtm, false).Invoke(context.Background(), "/dtmgimp.Dtm/"+operation, &DtmRequest{
+ return MustGetGrpcConn(s.Dtm, false).Invoke(context.Background(), "/dtmgimp.Dtm/"+operation, &dtmgpb.DtmRequest{
Gid: s.Gid,
TransType: s.TransType,
- TransOptions: &DtmTransOptions{
- WaitResult: s.WaitResult,
- TimeoutToFail: s.TimeoutToFail,
- RetryInterval: s.RetryInterval,
+ TransOptions: &dtmgpb.DtmTransOptions{
+ WaitResult: s.WaitResult,
+ TimeoutToFail: s.TimeoutToFail,
+ RetryInterval: s.RetryInterval,
+ PassthroughHeaders: s.PassthroughHeaders,
+ BranchHeaders: s.BranchHeaders,
},
QueryPrepared: s.QueryPrepared,
CustomedData: s.CustomData,
@@ -40,30 +44,43 @@ func DtmGrpcCall(s *dtmimp.TransBase, operation string) error {
}, &reply)
}
-const mdpre string = "dtm-"
+const dtmpre string = "dtm-"
// TransInfo2Ctx add trans info to grpc context
func TransInfo2Ctx(gid, transType, branchID, op, dtm string) context.Context {
md := metadata.Pairs(
- mdpre+"gid", gid,
- mdpre+"trans_type", transType,
- mdpre+"branch_id", branchID,
- mdpre+"op", op,
- mdpre+"dtm", dtm,
+ dtmpre+"gid", gid,
+ dtmpre+"trans_type", transType,
+ dtmpre+"branch_id", branchID,
+ dtmpre+"op", op,
+ dtmpre+"dtm", dtm,
)
return metadata.NewOutgoingContext(context.Background(), md)
}
+// Map2Kvs map to metadata kv
+func Map2Kvs(m map[string]string) []string {
+ kvs := []string{}
+ for k, v := range m {
+ kvs = append(kvs, k, v)
+ }
+ return kvs
+}
+
// LogDtmCtx logout dtm info in context metadata
func LogDtmCtx(ctx context.Context) {
tb := TransBaseFromGrpc(ctx)
if tb.Gid != "" {
- dtmimp.Logf("gid: %s trans_type: %s branch_id: %s op: %s dtm: %s", tb.Gid, tb.TransType, tb.BranchID, tb.Op, tb.Dtm)
+ logger.Debugf("gid: %s trans_type: %s branch_id: %s op: %s dtm: %s", tb.Gid, tb.TransType, tb.BranchID, tb.Op, tb.Dtm)
}
}
+func dtmGet(md metadata.MD, key string) string {
+ return mdGet(md, dtmpre+key)
+}
+
func mdGet(md metadata.MD, key string) string {
- v := md.Get(mdpre + key)
+ v := md.Get(key)
if len(v) == 0 {
return ""
}
@@ -73,7 +90,13 @@ func mdGet(md metadata.MD, key string) string {
// TransBaseFromGrpc get trans base info from a context metadata
func TransBaseFromGrpc(ctx context.Context) *dtmimp.TransBase {
md, _ := metadata.FromIncomingContext(ctx)
- tb := dtmimp.NewTransBase(mdGet(md, "gid"), mdGet(md, "trans_type"), mdGet(md, "dtm"), mdGet(md, "branch_id"))
- tb.Op = mdGet(md, "op")
+ tb := dtmimp.NewTransBase(dtmGet(md, "gid"), dtmGet(md, "trans_type"), dtmGet(md, "dtm"), dtmGet(md, "branch_id"))
+ tb.Op = dtmGet(md, "op")
return tb
}
+
+// GetMetaFromContext get header from context
+func GetMetaFromContext(ctx context.Context, name string) string {
+ md, _ := metadata.FromIncomingContext(ctx)
+ return mdGet(md, name)
+}
diff --git a/dtmgrpc/dtmgpb/dtmgimp.pb.go b/dtmgrpc/dtmgpb/dtmgimp.pb.go
new file mode 100644
index 0000000..9247260
--- /dev/null
+++ b/dtmgrpc/dtmgpb/dtmgimp.pb.go
@@ -0,0 +1,534 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.27.1
+// protoc v3.17.3
+// source: dtmgrpc/dtmgpb/dtmgimp.proto
+
+package dtmgpb
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ emptypb "google.golang.org/protobuf/types/known/emptypb"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type DtmTransOptions struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ WaitResult bool `protobuf:"varint,1,opt,name=WaitResult,proto3" json:"WaitResult,omitempty"`
+ TimeoutToFail int64 `protobuf:"varint,2,opt,name=TimeoutToFail,proto3" json:"TimeoutToFail,omitempty"`
+ RetryInterval int64 `protobuf:"varint,3,opt,name=RetryInterval,proto3" json:"RetryInterval,omitempty"`
+ PassthroughHeaders []string `protobuf:"bytes,4,rep,name=PassthroughHeaders,proto3" json:"PassthroughHeaders,omitempty"`
+ BranchHeaders map[string]string `protobuf:"bytes,5,rep,name=BranchHeaders,proto3" json:"BranchHeaders,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+}
+
+func (x *DtmTransOptions) Reset() {
+ *x = DtmTransOptions{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_dtmgrpc_dtmgpb_dtmgimp_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *DtmTransOptions) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DtmTransOptions) ProtoMessage() {}
+
+func (x *DtmTransOptions) ProtoReflect() protoreflect.Message {
+ mi := &file_dtmgrpc_dtmgpb_dtmgimp_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use DtmTransOptions.ProtoReflect.Descriptor instead.
+func (*DtmTransOptions) Descriptor() ([]byte, []int) {
+ return file_dtmgrpc_dtmgpb_dtmgimp_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *DtmTransOptions) GetWaitResult() bool {
+ if x != nil {
+ return x.WaitResult
+ }
+ return false
+}
+
+func (x *DtmTransOptions) GetTimeoutToFail() int64 {
+ if x != nil {
+ return x.TimeoutToFail
+ }
+ return 0
+}
+
+func (x *DtmTransOptions) GetRetryInterval() int64 {
+ if x != nil {
+ return x.RetryInterval
+ }
+ return 0
+}
+
+func (x *DtmTransOptions) GetPassthroughHeaders() []string {
+ if x != nil {
+ return x.PassthroughHeaders
+ }
+ return nil
+}
+
+func (x *DtmTransOptions) GetBranchHeaders() map[string]string {
+ if x != nil {
+ return x.BranchHeaders
+ }
+ return nil
+}
+
+// DtmRequest request sent to dtm server
+type DtmRequest struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Gid string `protobuf:"bytes,1,opt,name=Gid,proto3" json:"Gid,omitempty"`
+ TransType string `protobuf:"bytes,2,opt,name=TransType,proto3" json:"TransType,omitempty"`
+ TransOptions *DtmTransOptions `protobuf:"bytes,3,opt,name=TransOptions,proto3" json:"TransOptions,omitempty"`
+ CustomedData string `protobuf:"bytes,4,opt,name=CustomedData,proto3" json:"CustomedData,omitempty"`
+ BinPayloads [][]byte `protobuf:"bytes,5,rep,name=BinPayloads,proto3" json:"BinPayloads,omitempty"` // for MSG/SAGA branch payloads
+ QueryPrepared string `protobuf:"bytes,6,opt,name=QueryPrepared,proto3" json:"QueryPrepared,omitempty"` // for MSG
+ Steps string `protobuf:"bytes,7,opt,name=Steps,proto3" json:"Steps,omitempty"`
+}
+
+func (x *DtmRequest) Reset() {
+ *x = DtmRequest{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_dtmgrpc_dtmgpb_dtmgimp_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *DtmRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DtmRequest) ProtoMessage() {}
+
+func (x *DtmRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_dtmgrpc_dtmgpb_dtmgimp_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use DtmRequest.ProtoReflect.Descriptor instead.
+func (*DtmRequest) Descriptor() ([]byte, []int) {
+ return file_dtmgrpc_dtmgpb_dtmgimp_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *DtmRequest) GetGid() string {
+ if x != nil {
+ return x.Gid
+ }
+ return ""
+}
+
+func (x *DtmRequest) GetTransType() string {
+ if x != nil {
+ return x.TransType
+ }
+ return ""
+}
+
+func (x *DtmRequest) GetTransOptions() *DtmTransOptions {
+ if x != nil {
+ return x.TransOptions
+ }
+ return nil
+}
+
+func (x *DtmRequest) GetCustomedData() string {
+ if x != nil {
+ return x.CustomedData
+ }
+ return ""
+}
+
+func (x *DtmRequest) GetBinPayloads() [][]byte {
+ if x != nil {
+ return x.BinPayloads
+ }
+ return nil
+}
+
+func (x *DtmRequest) GetQueryPrepared() string {
+ if x != nil {
+ return x.QueryPrepared
+ }
+ return ""
+}
+
+func (x *DtmRequest) GetSteps() string {
+ if x != nil {
+ return x.Steps
+ }
+ return ""
+}
+
+type DtmGidReply struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Gid string `protobuf:"bytes,1,opt,name=Gid,proto3" json:"Gid,omitempty"`
+}
+
+func (x *DtmGidReply) Reset() {
+ *x = DtmGidReply{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_dtmgrpc_dtmgpb_dtmgimp_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *DtmGidReply) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DtmGidReply) ProtoMessage() {}
+
+func (x *DtmGidReply) ProtoReflect() protoreflect.Message {
+ mi := &file_dtmgrpc_dtmgpb_dtmgimp_proto_msgTypes[2]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use DtmGidReply.ProtoReflect.Descriptor instead.
+func (*DtmGidReply) Descriptor() ([]byte, []int) {
+ return file_dtmgrpc_dtmgpb_dtmgimp_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *DtmGidReply) GetGid() string {
+ if x != nil {
+ return x.Gid
+ }
+ return ""
+}
+
+type DtmBranchRequest struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Gid string `protobuf:"bytes,1,opt,name=Gid,proto3" json:"Gid,omitempty"`
+ TransType string `protobuf:"bytes,2,opt,name=TransType,proto3" json:"TransType,omitempty"`
+ BranchID string `protobuf:"bytes,3,opt,name=BranchID,proto3" json:"BranchID,omitempty"`
+ Op string `protobuf:"bytes,4,opt,name=Op,proto3" json:"Op,omitempty"`
+ Data map[string]string `protobuf:"bytes,5,rep,name=Data,proto3" json:"Data,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+ BusiPayload []byte `protobuf:"bytes,6,opt,name=BusiPayload,proto3" json:"BusiPayload,omitempty"`
+}
+
+func (x *DtmBranchRequest) Reset() {
+ *x = DtmBranchRequest{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_dtmgrpc_dtmgpb_dtmgimp_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *DtmBranchRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DtmBranchRequest) ProtoMessage() {}
+
+func (x *DtmBranchRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_dtmgrpc_dtmgpb_dtmgimp_proto_msgTypes[3]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use DtmBranchRequest.ProtoReflect.Descriptor instead.
+func (*DtmBranchRequest) Descriptor() ([]byte, []int) {
+ return file_dtmgrpc_dtmgpb_dtmgimp_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *DtmBranchRequest) GetGid() string {
+ if x != nil {
+ return x.Gid
+ }
+ return ""
+}
+
+func (x *DtmBranchRequest) GetTransType() string {
+ if x != nil {
+ return x.TransType
+ }
+ return ""
+}
+
+func (x *DtmBranchRequest) GetBranchID() string {
+ if x != nil {
+ return x.BranchID
+ }
+ return ""
+}
+
+func (x *DtmBranchRequest) GetOp() string {
+ if x != nil {
+ return x.Op
+ }
+ return ""
+}
+
+func (x *DtmBranchRequest) GetData() map[string]string {
+ if x != nil {
+ return x.Data
+ }
+ return nil
+}
+
+func (x *DtmBranchRequest) GetBusiPayload() []byte {
+ if x != nil {
+ return x.BusiPayload
+ }
+ return nil
+}
+
+var File_dtmgrpc_dtmgpb_dtmgimp_proto protoreflect.FileDescriptor
+
+var file_dtmgrpc_dtmgpb_dtmgimp_proto_rawDesc = []byte{
+ 0x0a, 0x1c, 0x64, 0x74, 0x6d, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x64, 0x74, 0x6d, 0x67, 0x70, 0x62,
+ 0x2f, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07,
+ 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc2, 0x02, 0x0a, 0x0f, 0x44, 0x74, 0x6d, 0x54, 0x72, 0x61, 0x6e,
+ 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x57, 0x61, 0x69, 0x74,
+ 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x57, 0x61,
+ 0x69, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x69, 0x6d, 0x65,
+ 0x6f, 0x75, 0x74, 0x54, 0x6f, 0x46, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52,
+ 0x0d, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x54, 0x6f, 0x46, 0x61, 0x69, 0x6c, 0x12, 0x24,
+ 0x0a, 0x0d, 0x52, 0x65, 0x74, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18,
+ 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x52, 0x65, 0x74, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65,
+ 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2e, 0x0a, 0x12, 0x50, 0x61, 0x73, 0x73, 0x74, 0x68, 0x72, 0x6f,
+ 0x75, 0x67, 0x68, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09,
+ 0x52, 0x12, 0x50, 0x61, 0x73, 0x73, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x48, 0x65, 0x61,
+ 0x64, 0x65, 0x72, 0x73, 0x12, 0x51, 0x0a, 0x0d, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x48, 0x65,
+ 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x64, 0x74,
+ 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x48, 0x65, 0x61, 0x64,
+ 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68,
+ 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x1a, 0x40, 0x0a, 0x12, 0x42, 0x72, 0x61, 0x6e, 0x63,
+ 0x68, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
+ 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
+ 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xfc, 0x01, 0x0a, 0x0a, 0x44, 0x74,
+ 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x47, 0x69, 0x64, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x47, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x54, 0x72,
+ 0x61, 0x6e, 0x73, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x54,
+ 0x72, 0x61, 0x6e, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x3c, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x6e,
+ 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18,
+ 0x2e, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x54, 0x72, 0x61, 0x6e,
+ 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d,
+ 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x43, 0x75,
+ 0x73, 0x74, 0x6f, 0x6d, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x12, 0x20, 0x0a, 0x0b, 0x42, 0x69,
+ 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0c, 0x52,
+ 0x0b, 0x42, 0x69, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x12, 0x24, 0x0a, 0x0d,
+ 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x64, 0x18, 0x06, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72,
+ 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x53, 0x74, 0x65, 0x70, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x05, 0x53, 0x74, 0x65, 0x70, 0x73, 0x22, 0x1f, 0x0a, 0x0b, 0x44, 0x74, 0x6d, 0x47,
+ 0x69, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x47, 0x69, 0x64, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x47, 0x69, 0x64, 0x22, 0x82, 0x02, 0x0a, 0x10, 0x44, 0x74,
+ 0x6d, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10,
+ 0x0a, 0x03, 0x47, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x47, 0x69, 0x64,
+ 0x12, 0x1c, 0x0a, 0x09, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x09, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a,
+ 0x0a, 0x08, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x08, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x49, 0x44, 0x12, 0x0e, 0x0a, 0x02, 0x4f, 0x70,
+ 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x4f, 0x70, 0x12, 0x37, 0x0a, 0x04, 0x44, 0x61,
+ 0x74, 0x61, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x64, 0x74, 0x6d, 0x67, 0x69,
+ 0x6d, 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75,
+ 0x65, 0x73, 0x74, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x44,
+ 0x61, 0x74, 0x61, 0x12, 0x20, 0x0a, 0x0b, 0x42, 0x75, 0x73, 0x69, 0x50, 0x61, 0x79, 0x6c, 0x6f,
+ 0x61, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x42, 0x75, 0x73, 0x69, 0x50, 0x61,
+ 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x1a, 0x37, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74,
+ 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0xb1,
+ 0x02, 0x0a, 0x03, 0x44, 0x74, 0x6d, 0x12, 0x38, 0x0a, 0x06, 0x4e, 0x65, 0x77, 0x47, 0x69, 0x64,
+ 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+ 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x14, 0x2e, 0x64, 0x74, 0x6d, 0x67, 0x69,
+ 0x6d, 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x47, 0x69, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00,
+ 0x12, 0x37, 0x0a, 0x06, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x12, 0x13, 0x2e, 0x64, 0x74, 0x6d,
+ 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
+ 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
+ 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x38, 0x0a, 0x07, 0x50, 0x72, 0x65,
+ 0x70, 0x61, 0x72, 0x65, 0x12, 0x13, 0x2e, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x44,
+ 0x74, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+ 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
+ 0x79, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x05, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x12, 0x13, 0x2e, 0x64,
+ 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+ 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0e, 0x52,
+ 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x19, 0x2e,
+ 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x42, 0x72, 0x61, 0x6e, 0x63,
+ 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
+ 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
+ 0x22, 0x00, 0x42, 0x0a, 0x5a, 0x08, 0x2e, 0x2f, 0x64, 0x74, 0x6d, 0x67, 0x70, 0x62, 0x62, 0x06,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+ file_dtmgrpc_dtmgpb_dtmgimp_proto_rawDescOnce sync.Once
+ file_dtmgrpc_dtmgpb_dtmgimp_proto_rawDescData = file_dtmgrpc_dtmgpb_dtmgimp_proto_rawDesc
+)
+
+func file_dtmgrpc_dtmgpb_dtmgimp_proto_rawDescGZIP() []byte {
+ file_dtmgrpc_dtmgpb_dtmgimp_proto_rawDescOnce.Do(func() {
+ file_dtmgrpc_dtmgpb_dtmgimp_proto_rawDescData = protoimpl.X.CompressGZIP(file_dtmgrpc_dtmgpb_dtmgimp_proto_rawDescData)
+ })
+ return file_dtmgrpc_dtmgpb_dtmgimp_proto_rawDescData
+}
+
+var file_dtmgrpc_dtmgpb_dtmgimp_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
+var file_dtmgrpc_dtmgpb_dtmgimp_proto_goTypes = []interface{}{
+ (*DtmTransOptions)(nil), // 0: dtmgimp.DtmTransOptions
+ (*DtmRequest)(nil), // 1: dtmgimp.DtmRequest
+ (*DtmGidReply)(nil), // 2: dtmgimp.DtmGidReply
+ (*DtmBranchRequest)(nil), // 3: dtmgimp.DtmBranchRequest
+ nil, // 4: dtmgimp.DtmTransOptions.BranchHeadersEntry
+ nil, // 5: dtmgimp.DtmBranchRequest.DataEntry
+ (*emptypb.Empty)(nil), // 6: google.protobuf.Empty
+}
+var file_dtmgrpc_dtmgpb_dtmgimp_proto_depIdxs = []int32{
+ 4, // 0: dtmgimp.DtmTransOptions.BranchHeaders:type_name -> dtmgimp.DtmTransOptions.BranchHeadersEntry
+ 0, // 1: dtmgimp.DtmRequest.TransOptions:type_name -> dtmgimp.DtmTransOptions
+ 5, // 2: dtmgimp.DtmBranchRequest.Data:type_name -> dtmgimp.DtmBranchRequest.DataEntry
+ 6, // 3: dtmgimp.Dtm.NewGid:input_type -> google.protobuf.Empty
+ 1, // 4: dtmgimp.Dtm.Submit:input_type -> dtmgimp.DtmRequest
+ 1, // 5: dtmgimp.Dtm.Prepare:input_type -> dtmgimp.DtmRequest
+ 1, // 6: dtmgimp.Dtm.Abort:input_type -> dtmgimp.DtmRequest
+ 3, // 7: dtmgimp.Dtm.RegisterBranch:input_type -> dtmgimp.DtmBranchRequest
+ 2, // 8: dtmgimp.Dtm.NewGid:output_type -> dtmgimp.DtmGidReply
+ 6, // 9: dtmgimp.Dtm.Submit:output_type -> google.protobuf.Empty
+ 6, // 10: dtmgimp.Dtm.Prepare:output_type -> google.protobuf.Empty
+ 6, // 11: dtmgimp.Dtm.Abort:output_type -> google.protobuf.Empty
+ 6, // 12: dtmgimp.Dtm.RegisterBranch:output_type -> google.protobuf.Empty
+ 8, // [8:13] is the sub-list for method output_type
+ 3, // [3:8] is the sub-list for method input_type
+ 3, // [3:3] is the sub-list for extension type_name
+ 3, // [3:3] is the sub-list for extension extendee
+ 0, // [0:3] is the sub-list for field type_name
+}
+
+func init() { file_dtmgrpc_dtmgpb_dtmgimp_proto_init() }
+func file_dtmgrpc_dtmgpb_dtmgimp_proto_init() {
+ if File_dtmgrpc_dtmgpb_dtmgimp_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_dtmgrpc_dtmgpb_dtmgimp_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*DtmTransOptions); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_dtmgrpc_dtmgpb_dtmgimp_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*DtmRequest); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_dtmgrpc_dtmgpb_dtmgimp_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*DtmGidReply); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_dtmgrpc_dtmgpb_dtmgimp_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*DtmBranchRequest); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_dtmgrpc_dtmgpb_dtmgimp_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 6,
+ NumExtensions: 0,
+ NumServices: 1,
+ },
+ GoTypes: file_dtmgrpc_dtmgpb_dtmgimp_proto_goTypes,
+ DependencyIndexes: file_dtmgrpc_dtmgpb_dtmgimp_proto_depIdxs,
+ MessageInfos: file_dtmgrpc_dtmgpb_dtmgimp_proto_msgTypes,
+ }.Build()
+ File_dtmgrpc_dtmgpb_dtmgimp_proto = out.File
+ file_dtmgrpc_dtmgpb_dtmgimp_proto_rawDesc = nil
+ file_dtmgrpc_dtmgpb_dtmgimp_proto_goTypes = nil
+ file_dtmgrpc_dtmgpb_dtmgimp_proto_depIdxs = nil
+}
diff --git a/dtmgrpc/dtmgimp/dtmgimp.proto b/dtmgrpc/dtmgpb/dtmgimp.proto
similarity index 90%
rename from dtmgrpc/dtmgimp/dtmgimp.proto
rename to dtmgrpc/dtmgpb/dtmgimp.proto
index a6ef5e6..f97b277 100644
--- a/dtmgrpc/dtmgimp/dtmgimp.proto
+++ b/dtmgrpc/dtmgpb/dtmgimp.proto
@@ -1,6 +1,6 @@
syntax = "proto3";
-option go_package = "./dtmgimp";
+option go_package = "./dtmgpb";
import "google/protobuf/empty.proto";
package dtmgimp;
@@ -18,6 +18,8 @@ message DtmTransOptions {
bool WaitResult = 1;
int64 TimeoutToFail = 2;
int64 RetryInterval = 3;
+ repeated string PassthroughHeaders = 4;
+ map BranchHeaders = 5;
}
// DtmRequest request sent to dtm server
diff --git a/dtmgrpc/dtmgimp/dtmgimp_grpc.pb.go b/dtmgrpc/dtmgpb/dtmgimp_grpc.pb.go
similarity index 97%
rename from dtmgrpc/dtmgimp/dtmgimp_grpc.pb.go
rename to dtmgrpc/dtmgpb/dtmgimp_grpc.pb.go
index 8d35989..8381443 100644
--- a/dtmgrpc/dtmgimp/dtmgimp_grpc.pb.go
+++ b/dtmgrpc/dtmgpb/dtmgimp_grpc.pb.go
@@ -1,12 +1,6 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
-package dtmgimp
+package dtmgpb
import (
context "context"
@@ -248,5 +242,5 @@ var Dtm_ServiceDesc = grpc.ServiceDesc{
},
},
Streams: []grpc.StreamDesc{},
- Metadata: "dtmgrpc/dtmgimp/dtmgimp.proto",
+ Metadata: "dtmgrpc/dtmgpb/dtmgimp.proto",
}
diff --git a/dtmgrpc/msg.go b/dtmgrpc/msg.go
index 31448a9..cbb8842 100644
--- a/dtmgrpc/msg.go
+++ b/dtmgrpc/msg.go
@@ -7,9 +7,11 @@
package dtmgrpc
import (
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmgrpc/dtmgimp"
+ "database/sql"
+
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmgrpc/dtmgimp"
"google.golang.org/protobuf/proto"
)
@@ -40,3 +42,21 @@ func (s *MsgGrpc) Prepare(queryPrepared string) error {
func (s *MsgGrpc) Submit() error {
return dtmgimp.DtmGrpcCall(&s.TransBase, "Submit")
}
+
+// PrepareAndSubmit one method for the entire busi->prepare->submit
+func (s *MsgGrpc) PrepareAndSubmit(queryPrepared string, db *sql.DB, busiCall dtmcli.BarrierBusiFunc) error {
+ bb, err := dtmcli.BarrierFrom(s.TransType, s.Gid, "00", "msg") // a special barrier for msg QueryPrepared
+ if err == nil {
+ err = bb.CallWithDB(db, func(tx *sql.Tx) error {
+ err := busiCall(tx)
+ if err == nil {
+ err = s.Prepare(queryPrepared)
+ }
+ return err
+ })
+ }
+ if err == nil {
+ err = s.Submit()
+ }
+ return err
+}
diff --git a/dtmgrpc/saga.go b/dtmgrpc/saga.go
index feb0879..af09038 100644
--- a/dtmgrpc/saga.go
+++ b/dtmgrpc/saga.go
@@ -7,8 +7,8 @@
package dtmgrpc
import (
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmgrpc/dtmgimp"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmgrpc/dtmgimp"
"google.golang.org/protobuf/proto"
)
diff --git a/dtmgrpc/tcc.go b/dtmgrpc/tcc.go
index ebc1c59..bd1b336 100644
--- a/dtmgrpc/tcc.go
+++ b/dtmgrpc/tcc.go
@@ -10,9 +10,11 @@ import (
context "context"
"fmt"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmgrpc/dtmgimp"
- "github.com/yedf/dtmdriver"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmgrpc/dtmgimp"
+ "github.com/dtm-labs/dtm/dtmgrpc/dtmgpb"
+ "github.com/dtm-labs/dtmdriver"
+ "google.golang.org/grpc/metadata"
"google.golang.org/protobuf/proto"
)
@@ -29,13 +31,14 @@ type TccGlobalFunc func(tcc *TccGrpc) error
// gid 全局事务id
// tccFunc tcc事务函数,里面会定义全局事务的分支
func TccGlobalTransaction(dtm string, gid string, tccFunc TccGlobalFunc) (rerr error) {
+ return TccGlobalTransaction2(dtm, gid, func(tg *TccGrpc) {}, tccFunc)
+}
+
+// TccGlobalTransaction2 new version of TccGlobalTransaction
+func TccGlobalTransaction2(dtm string, gid string, custom func(*TccGrpc), tccFunc TccGlobalFunc) (rerr error) {
tcc := &TccGrpc{TransBase: *dtmimp.NewTransBase(gid, "tcc", dtm, "")}
- dc := dtmgimp.MustGetDtmClient(tcc.Dtm)
- dr := &dtmgimp.DtmRequest{
- Gid: tcc.Gid,
- TransType: tcc.TransType,
- }
- _, rerr = dc.Prepare(context.Background(), dr)
+ custom(tcc)
+ rerr = dtmgimp.DtmGrpcCall(&tcc.TransBase, "Prepare")
if rerr != nil {
return rerr
}
@@ -43,10 +46,10 @@ func TccGlobalTransaction(dtm string, gid string, tccFunc TccGlobalFunc) (rerr e
defer func() {
x := recover()
if x == nil && rerr == nil {
- _, rerr = dc.Submit(context.Background(), dr)
+ rerr = dtmgimp.DtmGrpcCall(&tcc.TransBase, "Submit")
return
}
- _, err := dc.Abort(context.Background(), dr)
+ err := dtmgimp.DtmGrpcCall(&tcc.TransBase, "Abort")
if rerr == nil {
rerr = err
}
@@ -72,13 +75,15 @@ func TccFromGrpc(ctx context.Context) (*TccGrpc, error) {
func (t *TccGrpc) CallBranch(busiMsg proto.Message, tryURL string, confirmURL string, cancelURL string, reply interface{}) error {
branchID := t.NewSubBranchID()
bd, err := proto.Marshal(busiMsg)
- _, err = dtmgimp.MustGetDtmClient(t.Dtm).RegisterBranch(context.Background(), &dtmgimp.DtmBranchRequest{
- Gid: t.Gid,
- TransType: t.TransType,
- BranchID: branchID,
- BusiPayload: bd,
- Data: map[string]string{"confirm": confirmURL, "cancel": cancelURL},
- })
+ if err == nil {
+ _, err = dtmgimp.MustGetDtmClient(t.Dtm).RegisterBranch(context.Background(), &dtmgpb.DtmBranchRequest{
+ Gid: t.Gid,
+ TransType: t.TransType,
+ BranchID: branchID,
+ BusiPayload: bd,
+ Data: map[string]string{"confirm": confirmURL, "cancel": cancelURL},
+ })
+ }
if err != nil {
return err
}
@@ -86,6 +91,7 @@ func (t *TccGrpc) CallBranch(busiMsg proto.Message, tryURL string, confirmURL st
if err != nil {
return err
}
- return dtmgimp.MustGetGrpcConn(server, false).Invoke(
- dtmgimp.TransInfo2Ctx(t.Gid, t.TransType, branchID, "try", t.Dtm), method, busiMsg, reply)
+ ctx := dtmgimp.TransInfo2Ctx(t.Gid, t.TransType, branchID, "try", t.Dtm)
+ ctx = metadata.AppendToOutgoingContext(ctx, dtmgimp.Map2Kvs(t.BranchHeaders)...)
+ return dtmgimp.MustGetGrpcConn(server, false).Invoke(ctx, method, busiMsg, reply)
}
diff --git a/dtmgrpc/type.go b/dtmgrpc/type.go
index 3990261..194ff59 100644
--- a/dtmgrpc/type.go
+++ b/dtmgrpc/type.go
@@ -9,13 +9,27 @@ package dtmgrpc
import (
context "context"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmgrpc/dtmgimp"
- "github.com/yedf/dtmdriver"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmgrpc/dtmgimp"
+ "github.com/dtm-labs/dtmdriver"
+ grpc "google.golang.org/grpc"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
emptypb "google.golang.org/protobuf/types/known/emptypb"
)
+// DtmError2GrpcError translate dtm error to grpc error
+func DtmError2GrpcError(res interface{}) error {
+ e, ok := res.(error)
+ if ok && e == dtmimp.ErrFailure {
+ return status.New(codes.Aborted, dtmcli.ResultFailure).Err()
+ } else if ok && e == dtmimp.ErrOngoing {
+ return status.New(codes.FailedPrecondition, dtmcli.ResultOngoing).Err()
+ }
+ return e
+}
+
// MustGenGid must gen a gid from grpcServer
func MustGenGid(grpcServer string) string {
dc := dtmgimp.MustGetDtmClient(grpcServer)
@@ -24,16 +38,12 @@ func MustGenGid(grpcServer string) string {
return r.Gid
}
-// SetCurrentDBType set the current db type
-func SetCurrentDBType(dbType string) {
- dtmcli.SetCurrentDBType(dbType)
-}
-
-// GetCurrentDBType set the current db type
-func GetCurrentDBType() string {
- return dtmcli.GetCurrentDBType()
-}
-
+// UseDriver use the specified driver to handle grpc urls
func UseDriver(driverName string) error {
return dtmdriver.Use(driverName)
}
+
+// AddUnaryInterceptor adds grpc.UnaryClientInterceptor
+func AddUnaryInterceptor(interceptor grpc.UnaryClientInterceptor) {
+ dtmgimp.ClientInterceptors = append(dtmgimp.ClientInterceptors, interceptor)
+}
diff --git a/dtmgrpc/type_test.go b/dtmgrpc/type_test.go
index 9440905..c54583e 100644
--- a/dtmgrpc/type_test.go
+++ b/dtmgrpc/type_test.go
@@ -11,7 +11,6 @@ import (
"testing"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli"
)
func TestType(t *testing.T) {
@@ -21,10 +20,6 @@ func TestType(t *testing.T) {
_, err = TccFromGrpc(context.Background())
assert.Error(t, err)
- old := GetCurrentDBType()
- SetCurrentDBType(dtmcli.DBTypeMysql)
- SetCurrentDBType(old)
-
err = UseDriver("default")
assert.Nil(t, err)
}
diff --git a/dtmgrpc/xa.go b/dtmgrpc/xa.go
index c12a7e6..3cfd8bf 100644
--- a/dtmgrpc/xa.go
+++ b/dtmgrpc/xa.go
@@ -11,10 +11,11 @@ import (
"database/sql"
"fmt"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmgrpc/dtmgimp"
- "github.com/yedf/dtmdriver"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmgrpc/dtmgimp"
+ "github.com/dtm-labs/dtm/dtmgrpc/dtmgpb"
+ "github.com/dtm-labs/dtmdriver"
grpc "google.golang.org/grpc"
"google.golang.org/protobuf/proto"
emptypb "google.golang.org/protobuf/types/known/emptypb"
@@ -78,7 +79,7 @@ func (xc *XaGrpcClient) XaLocalTransaction(ctx context.Context, msg proto.Messag
if err != nil {
return err
}
- _, err = dtmgimp.MustGetDtmClient(xa.Dtm).RegisterBranch(context.Background(), &dtmgimp.DtmBranchRequest{
+ _, err = dtmgimp.MustGetDtmClient(xa.Dtm).RegisterBranch(context.Background(), &dtmgpb.DtmBranchRequest{
Gid: xa.Gid,
BranchID: xa.BranchID,
TransType: xa.TransType,
@@ -91,14 +92,20 @@ func (xc *XaGrpcClient) XaLocalTransaction(ctx context.Context, msg proto.Messag
// XaGlobalTransaction start a xa global transaction
func (xc *XaGrpcClient) XaGlobalTransaction(gid string, xaFunc XaGrpcGlobalFunc) error {
- xa := XaGrpc{TransBase: *dtmimp.NewTransBase(gid, "xa", xc.Server, "")}
+ return xc.XaGlobalTransaction2(gid, func(xg *XaGrpc) {}, xaFunc)
+}
+
+// XaGlobalTransaction2 new version of XaGlobalTransaction. support custom
+func (xc *XaGrpcClient) XaGlobalTransaction2(gid string, custom func(*XaGrpc), xaFunc XaGrpcGlobalFunc) error {
+ xa := &XaGrpc{TransBase: *dtmimp.NewTransBase(gid, "xa", xc.Server, "")}
+ custom(xa)
dc := dtmgimp.MustGetDtmClient(xa.Dtm)
- req := &dtmgimp.DtmRequest{
+ req := &dtmgpb.DtmRequest{
Gid: gid,
TransType: xa.TransType,
}
return xc.HandleGlobalTrans(&xa.TransBase, func(action string) error {
- f := map[string]func(context.Context, *dtmgimp.DtmRequest, ...grpc.CallOption) (*emptypb.Empty, error){
+ f := map[string]func(context.Context, *dtmgpb.DtmRequest, ...grpc.CallOption) (*emptypb.Empty, error){
"prepare": dc.Prepare,
"submit": dc.Submit,
"abort": dc.Abort,
@@ -106,7 +113,7 @@ func (xc *XaGrpcClient) XaGlobalTransaction(gid string, xaFunc XaGrpcGlobalFunc)
_, err := f(context.Background(), req)
return err
}, func() error {
- return xaFunc(&xa)
+ return xaFunc(xa)
})
}
diff --git a/dtmsvr/api.go b/dtmsvr/api.go
index eb26ffb..f2855f9 100644
--- a/dtmsvr/api.go
+++ b/dtmsvr/api.go
@@ -9,48 +9,56 @@ package dtmsvr
import (
"fmt"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmsvr/storage"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/dtmsvr/storage"
)
-func svcSubmit(t *TransGlobal) (interface{}, error) {
+func svcSubmit(t *TransGlobal) interface{} {
t.Status = dtmcli.StatusSubmitted
- err := t.saveNew()
+ branches, err := t.saveNew()
if err == storage.ErrUniqueConflict {
dbt := GetTransGlobal(t.Gid)
if dbt.Status == dtmcli.StatusPrepared {
dbt.changeStatus(t.Status)
+ branches = GetStore().FindBranches(t.Gid)
} else if dbt.Status != dtmcli.StatusSubmitted {
- return map[string]interface{}{"dtm_result": dtmcli.ResultFailure, "message": fmt.Sprintf("current status '%s', cannot sumbmit", dbt.Status)}, nil
+ return fmt.Errorf("current status '%s', cannot sumbmit. %w", dbt.Status, dtmcli.ErrFailure)
}
}
- return t.Process(), nil
+ return t.Process(branches)
}
-func svcPrepare(t *TransGlobal) (interface{}, error) {
+func svcPrepare(t *TransGlobal) interface{} {
t.Status = dtmcli.StatusPrepared
- err := t.saveNew()
+ _, err := t.saveNew()
if err == storage.ErrUniqueConflict {
dbt := GetTransGlobal(t.Gid)
if dbt.Status != dtmcli.StatusPrepared {
- return map[string]interface{}{"dtm_result": dtmcli.ResultFailure, "message": fmt.Sprintf("current status '%s', cannot prepare", dbt.Status)}, nil
+ return fmt.Errorf("current status '%s', cannot prepare. %w", dbt.Status, dtmcli.ErrFailure)
}
+ return nil
}
- return dtmcli.MapSuccess, nil
+ return err
}
-func svcAbort(t *TransGlobal) (interface{}, error) {
+func svcAbort(t *TransGlobal) interface{} {
dbt := GetTransGlobal(t.Gid)
+ if dbt.TransType == "msg" && dbt.Status == dtmcli.StatusPrepared {
+ dbt.changeStatus(dtmcli.StatusFailed)
+ return nil
+ }
if t.TransType != "xa" && t.TransType != "tcc" || dbt.Status != dtmcli.StatusPrepared && dbt.Status != dtmcli.StatusAborting {
- return map[string]interface{}{"dtm_result": dtmcli.ResultFailure, "message": fmt.Sprintf("trans type: '%s' current status '%s', cannot abort", dbt.TransType, dbt.Status)}, nil
+ return fmt.Errorf("trans type: '%s' current status '%s', cannot abort. %w", dbt.TransType, dbt.Status, dtmcli.ErrFailure)
}
dbt.changeStatus(dtmcli.StatusAborting)
- return dbt.Process(), nil
+ branches := GetStore().FindBranches(t.Gid)
+ return dbt.Process(branches)
}
-func svcRegisterBranch(transType string, branch *TransBranch, data map[string]string) (ret interface{}, rerr error) {
+func svcRegisterBranch(transType string, branch *TransBranch, data map[string]string) error {
branches := []TransBranch{*branch, *branch}
if transType == "tcc" {
for i, b := range []string{dtmcli.BranchCancel, dtmcli.BranchConfirm} {
@@ -63,7 +71,7 @@ func svcRegisterBranch(transType string, branch *TransBranch, data map[string]st
branches[1].Op = dtmcli.BranchCommit
branches[1].URL = data["url"]
} else {
- return nil, fmt.Errorf("unknow trans type: %s", transType)
+ return fmt.Errorf("unknow trans type: %s", transType)
}
err := dtmimp.CatchP(func() {
@@ -71,7 +79,10 @@ func svcRegisterBranch(transType string, branch *TransBranch, data map[string]st
})
if err == storage.ErrNotFound {
msg := fmt.Sprintf("no trans with gid: %s status: %s found", branch.Gid, dtmcli.StatusPrepared)
- return map[string]interface{}{"dtm_result": dtmcli.ResultFailure, "message": msg}, nil
+ logger.Errorf(msg)
+ return fmt.Errorf("message: %s %w", msg, dtmcli.ErrFailure)
}
- return dtmimp.If(err != nil, nil, dtmcli.MapSuccess), err
+ logger.Infof("LockGlobalSaveBranches result: %v: gid: %s old status: %s branches: %s",
+ err, branch.Gid, dtmcli.StatusPrepared, dtmimp.MustMarshalString(branches))
+ return err
}
diff --git a/dtmsvr/api_grpc.go b/dtmsvr/api_grpc.go
index 07e4d0e..8bc840a 100644
--- a/dtmsvr/api_grpc.go
+++ b/dtmsvr/api_grpc.go
@@ -9,9 +9,9 @@ package dtmsvr
import (
"context"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmgrpc/dtmgimp"
- pb "github.com/yedf/dtm/dtmgrpc/dtmgimp"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmgrpc"
+ pb "github.com/dtm-labs/dtm/dtmgrpc/dtmgpb"
"google.golang.org/protobuf/types/known/emptypb"
)
@@ -20,31 +20,31 @@ type dtmServer struct {
pb.UnimplementedDtmServer
}
-func (s *dtmServer) NewGid(ctx context.Context, in *emptypb.Empty) (*dtmgimp.DtmGidReply, error) {
- return &dtmgimp.DtmGidReply{Gid: GenGid()}, nil
+func (s *dtmServer) NewGid(ctx context.Context, in *emptypb.Empty) (*pb.DtmGidReply, error) {
+ return &pb.DtmGidReply{Gid: GenGid()}, nil
}
func (s *dtmServer) Submit(ctx context.Context, in *pb.DtmRequest) (*emptypb.Empty, error) {
- r, err := svcSubmit(TransFromDtmRequest(in))
- return &emptypb.Empty{}, dtmgimp.Result2Error(r, err)
+ r := svcSubmit(TransFromDtmRequest(ctx, in))
+ return &emptypb.Empty{}, dtmgrpc.DtmError2GrpcError(r)
}
func (s *dtmServer) Prepare(ctx context.Context, in *pb.DtmRequest) (*emptypb.Empty, error) {
- r, err := svcPrepare(TransFromDtmRequest(in))
- return &emptypb.Empty{}, dtmgimp.Result2Error(r, err)
+ r := svcPrepare(TransFromDtmRequest(ctx, in))
+ return &emptypb.Empty{}, dtmgrpc.DtmError2GrpcError(r)
}
func (s *dtmServer) Abort(ctx context.Context, in *pb.DtmRequest) (*emptypb.Empty, error) {
- r, err := svcAbort(TransFromDtmRequest(in))
- return &emptypb.Empty{}, dtmgimp.Result2Error(r, err)
+ r := svcAbort(TransFromDtmRequest(ctx, in))
+ return &emptypb.Empty{}, dtmgrpc.DtmError2GrpcError(r)
}
func (s *dtmServer) RegisterBranch(ctx context.Context, in *pb.DtmBranchRequest) (*emptypb.Empty, error) {
- r, err := svcRegisterBranch(in.TransType, &TransBranch{
+ r := svcRegisterBranch(in.TransType, &TransBranch{
Gid: in.Gid,
BranchID: in.BranchID,
Status: dtmcli.StatusPrepared,
BinData: in.BusiPayload,
}, in.Data)
- return &emptypb.Empty{}, dtmgimp.Result2Error(r, err)
+ return &emptypb.Empty{}, dtmgrpc.DtmError2GrpcError(r)
}
diff --git a/dtmsvr/api_http.go b/dtmsvr/api_http.go
index 5102506..6027100 100644
--- a/dtmsvr/api_http.go
+++ b/dtmsvr/api_http.go
@@ -9,23 +9,23 @@ package dtmsvr
import (
"errors"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmutil"
"github.com/gin-gonic/gin"
"github.com/prometheus/client_golang/prometheus/promhttp"
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
)
func addRoute(engine *gin.Engine) {
- engine.GET("/api/dtmsvr/newGid", common.WrapHandler(newGid))
- engine.POST("/api/dtmsvr/prepare", common.WrapHandler(prepare))
- engine.POST("/api/dtmsvr/submit", common.WrapHandler(submit))
- engine.POST("/api/dtmsvr/abort", common.WrapHandler(abort))
- engine.POST("/api/dtmsvr/registerBranch", common.WrapHandler(registerBranch))
- engine.POST("/api/dtmsvr/registerXaBranch", common.WrapHandler(registerBranch)) // compatible for old sdk
- engine.POST("/api/dtmsvr/registerTccBranch", common.WrapHandler(registerBranch)) // compatible for old sdk
- engine.GET("/api/dtmsvr/query", common.WrapHandler(query))
- engine.GET("/api/dtmsvr/all", common.WrapHandler(all))
+ engine.GET("/api/dtmsvr/newGid", dtmutil.WrapHandler2(newGid))
+ engine.POST("/api/dtmsvr/prepare", dtmutil.WrapHandler2(prepare))
+ engine.POST("/api/dtmsvr/submit", dtmutil.WrapHandler2(submit))
+ engine.POST("/api/dtmsvr/abort", dtmutil.WrapHandler2(abort))
+ engine.POST("/api/dtmsvr/registerBranch", dtmutil.WrapHandler2(registerBranch))
+ engine.POST("/api/dtmsvr/registerXaBranch", dtmutil.WrapHandler2(registerBranch)) // compatible for old sdk
+ engine.POST("/api/dtmsvr/registerTccBranch", dtmutil.WrapHandler2(registerBranch)) // compatible for old sdk
+ engine.GET("/api/dtmsvr/query", dtmutil.WrapHandler2(query))
+ engine.GET("/api/dtmsvr/all", dtmutil.WrapHandler2(all))
// add prometheus exporter
h := promhttp.Handler()
@@ -34,23 +34,23 @@ func addRoute(engine *gin.Engine) {
})
}
-func newGid(c *gin.Context) (interface{}, error) {
- return map[string]interface{}{"gid": GenGid(), "dtm_result": dtmcli.ResultSuccess}, nil
+func newGid(c *gin.Context) interface{} {
+ return map[string]interface{}{"gid": GenGid(), "dtm_result": dtmcli.ResultSuccess}
}
-func prepare(c *gin.Context) (interface{}, error) {
+func prepare(c *gin.Context) interface{} {
return svcPrepare(TransFromContext(c))
}
-func submit(c *gin.Context) (interface{}, error) {
+func submit(c *gin.Context) interface{} {
return svcSubmit(TransFromContext(c))
}
-func abort(c *gin.Context) (interface{}, error) {
+func abort(c *gin.Context) interface{} {
return svcAbort(TransFromContext(c))
}
-func registerBranch(c *gin.Context) (interface{}, error) {
+func registerBranch(c *gin.Context) interface{} {
data := map[string]string{}
err := c.BindJSON(&data)
e2p(err)
@@ -63,19 +63,19 @@ func registerBranch(c *gin.Context) (interface{}, error) {
return svcRegisterBranch(data["trans_type"], &branch, data)
}
-func query(c *gin.Context) (interface{}, error) {
+func query(c *gin.Context) interface{} {
gid := c.Query("gid")
if gid == "" {
- return nil, errors.New("no gid specified")
+ return errors.New("no gid specified")
}
trans := GetStore().FindTransGlobalStore(gid)
branches := GetStore().FindBranches(gid)
- return map[string]interface{}{"transaction": trans, "branches": branches}, nil
+ return map[string]interface{}{"transaction": trans, "branches": branches}
}
-func all(c *gin.Context) (interface{}, error) {
+func all(c *gin.Context) interface{} {
position := c.Query("position")
slimit := dtmimp.OrString(c.Query("limit"), "100")
globals := GetStore().ScanTransGlobalStores(&position, int64(dtmimp.MustAtoi(slimit)))
- return map[string]interface{}{"transactions": globals, "next_position": position}, nil
+ return map[string]interface{}{"transactions": globals, "next_position": position}
}
diff --git a/dtmsvr/config/config.go b/dtmsvr/config/config.go
new file mode 100644
index 0000000..cc74975
--- /dev/null
+++ b/dtmsvr/config/config.go
@@ -0,0 +1,107 @@
+package config
+
+import (
+ "encoding/json"
+ "io/ioutil"
+
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "gopkg.in/yaml.v2"
+)
+
+const (
+ // DtmMetricsPort is metric port
+ DtmMetricsPort = 8889
+ // Mysql is mysql driver
+ Mysql = "mysql"
+ // Redis is redis driver
+ Redis = "redis"
+ // BoltDb is boltdb driver
+ BoltDb = "boltdb"
+ // Postgres is postgres driver
+ Postgres = "postgres"
+)
+
+// MicroService config type for micro service
+type MicroService struct {
+ Driver string `yaml:"Driver" default:"default"`
+ Target string `yaml:"Target"`
+ EndPoint string `yaml:"EndPoint"`
+}
+
+// Log config customize log
+type Log struct {
+ Level string `yaml:"Level" default:"info"`
+ Output string `yaml:"Output" default:"console"`
+ FileName string `yaml:"FileName" default:"/tmp/dtm.log"`
+ FileMaxSize int64 `yaml:"FileMaxSize" default:"10"`
+ FileMaxBackups int64 `yaml:"FileMaxBackups" default:"5"`
+ FileMaxAge int64 `yaml:"FileMaxAge" default:"30"`
+ FileCompress int64 `yaml:"FileCompress" default:"0"`
+}
+
+// Store defines storage relevant info
+type Store struct {
+ Driver string `yaml:"Driver" default:"boltdb"`
+ Host string `yaml:"Host"`
+ Port int64 `yaml:"Port"`
+ User string `yaml:"User"`
+ Password string `yaml:"Password"`
+ MaxOpenConns int64 `yaml:"MaxOpenConns" default:"500"`
+ MaxIdleConns int64 `yaml:"MaxIdleConns" default:"500"`
+ ConnMaxLifeTime int64 `yaml:"ConnMaxLifeTime" default:"5"`
+ DataExpire int64 `yaml:"DataExpire" default:"604800"` // Trans data will expire in 7 days. only for redis/boltdb.
+ RedisPrefix string `yaml:"RedisPrefix" default:"{a}"` // Redis storage prefix. store data to only one slot in cluster
+ TransGlobalTable string `yaml:"TransGlobalTable" default:"dtm.trans_global"`
+ TransBranchOpTable string `yaml:"TransBranchOpTable" default:"dtm.trans_branch_op"`
+}
+
+// IsDB checks config driver is mysql or postgres
+func (s *Store) IsDB() bool {
+ return s.Driver == dtmcli.DBTypeMysql || s.Driver == dtmcli.DBTypePostgres
+}
+
+// GetDBConf returns db conf info
+func (s *Store) GetDBConf() dtmcli.DBConf {
+ return dtmcli.DBConf{
+ Driver: s.Driver,
+ Host: s.Host,
+ Port: s.Port,
+ User: s.User,
+ Password: s.Password,
+ }
+}
+
+type configType struct {
+ Store Store `yaml:"Store"`
+ TransCronInterval int64 `yaml:"TransCronInterval" default:"3"`
+ TimeoutToFail int64 `yaml:"TimeoutToFail" default:"35"`
+ RetryInterval int64 `yaml:"RetryInterval" default:"10"`
+ RequestTimeout int64 `yaml:"RequestTimeout" default:"3"`
+ HTTPPort int64 `yaml:"HttpPort" default:"36789"`
+ GrpcPort int64 `yaml:"GrpcPort" default:"36790"`
+ MicroService MicroService `yaml:"MicroService"`
+ UpdateBranchSync int64 `yaml:"UpdateBranchSync"`
+ UpdateBranchAsyncGoroutineNum int64 `yaml:"UpdateBranchAsyncGoroutineNum" default:"1"`
+ Log Log `yaml:"Log"`
+}
+
+// Config 配置
+var Config = configType{}
+
+// MustLoadConfig load config from env and file
+func MustLoadConfig(confFile string) {
+ loadFromEnv("", &Config)
+ if confFile != "" {
+ cont, err := ioutil.ReadFile(confFile)
+ logger.FatalIfError(err)
+ err = yaml.UnmarshalStrict(cont, &Config)
+ logger.FatalIfError(err)
+ }
+ scont, err := json.MarshalIndent(&Config, "", " ")
+ logger.FatalIfError(err)
+ logger.Infof("config file: %s loaded config is: \n%s", confFile, scont)
+ err = checkConfig(&Config)
+ logger.FatalfIf(err != nil, `config error: '%v'.
+ please visit http://d.dtm.pub to see the config document.`, err)
+}
diff --git a/dtmsvr/config/config_test.go b/dtmsvr/config/config_test.go
new file mode 100644
index 0000000..f7ac1e8
--- /dev/null
+++ b/dtmsvr/config/config_test.go
@@ -0,0 +1,84 @@
+package config
+
+import (
+ "errors"
+ "os"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestLoadFromEnv(t *testing.T) {
+ assert.Equal(t, "MICRO_SERVICE_DRIVER", toUnderscoreUpper("MicroService_Driver"))
+
+ ms := MicroService{}
+ os.Setenv("T_DRIVER", "d1")
+ loadFromEnv("T", &ms)
+ assert.Equal(t, "d1", ms.Driver)
+}
+
+func TestLoadConfig(t *testing.T) {
+ MustLoadConfig("../../conf.sample.yml")
+}
+func TestCheckConfig(t *testing.T) {
+ conf := Config
+ conf.RetryInterval = 1
+ retryIntervalErr := checkConfig(&conf)
+ retryIntervalExpect := errors.New("RetryInterval should not be less than 10")
+ assert.Equal(t, retryIntervalErr, retryIntervalExpect)
+
+ conf.RetryInterval = 10
+ conf.TimeoutToFail = 5
+ timeoutToFailErr := checkConfig(&conf)
+ timeoutToFailExpect := errors.New("TimeoutToFail should not be less than RetryInterval")
+ assert.Equal(t, timeoutToFailErr, timeoutToFailExpect)
+
+ conf.TimeoutToFail = 20
+ driverErr := checkConfig(&conf)
+ assert.Equal(t, driverErr, nil)
+
+ conf.Store = Store{Driver: Mysql}
+ hostErr := checkConfig(&conf)
+ hostExpect := errors.New("Db host not valid ")
+ assert.Equal(t, hostErr, hostExpect)
+
+ conf.Store = Store{Driver: Mysql, Host: "127.0.0.1"}
+ portErr := checkConfig(&conf)
+ portExpect := errors.New("Db port not valid ")
+ assert.Equal(t, portErr, portExpect)
+
+ conf.Store = Store{Driver: Mysql, Host: "127.0.0.1", Port: 8686}
+ userErr := checkConfig(&conf)
+ userExpect := errors.New("Db user not valid ")
+ assert.Equal(t, userErr, userExpect)
+
+ conf.Store = Store{Driver: Redis, Host: "", Port: 8686}
+ assert.Equal(t, errors.New("Redis host not valid"), checkConfig(&conf))
+
+ conf.Store = Store{Driver: Redis, Host: "127.0.0.1", Port: 0}
+ assert.Equal(t, errors.New("Redis port not valid"), checkConfig(&conf))
+
+}
+
+func TestConfig(t *testing.T) {
+ testConfigStringField(&Config.Store.Driver, "", t)
+ testConfigStringField(&Config.Store.User, "", t)
+ testConfigIntField(&Config.RetryInterval, 9, t)
+ testConfigIntField(&Config.TimeoutToFail, 9, t)
+}
+
+func testConfigStringField(fd *string, val string, t *testing.T) {
+ old := *fd
+ *fd = val
+ str := checkConfig(&Config)
+ assert.NotEqual(t, "", str)
+ *fd = old
+}
+
+func testConfigIntField(fd *int64, val int64, t *testing.T) {
+ old := *fd
+ *fd = val
+ str := checkConfig(&Config)
+ assert.NotEqual(t, "", str)
+ *fd = old
+}
diff --git a/common/config_utils.go b/dtmsvr/config/config_utils.go
similarity index 53%
rename from common/config_utils.go
rename to dtmsvr/config/config_utils.go
index 44c93f6..f588eee 100644
--- a/common/config_utils.go
+++ b/dtmsvr/config/config_utils.go
@@ -1,13 +1,14 @@
-package common
+package config
import (
+ "errors"
"fmt"
"os"
"reflect"
"regexp"
"strings"
- "github.com/yedf/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
)
func loadFromEnv(prefix string, conf interface{}) {
@@ -48,8 +49,42 @@ func loadFromEnvInner(prefix string, conf reflect.Value, defaultValue string) {
func toUnderscoreUpper(key string) string {
key = strings.Trim(key, "_")
+ matchLastCap := regexp.MustCompile("([A-Z])([A-Z][a-z])")
+ s2 := matchLastCap.ReplaceAllString(key, "${1}_${2}")
+
matchFirstCap := regexp.MustCompile("([a-z])([A-Z]+)")
- s2 := matchFirstCap.ReplaceAllString(key, "${1}_${2}")
- // dtmimp.Logf("loading from env: %s", strings.ToUpper(s2))
+ s2 = matchFirstCap.ReplaceAllString(s2, "${1}_${2}")
+ // logger.Infof("loading from env: %s", strings.ToUpper(s2))
return strings.ToUpper(s2)
}
+
+func checkConfig(conf *configType) error {
+ if conf.RetryInterval < 10 {
+ return errors.New("RetryInterval should not be less than 10")
+ }
+ if conf.TimeoutToFail < conf.RetryInterval {
+ return errors.New("TimeoutToFail should not be less than RetryInterval")
+ }
+ switch conf.Store.Driver {
+ case BoltDb:
+ return nil
+ case Mysql, Postgres:
+ if conf.Store.Host == "" {
+ return errors.New("Db host not valid ")
+ }
+ if conf.Store.Port == 0 {
+ return errors.New("Db port not valid ")
+ }
+ if conf.Store.User == "" {
+ return errors.New("Db user not valid ")
+ }
+ case Redis:
+ if conf.Store.Host == "" {
+ return errors.New("Redis host not valid")
+ }
+ if conf.Store.Port == 0 {
+ return errors.New("Redis port not valid")
+ }
+ }
+ return nil
+}
diff --git a/dtmsvr/cron.go b/dtmsvr/cron.go
index c4ef282..a768fa1 100644
--- a/dtmsvr/cron.go
+++ b/dtmsvr/cron.go
@@ -7,19 +7,22 @@
package dtmsvr
import (
+ "errors"
"fmt"
"math/rand"
"runtime/debug"
"time"
- "github.com/yedf/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
)
// NowForwardDuration will be set in test, trans may be timeout
-var NowForwardDuration time.Duration = time.Duration(0)
+var NowForwardDuration = time.Duration(0)
// CronForwardDuration will be set in test. cron will fetch trans which expire in CronForwardDuration
-var CronForwardDuration time.Duration = time.Duration(0)
+var CronForwardDuration = time.Duration(0)
// CronTransOnce cron expired trans. use expireIn as expire time
func CronTransOnce() (gid string) {
@@ -30,7 +33,9 @@ func CronTransOnce() (gid string) {
}
gid = trans.Gid
trans.WaitResult = true
- trans.Process()
+ branches := GetStore().FindBranches(gid)
+ err := trans.Process(branches)
+ dtmimp.PanicIf(err != nil && !errors.Is(err, dtmcli.ErrFailure), err)
return
}
@@ -49,12 +54,13 @@ func lockOneTrans(expireIn time.Duration) *TransGlobal {
if global == nil {
return nil
}
+ logger.Infof("cron job return a trans: %s", global.String())
return &TransGlobal{TransGlobalStore: *global}
}
func handlePanic(perr *error) {
if err := recover(); err != nil {
- dtmimp.LogRedf("----recovered panic %v\n%s", err, string(debug.Stack()))
+ logger.Errorf("----recovered panic %v\n%s", err, string(debug.Stack()))
if perr != nil {
*perr = fmt.Errorf("dtm panic: %v", err)
}
@@ -62,8 +68,8 @@ func handlePanic(perr *error) {
}
func sleepCronTime() {
- normal := time.Duration((float64(config.TransCronInterval) - rand.Float64()) * float64(time.Second))
+ normal := time.Duration((float64(conf.TransCronInterval) - rand.Float64()) * float64(time.Second))
interval := dtmimp.If(CronForwardDuration > 0, 1*time.Millisecond, normal).(time.Duration)
- dtmimp.Logf("sleeping for %v milli", interval/time.Microsecond)
+ logger.Debugf("sleeping for %v milli", interval/time.Microsecond)
time.Sleep(interval)
}
diff --git a/dtmsvr/storage/boltdb/boltdb.go b/dtmsvr/storage/boltdb/boltdb.go
index f5b9670..9202d23 100644
--- a/dtmsvr/storage/boltdb/boltdb.go
+++ b/dtmsvr/storage/boltdb/boltdb.go
@@ -1,48 +1,59 @@
+/*
+ * Copyright (c) 2021 yedf. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
package boltdb
import (
"fmt"
"strings"
- "sync"
"time"
bolt "go.etcd.io/bbolt"
- "gorm.io/gorm"
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmsvr/storage"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/dtmsvr/storage"
+ "github.com/dtm-labs/dtm/dtmutil"
)
-var config = &common.Config
+// Store implements storage.Store, and storage with boltdb
+type Store struct {
+ boltDb *bolt.DB
-type BoltdbStore struct {
+ dataExpire int64
+ retryInterval int64
}
-var boltDb *bolt.DB = nil
-var boltOnce sync.Once
+// NewStore will return the boltdb implement
+// TODO: change to options
+func NewStore(dataExpire int64, retryInterval int64) *Store {
+ s := &Store{
+ dataExpire: dataExpire,
+ retryInterval: retryInterval,
+ }
-func boltGet() *bolt.DB {
- boltOnce.Do(func() {
- db, err := bolt.Open("./dtm.bolt", 0666, &bolt.Options{Timeout: 1 * time.Second})
- dtmimp.E2P(err)
+ db, err := bolt.Open("./dtm.bolt", 0666, &bolt.Options{Timeout: 1 * time.Second})
+ dtmimp.E2P(err)
- // NOTE: we must ensure all buckets is exists before we use it
- err = initializeBuckets(db)
- dtmimp.E2P(err)
+ // NOTE: we must ensure all buckets is exists before we use it
+ err = initializeBuckets(db)
+ dtmimp.E2P(err)
- // TODO:
- // 1. refactor this code
- // 2. make cleanup run period, to avoid the file growup when server long-running
- err = cleanupExpiredData(
- time.Duration(common.Config.Store.DataExpire)*time.Second,
- db,
- )
- dtmimp.E2P(err)
+ // TODO:
+ // 1. refactor this code
+ // 2. make cleanup run period, to avoid the file growup when server long-running
+ err = cleanupExpiredData(
+ time.Duration(dataExpire)*time.Second,
+ db,
+ )
+ dtmimp.E2P(err)
- boltDb = db
- })
- return boltDb
+ s.boltDb = db
+ return s
}
func initializeBuckets(db *bolt.DB) error {
@@ -100,10 +111,10 @@ func cleanupGlobalWithGids(t *bolt.Tx, gids map[string]struct{}) {
return
}
- dtmimp.Logf("Start to cleanup %d gids", len(gids))
+ logger.Debugf("Start to cleanup %d gids", len(gids))
for gid := range gids {
- dtmimp.Logf("Start to delete gid: %s", gid)
- bucket.Delete([]byte(gid))
+ logger.Debugf("Start to delete gid: %s", gid)
+ dtmimp.E2P(bucket.Delete([]byte(gid)))
}
}
@@ -129,10 +140,10 @@ func cleanupBranchWithGids(t *bolt.Tx, gids map[string]struct{}) {
}
}
- dtmimp.Logf("Start to cleanup %d branches", len(branchKeys))
+ logger.Debugf("Start to cleanup %d branches", len(branchKeys))
for _, key := range branchKeys {
- dtmimp.Logf("Start to delete branch: %s", key)
- bucket.Delete([]byte(key))
+ logger.Debugf("Start to delete branch: %s", key)
+ dtmimp.E2P(bucket.Delete([]byte(key)))
}
}
@@ -155,10 +166,10 @@ func cleanupIndexWithGids(t *bolt.Tx, gids map[string]struct{}) {
}
}
- dtmimp.Logf("Start to cleanup %d indexes", len(indexKeys))
+ logger.Debugf("Start to cleanup %d indexes", len(indexKeys))
for _, key := range indexKeys {
- dtmimp.Logf("Start to delete index: %s", key)
- bucket.Delete([]byte(key))
+ logger.Debugf("Start to delete index: %s", key)
+ dtmimp.E2P(bucket.Delete([]byte(key)))
}
}
@@ -225,27 +236,35 @@ func tPutIndex(t *bolt.Tx, unix int64, gid string) {
dtmimp.E2P(err)
}
-func (s *BoltdbStore) Ping() error {
+// Ping execs ping cmd to boltdb
+func (s *Store) Ping() error {
return nil
}
-func (s *BoltdbStore) PopulateData(skipDrop bool) {
+// PopulateData populates data to boltdb
+func (s *Store) PopulateData(skipDrop bool) {
if !skipDrop {
- err := boltGet().Update(func(t *bolt.Tx) error {
- t.DeleteBucket(bucketIndex)
- t.DeleteBucket(bucketBranches)
- t.DeleteBucket(bucketGlobal)
- t.CreateBucket(bucketIndex)
- t.CreateBucket(bucketBranches)
- t.CreateBucket(bucketGlobal)
+ err := s.boltDb.Update(func(t *bolt.Tx) error {
+ dtmimp.E2P(t.DeleteBucket(bucketIndex))
+ dtmimp.E2P(t.DeleteBucket(bucketBranches))
+ dtmimp.E2P(t.DeleteBucket(bucketGlobal))
+ _, err := t.CreateBucket(bucketIndex)
+ dtmimp.E2P(err)
+ _, err = t.CreateBucket(bucketBranches)
+ dtmimp.E2P(err)
+ _, err = t.CreateBucket(bucketGlobal)
+ dtmimp.E2P(err)
+
return nil
})
dtmimp.E2P(err)
+ logger.Infof("Reset all data for boltdb")
}
}
-func (s *BoltdbStore) FindTransGlobalStore(gid string) (trans *storage.TransGlobalStore) {
- err := boltGet().View(func(t *bolt.Tx) error {
+// FindTransGlobalStore finds GlobalTrans data by gid
+func (s *Store) FindTransGlobalStore(gid string) (trans *storage.TransGlobalStore) {
+ err := s.boltDb.View(func(t *bolt.Tx) error {
trans = tGetGlobal(t, gid)
return nil
})
@@ -253,9 +272,10 @@ func (s *BoltdbStore) FindTransGlobalStore(gid string) (trans *storage.TransGlob
return
}
-func (s *BoltdbStore) ScanTransGlobalStores(position *string, limit int64) []storage.TransGlobalStore {
+// ScanTransGlobalStores lists GlobalTrans data
+func (s *Store) ScanTransGlobalStores(position *string, limit int64) []storage.TransGlobalStore {
globals := []storage.TransGlobalStore{}
- err := boltGet().View(func(t *bolt.Tx) error {
+ err := s.boltDb.View(func(t *bolt.Tx) error {
cursor := t.Bucket(bucketGlobal).Cursor()
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
if string(k) == *position {
@@ -279,9 +299,10 @@ func (s *BoltdbStore) ScanTransGlobalStores(position *string, limit int64) []sto
return globals
}
-func (s *BoltdbStore) FindBranches(gid string) []storage.TransBranchStore {
- var branches []storage.TransBranchStore = nil
- err := boltGet().View(func(t *bolt.Tx) error {
+// FindBranches finds Branch data by gid
+func (s *Store) FindBranches(gid string) []storage.TransBranchStore {
+ var branches []storage.TransBranchStore
+ err := s.boltDb.View(func(t *bolt.Tx) error {
branches = tGetBranches(t, gid)
return nil
})
@@ -289,12 +310,14 @@ func (s *BoltdbStore) FindBranches(gid string) []storage.TransBranchStore {
return branches
}
-func (s *BoltdbStore) UpdateBranchesSql(branches []storage.TransBranchStore, updates []string) *gorm.DB {
- return nil // not implemented
+// UpdateBranches update branches info
+func (s *Store) UpdateBranches(branches []storage.TransBranchStore, updates []string) (int, error) {
+ return 0, nil // not implemented
}
-func (s *BoltdbStore) LockGlobalSaveBranches(gid string, status string, branches []storage.TransBranchStore, branchStart int) {
- err := boltGet().Update(func(t *bolt.Tx) error {
+// LockGlobalSaveBranches creates branches
+func (s *Store) LockGlobalSaveBranches(gid string, status string, branches []storage.TransBranchStore, branchStart int) {
+ err := s.boltDb.Update(func(t *bolt.Tx) error {
g := tGetGlobal(t, gid)
if g == nil {
return storage.ErrNotFound
@@ -308,8 +331,9 @@ func (s *BoltdbStore) LockGlobalSaveBranches(gid string, status string, branches
dtmimp.E2P(err)
}
-func (s *BoltdbStore) MaySaveNewTrans(global *storage.TransGlobalStore, branches []storage.TransBranchStore) error {
- return boltGet().Update(func(t *bolt.Tx) error {
+// MaySaveNewTrans creates a new trans
+func (s *Store) MaySaveNewTrans(global *storage.TransGlobalStore, branches []storage.TransBranchStore) error {
+ return s.boltDb.Update(func(t *bolt.Tx) error {
g := tGetGlobal(t, global.Gid)
if g != nil {
return storage.ErrUniqueConflict
@@ -321,10 +345,11 @@ func (s *BoltdbStore) MaySaveNewTrans(global *storage.TransGlobalStore, branches
})
}
-func (s *BoltdbStore) ChangeGlobalStatus(global *storage.TransGlobalStore, newStatus string, updates []string, finished bool) {
+// ChangeGlobalStatus changes global trans status
+func (s *Store) ChangeGlobalStatus(global *storage.TransGlobalStore, newStatus string, updates []string, finished bool) {
old := global.Status
global.Status = newStatus
- err := boltGet().Update(func(t *bolt.Tx) error {
+ err := s.boltDb.Update(func(t *bolt.Tx) error {
g := tGetGlobal(t, global.Gid)
if g == nil || g.Status != old {
return storage.ErrNotFound
@@ -338,12 +363,13 @@ func (s *BoltdbStore) ChangeGlobalStatus(global *storage.TransGlobalStore, newSt
dtmimp.E2P(err)
}
-func (s *BoltdbStore) TouchCronTime(global *storage.TransGlobalStore, nextCronInterval int64) {
+// TouchCronTime updates cronTime
+func (s *Store) TouchCronTime(global *storage.TransGlobalStore, nextCronInterval int64) {
oldUnix := global.NextCronTime.Unix()
- global.NextCronTime = common.GetNextTime(nextCronInterval)
- global.UpdateTime = common.GetNextTime(0)
+ global.NextCronTime = dtmutil.GetNextTime(nextCronInterval)
+ global.UpdateTime = dtmutil.GetNextTime(0)
global.NextCronInterval = nextCronInterval
- err := boltGet().Update(func(t *bolt.Tx) error {
+ err := s.boltDb.Update(func(t *bolt.Tx) error {
g := tGetGlobal(t, global.Gid)
if g == nil || g.Gid != global.Gid {
return storage.ErrNotFound
@@ -356,13 +382,14 @@ func (s *BoltdbStore) TouchCronTime(global *storage.TransGlobalStore, nextCronIn
dtmimp.E2P(err)
}
-func (s *BoltdbStore) LockOneGlobalTrans(expireIn time.Duration) *storage.TransGlobalStore {
- var trans *storage.TransGlobalStore = nil
+// LockOneGlobalTrans finds GlobalTrans
+func (s *Store) LockOneGlobalTrans(expireIn time.Duration) *storage.TransGlobalStore {
+ var trans *storage.TransGlobalStore
min := fmt.Sprintf("%d", time.Now().Add(expireIn).Unix())
- next := time.Now().Add(time.Duration(config.RetryInterval) * time.Second)
- err := boltGet().Update(func(t *bolt.Tx) error {
+ next := time.Now().Add(time.Duration(s.retryInterval) * time.Second)
+ err := s.boltDb.Update(func(t *bolt.Tx) error {
cursor := t.Bucket(bucketIndex).Cursor()
- for trans == nil {
+ for trans == nil || trans.Status == dtmcli.StatusSucceed || trans.Status == dtmcli.StatusFailed {
k, v := cursor.First()
if k == nil || string(k) > min {
return storage.ErrNotFound
diff --git a/dtmsvr/storage/boltdb/boltdb_test.go b/dtmsvr/storage/boltdb/boltdb_test.go
index 7388222..70048ea 100644
--- a/dtmsvr/storage/boltdb/boltdb_test.go
+++ b/dtmsvr/storage/boltdb/boltdb_test.go
@@ -1,3 +1,9 @@
+/*
+ * Copyright (c) 2021 yedf. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
package boltdb
import (
@@ -8,8 +14,8 @@ import (
. "github.com/onsi/gomega"
bolt "go.etcd.io/bbolt"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmsvr/storage"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmsvr/storage"
)
func TestInitializeBuckets(t *testing.T) {
diff --git a/dtmsvr/storage/redis.go b/dtmsvr/storage/redis.go
deleted file mode 100644
index dcaed8a..0000000
--- a/dtmsvr/storage/redis.go
+++ /dev/null
@@ -1,257 +0,0 @@
-package storage
-
-import (
- "context"
- "fmt"
- "time"
-
- "github.com/go-redis/redis/v8"
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "gorm.io/gorm"
-)
-
-var ctx context.Context = context.Background()
-
-type RedisStore struct {
-}
-
-func (s *RedisStore) Ping() error {
- _, err := redisGet().Ping(ctx).Result()
- return err
-}
-
-func (s *RedisStore) PopulateData(skipDrop bool) {
- _, err := redisGet().FlushAll(ctx).Result()
- dtmimp.PanicIf(err != nil, err)
-}
-
-func (s *RedisStore) FindTransGlobalStore(gid string) *TransGlobalStore {
- r, err := redisGet().Get(ctx, config.Store.RedisPrefix+"_g_"+gid).Result()
- if err == redis.Nil {
- return nil
- }
- dtmimp.E2P(err)
- trans := &TransGlobalStore{}
- dtmimp.MustUnmarshalString(r, trans)
- return trans
-}
-
-func (s *RedisStore) ScanTransGlobalStores(position *string, limit int64) []TransGlobalStore {
- lid := uint64(0)
- if *position != "" {
- lid = uint64(dtmimp.MustAtoi(*position))
- }
- keys, cursor, err := redisGet().Scan(ctx, lid, config.Store.RedisPrefix+"_g_*", limit).Result()
- dtmimp.E2P(err)
- globals := []TransGlobalStore{}
- if len(keys) > 0 {
- values, err := redisGet().MGet(ctx, keys...).Result()
- dtmimp.E2P(err)
- for _, v := range values {
- global := TransGlobalStore{}
- dtmimp.MustUnmarshalString(v.(string), &global)
- globals = append(globals, global)
- }
- }
- if cursor > 0 {
- *position = fmt.Sprintf("%d", cursor)
- } else {
- *position = ""
- }
- return globals
-}
-
-func (s *RedisStore) FindBranches(gid string) []TransBranchStore {
- sa, err := redisGet().LRange(ctx, config.Store.RedisPrefix+"_b_"+gid, 0, -1).Result()
- dtmimp.E2P(err)
- branches := make([]TransBranchStore, len(sa))
- for k, v := range sa {
- dtmimp.MustUnmarshalString(v, &branches[k])
- }
- return branches
-}
-
-func (s *RedisStore) UpdateBranchesSql(branches []TransBranchStore, updates []string) *gorm.DB {
- return nil // not implemented
-}
-
-type argList struct {
- Keys []string
- List []interface{}
-}
-
-func newArgList() *argList {
- a := &argList{}
- return a.AppendRaw(config.Store.RedisPrefix).AppendObject(config.Store.DataExpire)
-}
-
-func (a *argList) AppendGid(gid string) *argList {
- a.Keys = append(a.Keys, config.Store.RedisPrefix+"_g_"+gid)
- a.Keys = append(a.Keys, config.Store.RedisPrefix+"_b_"+gid)
- a.Keys = append(a.Keys, config.Store.RedisPrefix+"_u")
- return a
-}
-
-func (a *argList) AppendRaw(v interface{}) *argList {
- a.List = append(a.List, v)
- return a
-}
-
-func (a *argList) AppendObject(v interface{}) *argList {
- return a.AppendRaw(dtmimp.MustMarshalString(v))
-}
-
-func (a *argList) AppendBranches(branches []TransBranchStore) *argList {
- for _, b := range branches {
- a.AppendRaw(dtmimp.MustMarshalString(b))
- }
- return a
-}
-
-func handleRedisResult(ret interface{}, err error) (string, error) {
- dtmimp.Logf("result is: '%v', err: '%v'", ret, err)
- if err != nil && err != redis.Nil {
- return "", err
- }
- s, _ := ret.(string)
- err = map[string]error{
- "NOT_FOUND": ErrNotFound,
- "UNIQUE_CONFLICT": ErrUniqueConflict,
- }[s]
- return s, err
-}
-
-func callLua(a *argList, lua string) (string, error) {
- dtmimp.Logf("calling lua. args: %v\nlua:%s", a, lua)
- ret, err := redisGet().Eval(ctx, lua, a.Keys, a.List...).Result()
- return handleRedisResult(ret, err)
-}
-
-func (s *RedisStore) MaySaveNewTrans(global *TransGlobalStore, branches []TransBranchStore) error {
- a := newArgList().
- AppendGid(global.Gid).
- AppendObject(global).
- AppendRaw(global.NextCronTime.Unix()).
- AppendBranches(branches)
- global.Steps = nil
- global.Payloads = nil
- _, err := callLua(a, `-- MaySaveNewTrans
-local gs = cjson.decode(ARGV[3])
-local g = redis.call('GET', KEYS[1])
-if g ~= false then
- return 'UNIQUE_CONFLICT'
-end
-
-redis.call('SET', KEYS[1], ARGV[3], 'EX', ARGV[2])
-redis.call('ZADD', KEYS[3], ARGV[4], gs.gid)
-for k = 5, table.getn(ARGV) do
- redis.call('RPUSH', KEYS[2], ARGV[k])
-end
-redis.call('EXPIRE', KEYS[2], ARGV[2])
-`)
- return err
-}
-
-func (s *RedisStore) LockGlobalSaveBranches(gid string, status string, branches []TransBranchStore, branchStart int) {
- args := newArgList().
- AppendGid(gid).
- AppendObject(&TransGlobalStore{Gid: gid, Status: status}).
- AppendRaw(branchStart).
- AppendBranches(branches)
- _, err := callLua(args, `
-local gs = cjson.decode(ARGV[3])
-local g = redis.call('GET', KEYS[1])
-if (g == false) then
- return 'NOT_FOUND'
-end
-local js = cjson.decode(g)
-if js.status ~= gs.status then
- return 'NOT_FOUND'
-end
-local start = ARGV[4]
-for k = 5, table.getn(ARGV) do
- if start == "-1" then
- redis.call('RPUSH', KEYS[2], ARGV[k])
- else
- redis.call('LSET', KEYS[2], start+k-5, ARGV[k])
- end
-end
-redis.call('EXPIRE', KEYS[2], ARGV[2])
- `)
- dtmimp.E2P(err)
-}
-
-func (s *RedisStore) ChangeGlobalStatus(global *TransGlobalStore, newStatus string, updates []string, finished bool) {
- old := global.Status
- global.Status = newStatus
- args := newArgList().AppendGid(global.Gid).AppendObject(global).AppendRaw(old).AppendRaw(finished)
- _, err := callLua(args, `-- ChangeGlobalStatus
-local gs = cjson.decode(ARGV[3])
-local old = redis.call('GET', KEYS[1])
-if old == false then
- return 'NOT_FOUND'
-end
-local os = cjson.decode(old)
-if os.status ~= ARGV[4] then
- return 'NOT_FOUND'
-end
-redis.call('SET', KEYS[1], ARGV[3], 'EX', ARGV[2])
-redis.log(redis.LOG_WARNING, 'finished: ', ARGV[5])
-if ARGV[5] == '1' then
- redis.call('ZREM', KEYS[3], gs.gid)
-end
-`)
- dtmimp.E2P(err)
-}
-
-func (s *RedisStore) LockOneGlobalTrans(expireIn time.Duration) *TransGlobalStore {
- expired := time.Now().Add(expireIn).Unix()
- next := time.Now().Add(time.Duration(config.RetryInterval) * time.Second).Unix()
- args := newArgList().AppendGid("").AppendRaw(expired).AppendRaw(next)
- lua := `-- LocakOneGlobalTrans
-local r = redis.call('ZRANGE', KEYS[3], 0, 0, 'WITHSCORES')
-local gid = r[1]
-if gid == nil then
- return 'NOT_FOUND'
-end
-
-if tonumber(r[2]) > tonumber(ARGV[3]) then
- return 'NOT_FOUND'
-end
-redis.call('ZADD', KEYS[3], ARGV[4], gid)
-return gid
-`
- for {
- r, err := callLua(args, lua)
- if err == ErrNotFound {
- return nil
- }
- dtmimp.E2P(err)
- global := s.FindTransGlobalStore(r)
- if global != nil {
- return global
- }
- }
-}
-
-func (s *RedisStore) TouchCronTime(global *TransGlobalStore, nextCronInterval int64) {
- global.NextCronTime = common.GetNextTime(nextCronInterval)
- global.UpdateTime = common.GetNextTime(0)
- global.NextCronInterval = nextCronInterval
- args := newArgList().AppendGid(global.Gid).AppendObject(global).AppendRaw(global.NextCronTime.Unix())
- _, err := callLua(args, `-- TouchCronTime
-local g = cjson.decode(ARGV[3])
-local old = redis.call('GET', KEYS[1])
-if old == false then
- return 'NOT_FOUND'
-end
-local os = cjson.decode(old)
-if os.status ~= g.status then
- return 'NOT_FOUND'
-end
-redis.call('ZADD', KEYS[3], ARGV[4], g.gid)
-redis.call('SET', KEYS[1], ARGV[3], 'EX', ARGV[2])
- `)
- dtmimp.E2P(err)
-}
diff --git a/dtmsvr/storage/redis/redis.go b/dtmsvr/storage/redis/redis.go
new file mode 100644
index 0000000..e0903a5
--- /dev/null
+++ b/dtmsvr/storage/redis/redis.go
@@ -0,0 +1,300 @@
+package redis
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "sync"
+ "time"
+
+ "github.com/go-redis/redis/v8"
+
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/dtmsvr/config"
+ "github.com/dtm-labs/dtm/dtmsvr/storage"
+ "github.com/dtm-labs/dtm/dtmutil"
+)
+
+// TODO: optimize this, it's very strange to use pointer to dtmutil.Config
+var conf = &config.Config
+
+// TODO: optimize this, all function should have context as first parameter
+var ctx = context.Background()
+
+// Store is the storage with redis, all transaction information will bachend with redis
+type Store struct {
+}
+
+// Ping execs ping cmd to redis
+func (s *Store) Ping() error {
+ _, err := redisGet().Ping(ctx).Result()
+ return err
+}
+
+// PopulateData populates data to redis
+func (s *Store) PopulateData(skipDrop bool) {
+ if !skipDrop {
+ _, err := redisGet().FlushAll(ctx).Result()
+ logger.Infof("call redis flushall. result: %v", err)
+ dtmimp.PanicIf(err != nil, err)
+ }
+}
+
+// FindTransGlobalStore finds GlobalTrans data by gid
+func (s *Store) FindTransGlobalStore(gid string) *storage.TransGlobalStore {
+ logger.Debugf("calling FindTransGlobalStore: %s", gid)
+ r, err := redisGet().Get(ctx, conf.Store.RedisPrefix+"_g_"+gid).Result()
+ if err == redis.Nil {
+ return nil
+ }
+ dtmimp.E2P(err)
+ trans := &storage.TransGlobalStore{}
+ dtmimp.MustUnmarshalString(r, trans)
+ return trans
+}
+
+// ScanTransGlobalStores lists GlobalTrans data
+func (s *Store) ScanTransGlobalStores(position *string, limit int64) []storage.TransGlobalStore {
+ logger.Debugf("calling ScanTransGlobalStores: %s %d", *position, limit)
+ lid := uint64(0)
+ if *position != "" {
+ lid = uint64(dtmimp.MustAtoi(*position))
+ }
+ keys, cursor, err := redisGet().Scan(ctx, lid, conf.Store.RedisPrefix+"_g_*", limit).Result()
+ dtmimp.E2P(err)
+ globals := []storage.TransGlobalStore{}
+ if len(keys) > 0 {
+ values, err := redisGet().MGet(ctx, keys...).Result()
+ dtmimp.E2P(err)
+ for _, v := range values {
+ global := storage.TransGlobalStore{}
+ dtmimp.MustUnmarshalString(v.(string), &global)
+ globals = append(globals, global)
+ }
+ }
+ if cursor > 0 {
+ *position = fmt.Sprintf("%d", cursor)
+ } else {
+ *position = ""
+ }
+ return globals
+}
+
+// FindBranches finds Branch data by gid
+func (s *Store) FindBranches(gid string) []storage.TransBranchStore {
+ logger.Debugf("calling FindBranches: %s", gid)
+ sa, err := redisGet().LRange(ctx, conf.Store.RedisPrefix+"_b_"+gid, 0, -1).Result()
+ dtmimp.E2P(err)
+ branches := make([]storage.TransBranchStore, len(sa))
+ for k, v := range sa {
+ dtmimp.MustUnmarshalString(v, &branches[k])
+ }
+ return branches
+}
+
+// UpdateBranches updates branches info
+func (s *Store) UpdateBranches(branches []storage.TransBranchStore, updates []string) (int, error) {
+ return 0, nil // not implemented
+}
+
+type argList struct {
+ Keys []string
+ List []interface{}
+}
+
+func newArgList() *argList {
+ a := &argList{}
+ return a.AppendRaw(conf.Store.RedisPrefix).AppendObject(conf.Store.DataExpire)
+}
+
+func (a *argList) AppendGid(gid string) *argList {
+ a.Keys = append(a.Keys, conf.Store.RedisPrefix+"_g_"+gid)
+ a.Keys = append(a.Keys, conf.Store.RedisPrefix+"_b_"+gid)
+ a.Keys = append(a.Keys, conf.Store.RedisPrefix+"_u")
+ a.Keys = append(a.Keys, conf.Store.RedisPrefix+"_s_"+gid)
+ return a
+}
+
+func (a *argList) AppendRaw(v interface{}) *argList {
+ a.List = append(a.List, v)
+ return a
+}
+
+func (a *argList) AppendObject(v interface{}) *argList {
+ return a.AppendRaw(dtmimp.MustMarshalString(v))
+}
+
+func (a *argList) AppendBranches(branches []storage.TransBranchStore) *argList {
+ for _, b := range branches {
+ a.AppendRaw(dtmimp.MustMarshalString(b))
+ }
+ return a
+}
+
+func handleRedisResult(ret interface{}, err error) (string, error) {
+ logger.Debugf("result is: '%v', err: '%v'", ret, err)
+ if err != nil && err != redis.Nil {
+ return "", err
+ }
+ s, _ := ret.(string)
+ err = map[string]error{
+ "NOT_FOUND": storage.ErrNotFound,
+ "UNIQUE_CONFLICT": storage.ErrUniqueConflict,
+ }[s]
+ return s, err
+}
+
+func callLua(a *argList, lua string) (string, error) {
+ logger.Debugf("calling lua. args: %v\nlua:%s", a, lua)
+ ret, err := redisGet().Eval(ctx, lua, a.Keys, a.List...).Result()
+ return handleRedisResult(ret, err)
+}
+
+// MaySaveNewTrans creates a new trans
+func (s *Store) MaySaveNewTrans(global *storage.TransGlobalStore, branches []storage.TransBranchStore) error {
+ a := newArgList().
+ AppendGid(global.Gid).
+ AppendObject(global).
+ AppendRaw(global.NextCronTime.Unix()).
+ AppendRaw(global.Gid).
+ AppendRaw(global.Status).
+ AppendBranches(branches)
+ global.Steps = nil
+ global.Payloads = nil
+ _, err := callLua(a, `-- MaySaveNewTrans
+local g = redis.call('GET', KEYS[1])
+if g ~= false then
+ return 'UNIQUE_CONFLICT'
+end
+
+redis.call('SET', KEYS[1], ARGV[3], 'EX', ARGV[2])
+redis.call('SET', KEYS[4], ARGV[6], 'EX', ARGV[2])
+redis.call('ZADD', KEYS[3], ARGV[4], ARGV[5])
+for k = 7, table.getn(ARGV) do
+ redis.call('RPUSH', KEYS[2], ARGV[k])
+end
+redis.call('EXPIRE', KEYS[2], ARGV[2])
+`)
+ return err
+}
+
+// LockGlobalSaveBranches creates branches
+func (s *Store) LockGlobalSaveBranches(gid string, status string, branches []storage.TransBranchStore, branchStart int) {
+ args := newArgList().
+ AppendGid(gid).
+ AppendRaw(status).
+ AppendRaw(branchStart).
+ AppendBranches(branches)
+ _, err := callLua(args, `-- LockGlobalSaveBranches
+local old = redis.call('GET', KEYS[4])
+if old ~= ARGV[3] then
+ return 'NOT_FOUND'
+end
+local start = ARGV[4]
+for k = 5, table.getn(ARGV) do
+ if start == "-1" then
+ redis.call('RPUSH', KEYS[2], ARGV[k])
+ else
+ redis.call('LSET', KEYS[2], start+k-5, ARGV[k])
+ end
+end
+redis.call('EXPIRE', KEYS[2], ARGV[2])
+ `)
+ dtmimp.E2P(err)
+}
+
+// ChangeGlobalStatus changes global trans status
+func (s *Store) ChangeGlobalStatus(global *storage.TransGlobalStore, newStatus string, updates []string, finished bool) {
+ old := global.Status
+ global.Status = newStatus
+ args := newArgList().
+ AppendGid(global.Gid).
+ AppendObject(global).
+ AppendRaw(old).
+ AppendRaw(finished).
+ AppendRaw(global.Gid).
+ AppendRaw(newStatus)
+ _, err := callLua(args, `-- ChangeGlobalStatus
+local old = redis.call('GET', KEYS[4])
+if old ~= ARGV[4] then
+ return 'NOT_FOUND'
+end
+redis.call('SET', KEYS[1], ARGV[3], 'EX', ARGV[2])
+redis.call('SET', KEYS[4], ARGV[7], 'EX', ARGV[2])
+if ARGV[5] == '1' then
+ redis.call('ZREM', KEYS[3], ARGV[6])
+end
+`)
+ dtmimp.E2P(err)
+}
+
+// LockOneGlobalTrans finds GlobalTrans
+func (s *Store) LockOneGlobalTrans(expireIn time.Duration) *storage.TransGlobalStore {
+ expired := time.Now().Add(expireIn).Unix()
+ next := time.Now().Add(time.Duration(conf.RetryInterval) * time.Second).Unix()
+ args := newArgList().AppendGid("").AppendRaw(expired).AppendRaw(next)
+ lua := `-- LockOneGlobalTrans
+local r = redis.call('ZRANGE', KEYS[3], 0, 0, 'WITHSCORES')
+local gid = r[1]
+if gid == nil then
+ return 'NOT_FOUND'
+end
+
+if tonumber(r[2]) > tonumber(ARGV[3]) then
+ return 'NOT_FOUND'
+end
+redis.call('ZADD', KEYS[3], ARGV[4], gid)
+return gid
+`
+ for {
+ r, err := callLua(args, lua)
+ if errors.Is(err, storage.ErrNotFound) {
+ return nil
+ }
+ dtmimp.E2P(err)
+ global := s.FindTransGlobalStore(r)
+ if global != nil {
+ return global
+ }
+ }
+}
+
+// TouchCronTime updates cronTime
+func (s *Store) TouchCronTime(global *storage.TransGlobalStore, nextCronInterval int64) {
+ global.NextCronTime = dtmutil.GetNextTime(nextCronInterval)
+ global.UpdateTime = dtmutil.GetNextTime(0)
+ global.NextCronInterval = nextCronInterval
+ args := newArgList().
+ AppendGid(global.Gid).
+ AppendObject(global).
+ AppendRaw(global.NextCronTime.Unix()).
+ AppendRaw(global.Status).
+ AppendRaw(global.Gid)
+ _, err := callLua(args, `-- TouchCronTime
+local old = redis.call('GET', KEYS[4])
+if old ~= ARGV[5] then
+ return 'NOT_FOUND'
+end
+redis.call('ZADD', KEYS[3], ARGV[4], ARGV[6])
+redis.call('SET', KEYS[1], ARGV[3], 'EX', ARGV[2])
+ `)
+ dtmimp.E2P(err)
+}
+
+var (
+ rdb *redis.Client
+ once sync.Once
+)
+
+func redisGet() *redis.Client {
+ once.Do(func() {
+ logger.Debugf("connecting to redis: %v", conf.Store)
+ rdb = redis.NewClient(&redis.Options{
+ Addr: fmt.Sprintf("%s:%d", conf.Store.Host, conf.Store.Port),
+ Username: conf.Store.User,
+ Password: conf.Store.Password,
+ })
+ })
+ return rdb
+}
diff --git a/dtmsvr/storage/registry/factory.go b/dtmsvr/storage/registry/factory.go
new file mode 100644
index 0000000..811bbf0
--- /dev/null
+++ b/dtmsvr/storage/registry/factory.go
@@ -0,0 +1,25 @@
+package registry
+
+import (
+ "sync"
+
+ "github.com/dtm-labs/dtm/dtmsvr/storage"
+)
+
+// SingletonFactory is the factory to build store in SINGLETON pattern.
+type SingletonFactory struct {
+ once sync.Once
+
+ store storage.Store
+
+ creatorFunction func() storage.Store
+}
+
+// GetStorage implement the StorageFactory.GetStorage
+func (f *SingletonFactory) GetStorage() storage.Store {
+ f.once.Do(func() {
+ f.store = f.creatorFunction()
+ })
+
+ return f.store
+}
diff --git a/dtmsvr/storage/registry/registry.go b/dtmsvr/storage/registry/registry.go
index 250ffe2..003fd69 100644
--- a/dtmsvr/storage/registry/registry.go
+++ b/dtmsvr/storage/registry/registry.go
@@ -3,22 +3,47 @@ package registry
import (
"time"
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmsvr/storage"
- "github.com/yedf/dtm/dtmsvr/storage/boltdb"
+ "github.com/dtm-labs/dtm/dtmsvr/config"
+ "github.com/dtm-labs/dtm/dtmsvr/storage"
+ "github.com/dtm-labs/dtm/dtmsvr/storage/boltdb"
+ "github.com/dtm-labs/dtm/dtmsvr/storage/redis"
+ "github.com/dtm-labs/dtm/dtmsvr/storage/sql"
)
-var config = &common.Config
+var conf = &config.Config
-var stores map[string]storage.Store = map[string]storage.Store{
- "redis": &storage.RedisStore{},
- "mysql": &storage.SqlStore{},
- "postgres": &storage.SqlStore{},
- "boltdb": &boltdb.BoltdbStore{},
+// StorageFactory is factory to get storage instance.
+type StorageFactory interface {
+ // GetStorage will return the Storage instance.
+ GetStorage() storage.Store
}
+var storeFactorys = map[string]StorageFactory{
+ "boltdb": &SingletonFactory{
+ creatorFunction: func() storage.Store {
+ return boltdb.NewStore(conf.Store.DataExpire, conf.RetryInterval)
+ },
+ },
+ "redis": &SingletonFactory{
+ creatorFunction: func() storage.Store {
+ return &redis.Store{}
+ },
+ },
+ "mysql": &SingletonFactory{
+ creatorFunction: func() storage.Store {
+ return &sql.Store{}
+ },
+ },
+ "postgres": &SingletonFactory{
+ creatorFunction: func() storage.Store {
+ return &sql.Store{}
+ },
+ },
+}
+
+// GetStore returns storage.Store
func GetStore() storage.Store {
- return stores[config.Store.Driver]
+ return storeFactorys[conf.Store.Driver].GetStorage()
}
// WaitStoreUp wait for db to go up
diff --git a/dtmsvr/storage/sql.go b/dtmsvr/storage/sql.go
deleted file mode 100644
index 40a399f..0000000
--- a/dtmsvr/storage/sql.go
+++ /dev/null
@@ -1,140 +0,0 @@
-package storage
-
-import (
- "fmt"
- "math"
- "time"
-
- "github.com/google/uuid"
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "gorm.io/gorm"
- "gorm.io/gorm/clause"
-)
-
-type SqlStore struct {
-}
-
-func (s *SqlStore) Ping() error {
- db, err := dtmimp.StandaloneDB(config.Store.GetDBConf())
- dtmimp.E2P(err)
- _, err = db.Exec("select 1")
- return err
-}
-
-func (s *SqlStore) PopulateData(skipDrop bool) {
- file := fmt.Sprintf("%s/dtmsvr.storage.%s.sql", common.GetSqlDir(), config.Store.Driver)
- common.RunSQLScript(config.Store.GetDBConf(), file, skipDrop)
-}
-
-func (s *SqlStore) FindTransGlobalStore(gid string) *TransGlobalStore {
- trans := &TransGlobalStore{}
- dbr := dbGet().Model(trans).Where("gid=?", gid).First(trans)
- if dbr.Error == gorm.ErrRecordNotFound {
- return nil
- }
- dtmimp.E2P(dbr.Error)
- return trans
-}
-
-func (s *SqlStore) ScanTransGlobalStores(position *string, limit int64) []TransGlobalStore {
- globals := []TransGlobalStore{}
- lid := math.MaxInt64
- if *position != "" {
- lid = dtmimp.MustAtoi(*position)
- }
- dbr := dbGet().Must().Where("id < ?", lid).Order("id desc").Limit(int(limit)).Find(&globals)
- if dbr.RowsAffected < limit {
- *position = ""
- } else {
- *position = fmt.Sprintf("%d", globals[len(globals)-1].ID)
- }
- return globals
-}
-
-func (s *SqlStore) FindBranches(gid string) []TransBranchStore {
- branches := []TransBranchStore{}
- dbGet().Must().Where("gid=?", gid).Order("id asc").Find(&branches)
- return branches
-}
-
-func (s *SqlStore) UpdateBranchesSql(branches []TransBranchStore, updates []string) *gorm.DB {
- return dbGet().Clauses(clause.OnConflict{
- OnConstraint: "trans_branch_op_pkey",
- DoUpdates: clause.AssignmentColumns(updates),
- }).Create(branches)
-}
-
-func (s *SqlStore) LockGlobalSaveBranches(gid string, status string, branches []TransBranchStore, branchStart int) {
- err := dbGet().Transaction(func(tx *gorm.DB) error {
- g := &TransGlobalStore{}
- dbr := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Model(g).Where("gid=? and status=?", gid, status).First(g)
- if dbr.Error == nil {
- dbr = tx.Save(branches)
- }
- return wrapError(dbr.Error)
- })
- dtmimp.E2P(err)
-}
-
-func (s *SqlStore) MaySaveNewTrans(global *TransGlobalStore, branches []TransBranchStore) error {
- return dbGet().Transaction(func(db1 *gorm.DB) error {
- db := &common.DB{DB: db1}
- dbr := db.Must().Clauses(clause.OnConflict{
- DoNothing: true,
- }).Create(global)
- if dbr.RowsAffected <= 0 { // 如果这个不是新事务,返回错误
- return ErrUniqueConflict
- }
- if len(branches) > 0 {
- db.Must().Clauses(clause.OnConflict{
- DoNothing: true,
- }).Create(&branches)
- }
- return nil
- })
-}
-
-func (s *SqlStore) ChangeGlobalStatus(global *TransGlobalStore, newStatus string, updates []string, finished bool) {
- old := global.Status
- global.Status = newStatus
- dbr := dbGet().Must().Model(global).Where("status=? and gid=?", old, global.Gid).Select(updates).Updates(global)
- if dbr.RowsAffected == 0 {
- dtmimp.E2P(ErrNotFound)
- }
-}
-
-func (s *SqlStore) TouchCronTime(global *TransGlobalStore, nextCronInterval int64) {
- global.NextCronTime = common.GetNextTime(nextCronInterval)
- global.UpdateTime = common.GetNextTime(0)
- global.NextCronInterval = nextCronInterval
- dbGet().Must().Model(global).Where("status=? and gid=?", global.Status, global.Gid).
- Select([]string{"next_cron_time", "update_time", "next_cron_interval"}).Updates(global)
-}
-
-func (s *SqlStore) LockOneGlobalTrans(expireIn time.Duration) *TransGlobalStore {
- db := dbGet()
- getTime := func(second int) string {
- return map[string]string{
- "mysql": fmt.Sprintf("date_add(now(), interval %d second)", second),
- "postgres": fmt.Sprintf("current_timestamp + interval '%d second'", second),
- }[config.Store.Driver]
- }
- expire := int(expireIn / time.Second)
- whereTime := fmt.Sprintf("next_cron_time < %s", getTime(expire))
- owner := uuid.NewString()
- global := &TransGlobalStore{}
- dbr := db.Must().Model(global).
- Where(whereTime + "and status in ('prepared', 'aborting', 'submitted')").
- Limit(1).
- Select([]string{"owner", "next_cron_time"}).
- Updates(&TransGlobalStore{
- Owner: owner,
- NextCronTime: common.GetNextTime(common.Config.RetryInterval),
- })
- if dbr.RowsAffected == 0 {
- return nil
- }
- dbr = db.Must().Where("owner=?", owner).First(global)
- return global
-}
diff --git a/dtmsvr/storage/sql/sql.go b/dtmsvr/storage/sql/sql.go
new file mode 100644
index 0000000..3c807af
--- /dev/null
+++ b/dtmsvr/storage/sql/sql.go
@@ -0,0 +1,178 @@
+package sql
+
+import (
+ "fmt"
+ "math"
+ "time"
+
+ "github.com/lithammer/shortuuid/v3"
+ "gorm.io/gorm"
+ "gorm.io/gorm/clause"
+
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmsvr/config"
+ "github.com/dtm-labs/dtm/dtmsvr/storage"
+ "github.com/dtm-labs/dtm/dtmutil"
+)
+
+var conf = &config.Config
+
+// Store implements storage.Store, and storage with db
+type Store struct {
+}
+
+// Ping execs ping cmd to db
+func (s *Store) Ping() error {
+ db, err := dtmimp.StandaloneDB(conf.Store.GetDBConf())
+ dtmimp.E2P(err)
+ _, err = db.Exec("select 1")
+ return err
+}
+
+// PopulateData populates data to db
+func (s *Store) PopulateData(skipDrop bool) {
+ file := fmt.Sprintf("%s/dtmsvr.storage.%s.sql", dtmutil.GetSQLDir(), conf.Store.Driver)
+ dtmutil.RunSQLScript(conf.Store.GetDBConf(), file, skipDrop)
+}
+
+// FindTransGlobalStore finds GlobalTrans data by gid
+func (s *Store) FindTransGlobalStore(gid string) *storage.TransGlobalStore {
+ trans := &storage.TransGlobalStore{}
+ dbr := dbGet().Model(trans).Where("gid=?", gid).First(trans)
+ if dbr.Error == gorm.ErrRecordNotFound {
+ return nil
+ }
+ dtmimp.E2P(dbr.Error)
+ return trans
+}
+
+// ScanTransGlobalStores lists GlobalTrans data
+func (s *Store) ScanTransGlobalStores(position *string, limit int64) []storage.TransGlobalStore {
+ globals := []storage.TransGlobalStore{}
+ lid := math.MaxInt64
+ if *position != "" {
+ lid = dtmimp.MustAtoi(*position)
+ }
+ dbr := dbGet().Must().Where("id < ?", lid).Order("id desc").Limit(int(limit)).Find(&globals)
+ if dbr.RowsAffected < limit {
+ *position = ""
+ } else {
+ *position = fmt.Sprintf("%d", globals[len(globals)-1].ID)
+ }
+ return globals
+}
+
+// FindBranches finds Branch data by gid
+func (s *Store) FindBranches(gid string) []storage.TransBranchStore {
+ branches := []storage.TransBranchStore{}
+ dbGet().Must().Where("gid=?", gid).Order("id asc").Find(&branches)
+ return branches
+}
+
+// UpdateBranches update branches info
+func (s *Store) UpdateBranches(branches []storage.TransBranchStore, updates []string) (int, error) {
+ db := dbGet().Clauses(clause.OnConflict{
+ OnConstraint: "trans_branch_op_pkey",
+ DoUpdates: clause.AssignmentColumns(updates),
+ }).Create(branches)
+ return int(db.RowsAffected), db.Error
+}
+
+// LockGlobalSaveBranches creates branches
+func (s *Store) LockGlobalSaveBranches(gid string, status string, branches []storage.TransBranchStore, branchStart int) {
+ err := dbGet().Transaction(func(tx *gorm.DB) error {
+ g := &storage.TransGlobalStore{}
+ dbr := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Model(g).Where("gid=? and status=?", gid, status).First(g)
+ if dbr.Error == nil {
+ dbr = tx.Save(branches)
+ }
+ return wrapError(dbr.Error)
+ })
+ dtmimp.E2P(err)
+}
+
+// MaySaveNewTrans creates a new trans
+func (s *Store) MaySaveNewTrans(global *storage.TransGlobalStore, branches []storage.TransBranchStore) error {
+ return dbGet().Transaction(func(db1 *gorm.DB) error {
+ db := &dtmutil.DB{DB: db1}
+ dbr := db.Must().Clauses(clause.OnConflict{
+ DoNothing: true,
+ }).Create(global)
+ if dbr.RowsAffected <= 0 { // 如果这个不是新事务,返回错误
+ return storage.ErrUniqueConflict
+ }
+ if len(branches) > 0 {
+ db.Must().Clauses(clause.OnConflict{
+ DoNothing: true,
+ }).Create(&branches)
+ }
+ return nil
+ })
+}
+
+// ChangeGlobalStatus changes global trans status
+func (s *Store) ChangeGlobalStatus(global *storage.TransGlobalStore, newStatus string, updates []string, finished bool) {
+ old := global.Status
+ global.Status = newStatus
+ dbr := dbGet().Must().Model(global).Where("status=? and gid=?", old, global.Gid).Select(updates).Updates(global)
+ if dbr.RowsAffected == 0 {
+ dtmimp.E2P(storage.ErrNotFound)
+ }
+}
+
+// TouchCronTime updates cronTime
+func (s *Store) TouchCronTime(global *storage.TransGlobalStore, nextCronInterval int64) {
+ global.NextCronTime = dtmutil.GetNextTime(nextCronInterval)
+ global.UpdateTime = dtmutil.GetNextTime(0)
+ global.NextCronInterval = nextCronInterval
+ dbGet().Must().Model(global).Where("status=? and gid=?", global.Status, global.Gid).
+ Select([]string{"next_cron_time", "update_time", "next_cron_interval"}).Updates(global)
+}
+
+// LockOneGlobalTrans finds GlobalTrans
+func (s *Store) LockOneGlobalTrans(expireIn time.Duration) *storage.TransGlobalStore {
+ db := dbGet()
+ getTime := func(second int) string {
+ return map[string]string{
+ "mysql": fmt.Sprintf("date_add(now(), interval %d second)", second),
+ "postgres": fmt.Sprintf("current_timestamp + interval '%d second'", second),
+ }[conf.Store.Driver]
+ }
+ expire := int(expireIn / time.Second)
+ whereTime := fmt.Sprintf("next_cron_time < %s", getTime(expire))
+ owner := shortuuid.New()
+ global := &storage.TransGlobalStore{}
+ dbr := db.Must().Model(global).
+ Where(whereTime + "and status in ('prepared', 'aborting', 'submitted')").
+ Limit(1).
+ Select([]string{"owner", "next_cron_time"}).
+ Updates(&storage.TransGlobalStore{
+ Owner: owner,
+ NextCronTime: dtmutil.GetNextTime(conf.RetryInterval),
+ })
+ if dbr.RowsAffected == 0 {
+ return nil
+ }
+ db.Must().Where("owner=?", owner).First(global)
+ return global
+}
+
+// SetDBConn sets db conn pool
+func SetDBConn(db *gorm.DB) {
+ sqldb, _ := db.DB()
+ sqldb.SetMaxOpenConns(int(conf.Store.MaxOpenConns))
+ sqldb.SetMaxIdleConns(int(conf.Store.MaxIdleConns))
+ sqldb.SetConnMaxLifetime(time.Duration(conf.Store.ConnMaxLifeTime) * time.Minute)
+}
+
+func dbGet() *dtmutil.DB {
+ return dtmutil.DbGet(conf.Store.GetDBConf(), SetDBConn)
+}
+
+func wrapError(err error) error {
+ if err == gorm.ErrRecordNotFound {
+ return storage.ErrNotFound
+ }
+ dtmimp.E2P(err)
+ return err
+}
diff --git a/dtmsvr/storage/store.go b/dtmsvr/storage/store.go
index d68b1a4..eca4d38 100644
--- a/dtmsvr/storage/store.go
+++ b/dtmsvr/storage/store.go
@@ -1,35 +1,33 @@
+/*
+ * Copyright (c) 2021 yedf. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
package storage
import (
"errors"
"time"
-
- "github.com/go-redis/redis/v8"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "gorm.io/gorm"
)
+// ErrNotFound defines the query item is not found in storage implement.
var ErrNotFound = errors.New("storage: NotFound")
+
+// ErrUniqueConflict defines the item is conflict with unique key in storage implement.
var ErrUniqueConflict = errors.New("storage: UniqueKeyConflict")
+// Store defines storage relevant interface
type Store interface {
Ping() error
PopulateData(skipDrop bool)
FindTransGlobalStore(gid string) *TransGlobalStore
ScanTransGlobalStores(position *string, limit int64) []TransGlobalStore
FindBranches(gid string) []TransBranchStore
- UpdateBranchesSql(branches []TransBranchStore, updates []string) *gorm.DB
+ UpdateBranches(branches []TransBranchStore, updates []string) (int, error)
LockGlobalSaveBranches(gid string, status string, branches []TransBranchStore, branchStart int)
MaySaveNewTrans(global *TransGlobalStore, branches []TransBranchStore) error
ChangeGlobalStatus(global *TransGlobalStore, newStatus string, updates []string, finished bool)
TouchCronTime(global *TransGlobalStore, nextCronInterval int64)
LockOneGlobalTrans(expireIn time.Duration) *TransGlobalStore
}
-
-func wrapError(err error) error {
- if err == gorm.ErrRecordNotFound || err == redis.Nil {
- return ErrNotFound
- }
- dtmimp.E2P(err)
- return err
-}
diff --git a/dtmsvr/storage/trans.go b/dtmsvr/storage/trans.go
index dbabc01..077bd0d 100644
--- a/dtmsvr/storage/trans.go
+++ b/dtmsvr/storage/trans.go
@@ -3,12 +3,20 @@ package storage
import (
"time"
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmsvr/config"
+ "github.com/dtm-labs/dtm/dtmutil"
)
+// TransGlobalExt defines Header info
+type TransGlobalExt struct {
+ Headers map[string]string `json:"headers,omitempty" gorm:"-"`
+}
+
+// TransGlobalStore defines GlobalStore storage info
type TransGlobalStore struct {
- common.ModelBase
+ dtmutil.ModelBase
Gid string `json:"gid,omitempty"`
TransType string `json:"trans_type,omitempty"`
Steps []map[string]string `json:"steps,omitempty" gorm:"-"`
@@ -17,7 +25,6 @@ type TransGlobalStore struct {
Status string `json:"status,omitempty"`
QueryPrepared string `json:"query_prepared,omitempty"`
Protocol string `json:"protocol,omitempty"`
- CommitTime *time.Time `json:"commit_time,omitempty"`
FinishTime *time.Time `json:"finish_time,omitempty"`
RollbackTime *time.Time `json:"rollback_time,omitempty"`
Options string `json:"options,omitempty"`
@@ -25,17 +32,23 @@ type TransGlobalStore struct {
NextCronInterval int64 `json:"next_cron_interval,omitempty"`
NextCronTime *time.Time `json:"next_cron_time,omitempty"`
Owner string `json:"owner,omitempty"`
+ Ext TransGlobalExt `json:"-" gorm:"-"`
+ ExtData string `json:"ext_data,omitempty"` // storage of ext. a db field to store many values. like Options
dtmcli.TransOptions
}
// TableName TableName
-func (*TransGlobalStore) TableName() string {
- return "dtm.trans_global"
+func (g *TransGlobalStore) TableName() string {
+ return config.Config.Store.TransGlobalTable
+}
+
+func (g *TransGlobalStore) String() string {
+ return dtmimp.MustMarshalString(g)
}
// TransBranchStore branch transaction
type TransBranchStore struct {
- common.ModelBase
+ dtmutil.ModelBase
Gid string `json:"gid,omitempty"`
URL string `json:"url,omitempty"`
BinData []byte
@@ -47,6 +60,10 @@ type TransBranchStore struct {
}
// TableName TableName
-func (*TransBranchStore) TableName() string {
- return "dtm.trans_branch_op"
+func (b *TransBranchStore) TableName() string {
+ return config.Config.Store.TransBranchOpTable
+}
+
+func (b *TransBranchStore) String() string {
+ return dtmimp.MustMarshalString(*b)
}
diff --git a/dtmsvr/storage/utils.go b/dtmsvr/storage/utils.go
deleted file mode 100644
index d2d73d1..0000000
--- a/dtmsvr/storage/utils.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package storage
-
-import (
- "github.com/go-redis/redis/v8"
- "github.com/yedf/dtm/common"
-)
-
-var config = &common.Config
-
-func dbGet() *common.DB {
- return common.DbGet(config.Store.GetDBConf())
-}
-
-func redisGet() *redis.Client {
- return common.RedisGet()
-}
diff --git a/dtmsvr/svr.go b/dtmsvr/svr.go
index af92105..56f288c 100644
--- a/dtmsvr/svr.go
+++ b/dtmsvr/svr.go
@@ -7,46 +7,64 @@
package dtmsvr
import (
+ "context"
"fmt"
"net"
"time"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/dtmgrpc"
+ "github.com/dtm-labs/dtm/dtmgrpc/dtmgimp"
+ "github.com/dtm-labs/dtm/dtmgrpc/dtmgpb"
+ "github.com/dtm-labs/dtm/dtmutil"
+ "github.com/dtm-labs/dtmdriver"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmgrpc/dtmgimp"
- "github.com/yedf/dtmdriver"
"google.golang.org/grpc"
)
// StartSvr StartSvr
func StartSvr() {
- dtmimp.Logf("start dtmsvr")
- app := common.GetGinApp()
+ logger.Infof("start dtmsvr")
+ dtmcli.GetRestyClient().SetTimeout(time.Duration(conf.RequestTimeout) * time.Second)
+ dtmgrpc.AddUnaryInterceptor(func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
+ ctx2, cancel := context.WithTimeout(ctx, time.Duration(conf.RequestTimeout)*time.Second)
+ defer cancel()
+ return invoker(ctx2, method, req, reply, cc, opts...)
+ })
+ app := dtmutil.GetGinApp()
app = httpMetrics(app)
addRoute(app)
- dtmimp.Logf("dtmsvr listen at: %d", config.HttpPort)
- go app.Run(fmt.Sprintf(":%d", config.HttpPort))
+ logger.Infof("dtmsvr listen at: %d", conf.HTTPPort)
+ go func() {
+ err := app.Run(fmt.Sprintf(":%d", conf.HTTPPort))
+ if err != nil {
+ logger.Errorf("start server err: %v", err)
+ }
+ }()
- lis, err := net.Listen("tcp", fmt.Sprintf(":%d", config.GrpcPort))
- dtmimp.FatalIfError(err)
+ lis, err := net.Listen("tcp", fmt.Sprintf(":%d", conf.GrpcPort))
+ logger.FatalIfError(err)
s := grpc.NewServer(
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
grpc.UnaryServerInterceptor(grpcMetrics), grpc.UnaryServerInterceptor(dtmgimp.GrpcServerLog)),
))
- dtmgimp.RegisterDtmServer(s, &dtmServer{})
- dtmimp.Logf("grpc listening at %v", lis.Addr())
+ dtmgpb.RegisterDtmServer(s, &dtmServer{})
+ logger.Infof("grpc listening at %v", lis.Addr())
go func() {
err := s.Serve(lis)
- dtmimp.FatalIfError(err)
+ logger.FatalIfError(err)
}()
- go updateBranchAsync()
+
+ for i := 0; i < int(conf.UpdateBranchAsyncGoroutineNum); i++ {
+ go updateBranchAsync()
+ }
time.Sleep(100 * time.Millisecond)
- err = dtmdriver.Use(config.MicroService.Driver)
- dtmimp.FatalIfError(err)
- err = dtmdriver.GetDriver().RegisterGrpcService(config.MicroService.Target, config.MicroService.EndPoint)
- dtmimp.FatalIfError(err)
+ err = dtmdriver.Use(conf.MicroService.Driver)
+ logger.FatalIfError(err)
+ err = dtmdriver.GetDriver().RegisterGrpcService(conf.MicroService.Target, conf.MicroService.EndPoint)
+ logger.FatalIfError(err)
}
// PopulateDB setup mysql data
@@ -59,8 +77,8 @@ var UpdateBranchAsyncInterval = 200 * time.Millisecond
var updateBranchAsyncChan chan branchStatus = make(chan branchStatus, 1000)
func updateBranchAsync() {
- for { // flush branches every second
- defer common.RecoverPanic(nil)
+ flushBranchs := func() {
+ defer dtmutil.RecoverPanic(nil)
updates := []TransBranch{}
started := time.Now()
checkInterval := 20 * time.Millisecond
@@ -68,7 +86,8 @@ func updateBranchAsync() {
select {
case updateBranch := <-updateBranchAsyncChan:
updates = append(updates, TransBranch{
- ModelBase: common.ModelBase{ID: updateBranch.id},
+ ModelBase: dtmutil.ModelBase{ID: updateBranch.id},
+ Gid: updateBranch.gid,
Status: updateBranch.status,
FinishTime: updateBranch.finishTime,
})
@@ -76,15 +95,19 @@ func updateBranchAsync() {
}
}
for len(updates) > 0 {
- dbr := GetStore().UpdateBranchesSql(updates, []string{"status", "finish_time", "update_time"})
+ rowAffected, err := GetStore().UpdateBranches(updates, []string{"status", "finish_time", "update_time"})
- dtmimp.Logf("flushed %d branch status to db. affected: %d", len(updates), dbr.RowsAffected)
- if dbr.Error != nil {
- dtmimp.LogRedf("async update branch status error: %v", dbr.Error)
+ if err != nil {
+ logger.Errorf("async update branch status error: %v", err)
time.Sleep(1 * time.Second)
} else {
+ logger.Infof("flushed %d branch status to db. affected: %d", len(updates), rowAffected)
updates = []TransBranch{}
}
}
+
+ }
+ for { // flush branches every 200ms
+ flushBranchs()
}
}
diff --git a/dtmsvr/svr_imports.go b/dtmsvr/svr_imports.go
deleted file mode 100644
index f708b1f..0000000
--- a/dtmsvr/svr_imports.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package dtmsvr
-
-import (
- _ "github.com/ychensha/dtmdriver-polaris"
- _ "github.com/yedf/dtmdriver-gozero"
- _ "github.com/yedf/dtmdriver-protocol1"
-)
diff --git a/dtmsvr/trans_class.go b/dtmsvr/trans_class.go
index 4ce6b9e..50f9b01 100644
--- a/dtmsvr/trans_class.go
+++ b/dtmsvr/trans_class.go
@@ -7,13 +7,16 @@
package dtmsvr
import (
+ "context"
"time"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/dtmgrpc/dtmgimp"
+ "github.com/dtm-labs/dtm/dtmgrpc/dtmgpb"
+ "github.com/dtm-labs/dtm/dtmsvr/storage"
"github.com/gin-gonic/gin"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmgrpc/dtmgimp"
- "github.com/yedf/dtm/dtmsvr/storage"
)
// TransGlobal global transaction
@@ -57,7 +60,7 @@ func TransFromContext(c *gin.Context) *TransGlobal {
e2p(err)
m := TransGlobal{}
dtmimp.MustUnmarshal(b, &m)
- dtmimp.Logf("creating trans in prepare")
+ logger.Debugf("creating trans in prepare")
// Payloads will be store in BinPayloads, Payloads is only used to Unmarshal
for _, p := range m.Payloads {
m.BinPayloads = append(m.BinPayloads, []byte(p))
@@ -68,12 +71,22 @@ func TransFromContext(c *gin.Context) *TransGlobal {
}
}
m.Protocol = "http"
+
+ m.Ext.Headers = map[string]string{}
+ if len(m.PassthroughHeaders) > 0 {
+ for _, h := range m.PassthroughHeaders {
+ v := c.GetHeader(h)
+ if v != "" {
+ m.Ext.Headers[h] = v
+ }
+ }
+ }
return &m
}
// TransFromDtmRequest TransFromContext
-func TransFromDtmRequest(c *dtmgimp.DtmRequest) *TransGlobal {
- o := &dtmgimp.DtmTransOptions{}
+func TransFromDtmRequest(ctx context.Context, c *dtmgpb.DtmRequest) *TransGlobal {
+ o := &dtmgpb.DtmTransOptions{}
if c.TransOptions != nil {
o = c.TransOptions
}
@@ -84,13 +97,24 @@ func TransFromDtmRequest(c *dtmgimp.DtmRequest) *TransGlobal {
Protocol: "grpc",
BinPayloads: c.BinPayloads,
TransOptions: dtmcli.TransOptions{
- WaitResult: o.WaitResult,
- TimeoutToFail: o.TimeoutToFail,
- RetryInterval: o.RetryInterval,
+ WaitResult: o.WaitResult,
+ TimeoutToFail: o.TimeoutToFail,
+ RetryInterval: o.RetryInterval,
+ PassthroughHeaders: o.PassthroughHeaders,
+ BranchHeaders: o.BranchHeaders,
},
}}
if c.Steps != "" {
dtmimp.MustUnmarshalString(c.Steps, &r.Steps)
}
+ if len(o.PassthroughHeaders) > 0 {
+ r.Ext.Headers = map[string]string{}
+ for _, h := range o.PassthroughHeaders {
+ v := dtmgimp.GetMetaFromContext(ctx, h)
+ if v != "" {
+ r.Ext.Headers[h] = v
+ }
+ }
+ }
return &r
}
diff --git a/dtmsvr/trans_process.go b/dtmsvr/trans_process.go
index 38a68b9..7a8fe46 100644
--- a/dtmsvr/trans_process.go
+++ b/dtmsvr/trans_process.go
@@ -7,63 +7,75 @@
package dtmsvr
import (
+ "fmt"
"time"
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/dtmutil"
)
// Process process global transaction once
-func (t *TransGlobal) Process() map[string]interface{} {
- r := t.process()
- transactionMetrics(t, r["dtm_result"] == dtmcli.ResultSuccess)
+func (t *TransGlobal) Process(branches []TransBranch) error {
+ r := t.process(branches)
+ transactionMetrics(t, r == nil)
return r
}
-func (t *TransGlobal) process() map[string]interface{} {
+func (t *TransGlobal) process(branches []TransBranch) error {
if t.Options != "" {
dtmimp.MustUnmarshalString(t.Options, &t.TransOptions)
}
+ if t.ExtData != "" {
+ dtmimp.MustUnmarshalString(t.ExtData, &t.Ext)
+ }
if !t.WaitResult {
- go t.processInner()
- return dtmcli.MapSuccess
+ go func() {
+ err := t.processInner(branches)
+ if err != nil {
+ logger.Errorf("processInner err: %v", err)
+ }
+ }()
+ return nil
}
submitting := t.Status == dtmcli.StatusSubmitted
- err := t.processInner()
+ err := t.processInner(branches)
if err != nil {
- return map[string]interface{}{"dtm_result": dtmcli.ResultFailure, "message": err.Error()}
+ return err
}
if submitting && t.Status != dtmcli.StatusSucceed {
- return map[string]interface{}{"dtm_result": dtmcli.ResultFailure, "message": "trans failed by user"}
+ return fmt.Errorf("wait result not return success: %w", dtmcli.ErrFailure)
}
- return dtmcli.MapSuccess
+ return nil
}
-func (t *TransGlobal) processInner() (rerr error) {
+func (t *TransGlobal) processInner(branches []TransBranch) (rerr error) {
defer handlePanic(&rerr)
defer func() {
- if rerr != nil {
- dtmimp.LogRedf("processInner got error: %s", rerr.Error())
+ if rerr != nil && rerr != dtmcli.ErrOngoing {
+ logger.Errorf("processInner got error: %s", rerr.Error())
}
if TransProcessedTestChan != nil {
- dtmimp.Logf("processed: %s", t.Gid)
+ logger.Debugf("processed: %s", t.Gid)
TransProcessedTestChan <- t.Gid
- dtmimp.Logf("notified: %s", t.Gid)
+ logger.Debugf("notified: %s", t.Gid)
}
}()
- dtmimp.Logf("processing: %s status: %s", t.Gid, t.Status)
- branches := GetStore().FindBranches(t.Gid)
+ logger.Debugf("processing: %s status: %s", t.Gid, t.Status)
t.lastTouched = time.Now()
rerr = t.getProcessor().ProcessOnce(branches)
return
}
-func (t *TransGlobal) saveNew() error {
- branches := t.getProcessor().GenBranches()
+func (t *TransGlobal) saveNew() ([]TransBranch, error) {
t.NextCronInterval = t.getNextCronInterval(cronReset)
- t.NextCronTime = common.GetNextTime(t.NextCronInterval)
+ t.NextCronTime = dtmutil.GetNextTime(t.NextCronInterval)
+ t.ExtData = dtmimp.MustMarshalString(t.Ext)
+ if t.ExtData == "{}" {
+ t.ExtData = ""
+ }
t.Options = dtmimp.MustMarshalString(t.TransOptions)
if t.Options == "{}" {
t.Options = ""
@@ -71,5 +83,13 @@ func (t *TransGlobal) saveNew() error {
now := time.Now()
t.CreateTime = &now
t.UpdateTime = &now
- return GetStore().MaySaveNewTrans(&t.TransGlobalStore, branches)
+ branches := t.getProcessor().GenBranches()
+ for i := range branches {
+ branches[i].CreateTime = &now
+ branches[i].UpdateTime = &now
+ }
+ err := GetStore().MaySaveNewTrans(&t.TransGlobalStore, branches)
+ logger.Infof("MaySaveNewTrans result: %v, global: %v branches: %v",
+ err, t.TransGlobalStore.String(), dtmimp.MustMarshalString(branches))
+ return branches, err
}
diff --git a/dtmsvr/trans_status.go b/dtmsvr/trans_status.go
index b7fda72..c70e5f2 100644
--- a/dtmsvr/trans_status.go
+++ b/dtmsvr/trans_status.go
@@ -7,21 +7,25 @@
package dtmsvr
import (
+ "errors"
"fmt"
"strings"
"time"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmgrpc/dtmgimp"
- "github.com/yedf/dtmdriver"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/dtmgrpc/dtmgimp"
+ "github.com/dtm-labs/dtmdriver"
"google.golang.org/grpc/codes"
+ "google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
func (t *TransGlobal) touchCronTime(ctype cronType) {
t.lastTouched = time.Now()
GetStore().TouchCronTime(&t.TransGlobalStore, t.getNextCronInterval(ctype))
+ logger.Infof("TouchCronTime for: %s", t.TransGlobalStore.String())
}
func (t *TransGlobal) changeStatus(status string) {
@@ -36,6 +40,7 @@ func (t *TransGlobal) changeStatus(status string) {
}
t.UpdateTime = &now
GetStore().ChangeGlobalStatus(&t.TransGlobalStore, status, updates, status == dtmcli.StatusSucceed || status == dtmcli.StatusFailed)
+ logger.Infof("ChangeGlobalStatus to %s ok for %s", status, t.TransGlobalStore.String())
t.Status = status
}
@@ -44,17 +49,19 @@ func (t *TransGlobal) changeBranchStatus(b *TransBranch, status string, branchPo
b.Status = status
b.FinishTime = &now
b.UpdateTime = &now
- if config.Store.Driver != dtmimp.DBTypeMysql && config.Store.Driver != dtmimp.DBTypePostgres || config.UpdateBranchSync > 0 || t.updateBranchSync {
+ if conf.Store.Driver != dtmimp.DBTypeMysql && conf.Store.Driver != dtmimp.DBTypePostgres || conf.UpdateBranchSync > 0 || t.updateBranchSync {
GetStore().LockGlobalSaveBranches(t.Gid, t.Status, []TransBranch{*b}, branchPos)
+ logger.Infof("LockGlobalSaveBranches ok: gid: %s old status: %s branches: %s",
+ b.Gid, dtmcli.StatusPrepared, b.String())
} else { // 为了性能优化,把branch的status更新异步化
- updateBranchAsyncChan <- branchStatus{id: b.ID, status: status, finishTime: &now}
+ updateBranchAsyncChan <- branchStatus{id: b.ID, gid: t.Gid, status: status, finishTime: &now}
}
}
func (t *TransGlobal) isTimeout() bool {
timeout := t.TimeoutToFail
if t.TimeoutToFail == 0 && t.TransType != "saga" {
- timeout = config.TimeoutToFail
+ timeout = conf.TimeoutToFail
}
if timeout == 0 {
return false
@@ -66,28 +73,36 @@ func (t *TransGlobal) needProcess() bool {
return t.Status == dtmcli.StatusSubmitted || t.Status == dtmcli.StatusAborting || t.Status == dtmcli.StatusPrepared && t.isTimeout()
}
-func (t *TransGlobal) getURLResult(url string, branchID, op string, branchPayload []byte) (string, error) {
+func (t *TransGlobal) getURLResult(url string, branchID, op string, branchPayload []byte) error {
+ if url == "" { // empty url is success
+ return nil
+ }
if t.Protocol == "grpc" {
dtmimp.PanicIf(strings.HasPrefix(url, "http"), fmt.Errorf("bad url for grpc: %s", url))
server, method, err := dtmdriver.GetDriver().ParseServerMethod(url)
if err != nil {
- return "", err
+ return err
}
conn := dtmgimp.MustGetGrpcConn(server, true)
ctx := dtmgimp.TransInfo2Ctx(t.Gid, t.TransType, branchID, op, "")
+ kvs := dtmgimp.Map2Kvs(t.Ext.Headers)
+ kvs = append(kvs, dtmgimp.Map2Kvs(t.BranchHeaders)...)
+ ctx = metadata.AppendToOutgoingContext(ctx, kvs...)
err = conn.Invoke(ctx, method, branchPayload, &[]byte{})
if err == nil {
- return dtmcli.ResultSuccess, nil
+ return nil
}
st, ok := status.FromError(err)
if ok && st.Code() == codes.Aborted {
+ // version lower then v1.10, will specify Ongoing in code Aborted
if st.Message() == dtmcli.ResultOngoing {
- return dtmcli.ResultOngoing, nil
- } else if st.Message() == dtmcli.ResultFailure {
- return dtmcli.ResultFailure, nil
+ return dtmcli.ErrOngoing
}
+ return dtmcli.ErrFailure
+ } else if ok && st.Code() == codes.FailedPrecondition {
+ return dtmcli.ErrOngoing
}
- return "", err
+ return err
}
dtmimp.PanicIf(!strings.HasPrefix(url, "http"), fmt.Errorf("bad url for http: %s", url))
resp, err := dtmimp.RestyClient.R().SetBody(string(branchPayload)).
@@ -98,26 +113,25 @@ func (t *TransGlobal) getURLResult(url string, branchID, op string, branchPayloa
"op": op,
}).
SetHeader("Content-type", "application/json").
+ SetHeaders(t.Ext.Headers).
+ SetHeaders(t.TransOptions.BranchHeaders).
Execute(dtmimp.If(branchPayload != nil || t.TransType == "xa", "POST", "GET").(string), url)
if err != nil {
- return "", err
+ return err
}
- return resp.String(), nil
+ return dtmimp.RespAsErrorCompatible(resp)
}
func (t *TransGlobal) getBranchResult(branch *TransBranch) (string, error) {
- body, err := t.getURLResult(branch.URL, branch.BranchID, branch.Op, branch.BinData)
- if err != nil {
- return "", err
- }
- if strings.Contains(body, dtmcli.ResultSuccess) {
+ err := t.getURLResult(branch.URL, branch.BranchID, branch.Op, branch.BinData)
+ if err == nil {
return dtmcli.StatusSucceed, nil
- } else if strings.HasSuffix(t.TransType, "saga") && branch.Op == dtmcli.BranchAction && strings.Contains(body, dtmcli.ResultFailure) {
+ } else if t.TransType == "saga" && branch.Op == dtmcli.BranchAction && errors.Is(err, dtmcli.ErrFailure) {
return dtmcli.StatusFailed, nil
- } else if strings.Contains(body, dtmcli.ResultOngoing) {
- return "", dtmimp.ErrOngoing
+ } else if errors.Is(err, dtmcli.ErrOngoing) {
+ return "", dtmcli.ErrOngoing
}
- return "", fmt.Errorf("http result should contains SUCCESS|FAILURE|ONGOING. grpc error should return nil|Aborted with message(FAILURE|ONGOING). \nrefer to: https://dtm.pub/summary/arch.html#http\nunkown result will be retried: %s", body)
+ return "", fmt.Errorf("http/grpc result should be specified as in:\nhttps://dtm.pub/summary/arch.html#http\nunkown result will be retried: %s", err)
}
func (t *TransGlobal) execBranch(branch *TransBranch, branchPos int) error {
@@ -128,7 +142,7 @@ func (t *TransGlobal) execBranch(branch *TransBranch, branchPos int) error {
branchMetrics(t, branch, status == dtmcli.StatusSucceed)
// if time pass 1500ms and NextCronInterval is not default, then reset NextCronInterval
if err == nil && time.Since(t.lastTouched)+NowForwardDuration >= 1500*time.Millisecond ||
- t.NextCronInterval > config.RetryInterval && t.NextCronInterval > t.RetryInterval {
+ t.NextCronInterval > conf.RetryInterval && t.NextCronInterval > t.RetryInterval {
t.touchCronTime(cronReset)
} else if err == dtmimp.ErrOngoing {
t.touchCronTime(cronKeep)
@@ -146,6 +160,6 @@ func (t *TransGlobal) getNextCronInterval(ctype cronType) int64 {
} else if t.RetryInterval != 0 {
return t.RetryInterval
} else {
- return config.RetryInterval
+ return conf.RetryInterval
}
}
diff --git a/dtmsvr/trans_type_msg.go b/dtmsvr/trans_type_msg.go
index f6bc9b1..5fe3eb2 100644
--- a/dtmsvr/trans_type_msg.go
+++ b/dtmsvr/trans_type_msg.go
@@ -7,11 +7,11 @@
package dtmsvr
import (
+ "errors"
"fmt"
- "strings"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
)
type transMsgProcessor struct {
@@ -42,15 +42,15 @@ func (t *TransGlobal) mayQueryPrepared() {
if !t.needProcess() || t.Status == dtmcli.StatusSubmitted {
return
}
- body, err := t.getURLResult(t.QueryPrepared, "", "", nil)
- if strings.Contains(body, dtmcli.ResultSuccess) {
+ err := t.getURLResult(t.QueryPrepared, "00", "msg", nil)
+ if err == nil {
t.changeStatus(dtmcli.StatusSubmitted)
- } else if strings.Contains(body, dtmcli.ResultFailure) {
+ } else if errors.Is(err, dtmcli.ErrFailure) {
t.changeStatus(dtmcli.StatusFailed)
- } else if strings.Contains(body, dtmcli.ResultOngoing) {
+ } else if errors.Is(err, dtmcli.ErrOngoing) {
t.touchCronTime(cronReset)
} else {
- dtmimp.LogRedf("getting result failed for %s. error: %s", t.QueryPrepared, err.Error())
+ logger.Errorf("getting result failed for %s. error: %v", t.QueryPrepared, err)
t.touchCronTime(cronBackoff)
}
}
diff --git a/dtmsvr/trans_type_saga.go b/dtmsvr/trans_type_saga.go
index dad3d6f..513190c 100644
--- a/dtmsvr/trans_type_saga.go
+++ b/dtmsvr/trans_type_saga.go
@@ -7,11 +7,13 @@
package dtmsvr
import (
+ "errors"
"fmt"
"time"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
)
type transSagaProcessor struct {
@@ -19,7 +21,9 @@ type transSagaProcessor struct {
}
func init() {
- registorProcessorCreator("saga", func(trans *TransGlobal) transProcessor { return &transSagaProcessor{TransGlobal: trans} })
+ registorProcessorCreator("saga", func(trans *TransGlobal) transProcessor {
+ return &transSagaProcessor{TransGlobal: trans}
+ })
}
func (t *transSagaProcessor) GenBranches() []TransBranch {
@@ -43,6 +47,7 @@ func (t *transSagaProcessor) GenBranches() []TransBranch {
type cSagaCustom struct {
Orders map[int][]int `json:"orders"`
Concurrent bool `json:"concurrent"`
+ cOrders map[int][]int
}
type branchResult struct {
@@ -54,15 +59,20 @@ type branchResult struct {
func (t *transSagaProcessor) ProcessOnce(branches []TransBranch) error {
// when saga tasks is fetched, it always need to process
- dtmimp.Logf("status: %s timeout: %t", t.Status, t.isTimeout())
+ logger.Debugf("status: %s timeout: %t", t.Status, t.isTimeout())
if t.Status == dtmcli.StatusSubmitted && t.isTimeout() {
t.changeStatus(dtmcli.StatusAborting)
}
n := len(branches)
- csc := cSagaCustom{Orders: map[int][]int{}}
+ csc := cSagaCustom{Orders: map[int][]int{}, cOrders: map[int][]int{}}
if t.CustomData != "" {
dtmimp.MustUnmarshalString(t.CustomData, &csc)
+ for k, v := range csc.Orders {
+ for _, b := range v {
+ csc.cOrders[b] = append(csc.cOrders[b], k)
+ }
+ }
}
if csc.Concurrent || t.TimeoutToFail > 0 { // when saga is not normal, update branch sync
t.updateBranchSync = true
@@ -79,22 +89,41 @@ func (t *transSagaProcessor) ProcessOnce(branches []TransBranch) error {
rsAFailed++
}
}
- branchResults[i] = branchResult{status: branches[i].Status, op: branches[i].Op}
+ branchResults[i] = branchResult{index: i, status: branches[i].Status, op: branches[i].Op}
}
- isPreconditionsSucceed := func(current int) bool {
+ shouldRun := func(current int) bool {
// if !csc.Concurrent,then check the branch in previous step is succeed
- if !csc.Concurrent && current >= 2 && branches[current-2].Status != dtmcli.StatusSucceed {
+ if !csc.Concurrent && current >= 2 && branchResults[current-2].status != dtmcli.StatusSucceed {
return false
}
// if csc.concurrent, then check the Orders. origin one step correspond to 2 step in dtmsvr
for _, pre := range csc.Orders[current/2] {
- if branches[pre*2+1].Status != dtmcli.StatusSucceed {
+ if branchResults[pre*2+1].status != dtmcli.StatusSucceed {
+ return false
+ }
+ }
+ return true
+ }
+ shouldRollback := func(current int) bool {
+ rollbacked := func(i int) bool {
+ // current compensate op rollbacked or related action still prepared
+ return branchResults[i].status == dtmcli.StatusSucceed || branchResults[i+1].status == dtmcli.StatusPrepared
+ }
+ if rollbacked(current) {
+ return false
+ }
+ // if !csc.Concurrent,then check the branch in next step is rollbacked
+ if !csc.Concurrent && current < n-2 && !rollbacked(current+2) {
+ return false
+ }
+ // if csc.concurrent, then check the cOrders. origin one step correspond to 2 step in dtmsvr
+ for _, next := range csc.cOrders[current/2] {
+ if !rollbacked(2 * next) {
return false
}
}
return true
}
-
resultChan := make(chan branchResult, n)
asyncExecBranch := func(i int) {
var err error
@@ -103,21 +132,32 @@ func (t *transSagaProcessor) ProcessOnce(branches []TransBranch) error {
err = dtmimp.AsError(x)
}
resultChan <- branchResult{index: i, status: branches[i].Status, op: branches[i].Op}
- if err != nil {
- dtmimp.LogRedf("exec branch error: %v", err)
+ if err != nil && !errors.Is(err, dtmcli.ErrOngoing) {
+ logger.Errorf("exec branch error: %v", err)
}
}()
err = t.execBranch(&branches[i], i)
}
pickToRunActions := func() []int {
toRun := []int{}
- for current := 0; current < n; current++ {
+ for current := 1; current < n; current += 2 {
+ br := &branchResults[current]
+ if !br.started && br.status == dtmcli.StatusPrepared && shouldRun(current) {
+ toRun = append(toRun, current)
+ }
+ }
+ logger.Debugf("toRun picked for action is: %v branchResults: %v compensate orders: %v", toRun, branchResults, csc.cOrders)
+ return toRun
+ }
+ pickToRunCompensates := func() []int {
+ toRun := []int{}
+ for current := n - 2; current >= 0; current -= 2 {
br := &branchResults[current]
- if br.op == dtmcli.BranchAction && !br.started && isPreconditionsSucceed(current) && br.status == dtmcli.StatusPrepared {
+ if !br.started && br.status == dtmcli.StatusPrepared && shouldRollback(current) {
toRun = append(toRun, current)
}
}
- dtmimp.Logf("toRun picked for action is: %v", toRun)
+ logger.Debugf("toRun picked for compensate is: %v branchResults: %v compensate orders: %v", toRun, branchResults, csc.cOrders)
return toRun
}
runBranches := func(toRun []int) {
@@ -129,18 +169,6 @@ func (t *transSagaProcessor) ProcessOnce(branches []TransBranch) error {
go asyncExecBranch(b)
}
}
- pickAndRunCompensates := func(toRunActions []int) {
- for _, b := range toRunActions {
- // these branches may have run. so flag them to status succeed, then run the corresponding compensate
- branchResults[b].status = dtmcli.StatusSucceed
- }
- for i, b := range branchResults {
- if b.op == dtmcli.BranchCompensate && b.status != dtmcli.StatusSucceed && branchResults[i+1].status != dtmcli.StatusPrepared {
- rsCToStart++
- go asyncExecBranch(i)
- }
- }
- }
waitDoneOnce := func() {
select {
case r := <-resultChan:
@@ -159,13 +187,30 @@ func (t *transSagaProcessor) ProcessOnce(branches []TransBranch) error {
rsCSucceed++
}
}
- dtmimp.Logf("branch done: %v", r)
+ logger.Debugf("branch done: %v", r)
case <-time.After(time.Duration(time.Second * 3)):
- dtmimp.Logf("wait once for done")
+ logger.Debugf("wait once for done")
}
}
-
- for t.Status == dtmcli.StatusSubmitted && !t.isTimeout() && rsAFailed == 0 && rsADone != rsAToStart {
+ prepareToCompensate := func() {
+ _ = pickToRunActions() // flag started
+ for i := 1; i < len(branchResults); i += 2 {
+ // these branches may have run. so flag them to status succeed, then run the corresponding
+ // compensate
+ if branchResults[i].started && branchResults[i].status == dtmcli.StatusPrepared {
+ branchResults[i].status = dtmcli.StatusSucceed
+ }
+ }
+ for i, b := range branchResults {
+ if b.op == dtmcli.BranchCompensate && b.status != dtmcli.StatusSucceed &&
+ branchResults[i+1].status != dtmcli.StatusPrepared {
+ rsCToStart++
+ }
+ }
+ logger.Debugf("rsCToStart: %d branchResults: %v", rsCToStart, branchResults)
+ }
+ timeLimit := time.Now().Add(time.Duration(conf.RequestTimeout+2) * time.Second)
+ for time.Now().Before(timeLimit) && t.Status == dtmcli.StatusSubmitted && !t.isTimeout() && rsAFailed == 0 {
toRun := pickToRunActions()
runBranches(toRun)
if rsADone == rsAStarted { // no branch is running, so break
@@ -181,11 +226,16 @@ func (t *transSagaProcessor) ProcessOnce(branches []TransBranch) error {
t.changeStatus(dtmcli.StatusAborting)
}
if t.Status == dtmcli.StatusAborting {
- toRun := pickToRunActions()
- pickAndRunCompensates(toRun)
- for rsCDone != rsCToStart {
- waitDoneOnce()
+ prepareToCompensate()
+ }
+ for time.Now().Before(timeLimit) && t.Status == dtmcli.StatusAborting {
+ toRun := pickToRunCompensates()
+ runBranches(toRun)
+ if rsCDone == rsCToStart { // no branch is running, so break
+ break
}
+ logger.Debugf("rsCDone: %d rsCToStart: %d", rsCDone, rsCToStart)
+ waitDoneOnce()
}
if t.Status == dtmcli.StatusAborting && rsCToStart == rsCSucceed {
t.changeStatus(dtmcli.StatusFailed)
diff --git a/dtmsvr/trans_type_tcc.go b/dtmsvr/trans_type_tcc.go
index 88145d1..31ae557 100644
--- a/dtmsvr/trans_type_tcc.go
+++ b/dtmsvr/trans_type_tcc.go
@@ -7,8 +7,9 @@
package dtmsvr
import (
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
)
type transTccProcessor struct {
@@ -33,7 +34,7 @@ func (t *transTccProcessor) ProcessOnce(branches []TransBranch) error {
op := dtmimp.If(t.Status == dtmcli.StatusSubmitted, dtmcli.BranchConfirm, dtmcli.BranchCancel).(string)
for current := len(branches) - 1; current >= 0; current-- {
if branches[current].Op == op && branches[current].Status == dtmcli.StatusPrepared {
- dtmimp.Logf("branch info: current: %d ID: %d", current, branches[current].ID)
+ logger.Debugf("branch info: current: %d ID: %d", current, branches[current].ID)
err := t.execBranch(&branches[current], current)
if err != nil {
return err
diff --git a/dtmsvr/trans_type_xa.go b/dtmsvr/trans_type_xa.go
index ad44dd0..4f45205 100644
--- a/dtmsvr/trans_type_xa.go
+++ b/dtmsvr/trans_type_xa.go
@@ -7,8 +7,8 @@
package dtmsvr
import (
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
)
type transXaProcessor struct {
diff --git a/dtmsvr/utils.go b/dtmsvr/utils.go
index 07e017a..3afac50 100644
--- a/dtmsvr/utils.go
+++ b/dtmsvr/utils.go
@@ -10,40 +10,42 @@ import (
"fmt"
"time"
- "github.com/google/uuid"
-
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmsvr/storage"
- "github.com/yedf/dtm/dtmsvr/storage/registry"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmsvr/config"
+ "github.com/dtm-labs/dtm/dtmsvr/storage"
+ "github.com/dtm-labs/dtm/dtmsvr/storage/registry"
+ "github.com/lithammer/shortuuid/v3"
)
type branchStatus struct {
id uint64
+ gid string
status string
finishTime *time.Time
}
-var p2e = dtmimp.P2E
var e2p = dtmimp.E2P
-var config = &common.Config
+var conf = &config.Config
+// GetStore returns storage.Store
func GetStore() storage.Store {
return registry.GetStore()
}
// TransProcessedTestChan only for test usage. when transaction processed once, write gid to this chan
-var TransProcessedTestChan chan string = nil
+var TransProcessedTestChan chan string
// GenGid generate gid, use uuid
func GenGid() string {
- return uuid.NewString()
+ return shortuuid.New()
}
// GetTransGlobal construct trans from db
func GetTransGlobal(gid string) *TransGlobal {
trans := GetStore().FindTransGlobalStore(gid)
+ //nolint:staticcheck
dtmimp.PanicIf(trans == nil, fmt.Errorf("no TransGlobal with gid: %s found", gid))
+ //nolint:staticcheck
return &TransGlobal{TransGlobalStore: *trans}
}
diff --git a/dtmsvr/utils_test.go b/dtmsvr/utils_test.go
index 6c6f371..2c24571 100644
--- a/dtmsvr/utils_test.go
+++ b/dtmsvr/utils_test.go
@@ -22,6 +22,6 @@ func TestSetNextCron(t *testing.T) {
tg.RetryInterval = 15
assert.Equal(t, int64(15), tg.getNextCronInterval(cronReset))
tg.RetryInterval = 0
- assert.Equal(t, config.RetryInterval, tg.getNextCronInterval(cronReset))
- assert.Equal(t, config.RetryInterval*2, tg.getNextCronInterval(cronBackoff))
+ assert.Equal(t, conf.RetryInterval, tg.getNextCronInterval(cronReset))
+ assert.Equal(t, conf.RetryInterval*2, tg.getNextCronInterval(cronBackoff))
}
diff --git a/dtmutil/consts.go b/dtmutil/consts.go
new file mode 100644
index 0000000..9f1afe4
--- /dev/null
+++ b/dtmutil/consts.go
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2021 yedf. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+package dtmutil
+
+const (
+ // DefaultHTTPServer default url for http server. used by test and examples
+ DefaultHTTPServer = "http://localhost:36789/api/dtmsvr"
+ // DefaultGrpcServer default url for grpc server. used by test and examples
+ DefaultGrpcServer = "localhost:36790"
+)
diff --git a/common/db.go b/dtmutil/db.go
similarity index 78%
rename from common/db.go
rename to dtmutil/db.go
index a9c315d..86973b2 100644
--- a/common/db.go
+++ b/dtmutil/db.go
@@ -1,4 +1,4 @@
-package common
+package dtmutil
import (
"database/sql"
@@ -7,10 +7,11 @@ import (
"sync"
"time"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
_ "github.com/go-sql-driver/mysql" // register mysql driver
_ "github.com/lib/pq" // register postgres driver
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
"gorm.io/gorm"
@@ -27,7 +28,7 @@ func getGormDialetor(driver string, dsn string) gorm.Dialector {
if driver == dtmcli.DBTypePostgres {
return postgres.Open(dsn)
}
- dtmimp.PanicIf(driver != dtmcli.DBTypeMysql, fmt.Errorf("unkown driver: %s", driver))
+ dtmimp.PanicIf(driver != dtmcli.DBTypeMysql, fmt.Errorf("unknown driver: %s", driver))
return mysql.Open(dsn)
}
@@ -65,7 +66,7 @@ func (op *tracePlugin) Initialize(db *gorm.DB) (err error) {
after := func(db *gorm.DB) {
_ts, _ := db.InstanceGet("ivy.startTime")
sql := db.Dialector.Explain(db.Statement.SQL.String(), db.Statement.Vars...)
- dtmimp.Logf("used: %d ms affected: %d sql is: %s", time.Since(_ts.(time.Time)).Milliseconds(), db.RowsAffected, sql)
+ logger.Debugf("used: %d ms affected: %d sql is: %s", time.Since(_ts.(time.Time)).Milliseconds(), db.RowsAffected, sql)
if v, ok := db.InstanceGet("ivy.must"); ok && v.(bool) {
if db.Error != nil && db.Error != gorm.ErrRecordNotFound {
panic(db.Error)
@@ -76,7 +77,7 @@ func (op *tracePlugin) Initialize(db *gorm.DB) (err error) {
beforeName := "cb_before"
afterName := "cb_after"
- dtmimp.Logf("installing db plugin: %s", op.Name())
+ logger.Debugf("installing db plugin: %s", op.Name())
// 开始前
_ = db.Callback().Create().Before("gorm:before_create").Register(beforeName, before)
_ = db.Callback().Query().Before("gorm:query").Register(beforeName, before)
@@ -95,27 +96,22 @@ func (op *tracePlugin) Initialize(db *gorm.DB) (err error) {
return
}
-// SetDBConn set db connection conf
-func SetDBConn(db *DB) {
- sqldb, _ := db.DB.DB()
- sqldb.SetMaxOpenConns(int(Config.Store.MaxOpenConns))
- sqldb.SetMaxIdleConns(int(Config.Store.MaxIdleConns))
- sqldb.SetConnMaxLifetime(time.Duration(Config.Store.ConnMaxLifeTime) * time.Minute)
-}
-
// DbGet get db connection for specified conf
-func DbGet(conf dtmcli.DBConf) *DB {
+func DbGet(conf dtmcli.DBConf, ops ...func(*gorm.DB)) *DB {
dsn := dtmimp.GetDsn(conf)
db, ok := dbs.Load(dsn)
if !ok {
- dtmimp.Logf("connecting %s", strings.Replace(dsn, conf.Passwrod, "****", 1))
+ logger.Debugf("connecting %s", strings.Replace(dsn, conf.Password, "****", 1))
db1, err := gorm.Open(getGormDialetor(conf.Driver, dsn), &gorm.Config{
SkipDefaultTransaction: true,
})
dtmimp.E2P(err)
- db1.Use(&tracePlugin{})
+ err = db1.Use(&tracePlugin{})
+ dtmimp.E2P(err)
db = &DB{DB: db1}
- SetDBConn(db.(*DB))
+ for _, op := range ops {
+ op(db1)
+ }
dbs.Store(dsn, db)
}
return db.(*DB)
diff --git a/dtmutil/utils.go b/dtmutil/utils.go
new file mode 100644
index 0000000..849b539
--- /dev/null
+++ b/dtmutil/utils.go
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2021 yedf. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+package dtmutil
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "path/filepath"
+ "strings"
+ "time"
+
+ "github.com/gin-gonic/gin"
+ "github.com/go-resty/resty/v2"
+
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+)
+
+// GetGinApp init and return gin
+func GetGinApp() *gin.Engine {
+ gin.SetMode(gin.ReleaseMode)
+ app := gin.New()
+ app.Use(gin.Recovery())
+ app.Use(func(c *gin.Context) {
+ body := ""
+ if c.Request.Body != nil {
+ rb, err := c.GetRawData()
+ dtmimp.E2P(err)
+ if len(rb) > 0 {
+ body = string(rb)
+ c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(rb))
+ }
+ }
+ logger.Debugf("begin %s %s body: %s", c.Request.Method, c.Request.URL, body)
+ c.Next()
+ })
+ app.Any("/api/ping", func(c *gin.Context) { c.JSON(200, map[string]interface{}{"msg": "pong"}) })
+ return app
+}
+
+// WrapHandler2 wrap a function te bo the handler of gin request
+func WrapHandler2(fn func(*gin.Context) interface{}) gin.HandlerFunc {
+ return func(c *gin.Context) {
+ began := time.Now()
+ var err error
+ r := func() interface{} {
+ defer dtmimp.P2E(&err)
+ return fn(c)
+ }()
+
+ status := http.StatusOK
+
+ // in dtm test/busi, there are some functions, which will return a resty response
+ // pass resty response as gin's response
+ if resp, ok := r.(*resty.Response); ok {
+ b := resp.Body()
+ status = resp.StatusCode()
+ r = nil
+ err = json.Unmarshal(b, &r)
+ }
+
+ // error maybe returned in r, assign it to err
+ if ne, ok := r.(error); ok && err == nil {
+ err = ne
+ }
+
+ // if err != nil || r == nil. then set the status and dtm_result
+ // dtm_result is for compatible with version lower than v1.10
+ // when >= v1.10, result test should base on status, not dtm_result.
+ result := map[string]interface{}{}
+ if err != nil {
+ if errors.Is(err, dtmcli.ErrFailure) {
+ status = http.StatusConflict
+ result["dtm_result"] = dtmcli.ResultFailure
+ } else if errors.Is(err, dtmcli.ErrOngoing) {
+ status = http.StatusTooEarly
+ result["dtm_result"] = dtmcli.ResultOngoing
+ } else if err != nil {
+ status = http.StatusInternalServerError
+ }
+ result["message"] = err.Error()
+ r = result
+ } else if r == nil {
+ result["dtm_result"] = dtmcli.ResultSuccess
+ r = result
+ }
+
+ b, _ := json.Marshal(r)
+ cont := string(b)
+ if status == http.StatusOK || status == http.StatusTooEarly {
+ logger.Infof("%2dms %d %s %s %s", time.Since(began).Milliseconds(), status, c.Request.Method, c.Request.RequestURI, cont)
+ } else {
+ logger.Errorf("%2dms %d %s %s %s", time.Since(began).Milliseconds(), status, c.Request.Method, c.Request.RequestURI, cont)
+ }
+ c.JSON(status, r)
+ }
+}
+
+// MustGetwd must version of os.Getwd
+func MustGetwd() string {
+ wd, err := os.Getwd()
+ dtmimp.E2P(err)
+ return wd
+}
+
+// GetSQLDir 获取调用该函数的caller源代码的目录,主要用于测试时,查找相关文件
+func GetSQLDir() string {
+ wd := MustGetwd()
+ if filepath.Base(wd) == "test" {
+ wd = filepath.Dir(wd)
+ }
+ return wd + "/sqls"
+}
+
+// RecoverPanic execs recovery operation
+func RecoverPanic(err *error) {
+ if x := recover(); x != nil {
+ e := dtmimp.AsError(x)
+ if err != nil {
+ *err = e
+ }
+ }
+}
+
+// GetNextTime gets next time from second
+func GetNextTime(second int64) *time.Time {
+ next := time.Now().Add(time.Duration(second) * time.Second)
+ return &next
+}
+
+// RunSQLScript 1
+func RunSQLScript(conf dtmcli.DBConf, script string, skipDrop bool) {
+ con, err := dtmimp.StandaloneDB(conf)
+ logger.FatalIfError(err)
+ defer func() { _ = con.Close() }()
+ content, err := ioutil.ReadFile(script)
+ logger.FatalIfError(err)
+ sqls := strings.Split(string(content), ";")
+ for _, sql := range sqls {
+ s := strings.TrimSpace(sql)
+ if s == "" || (skipDrop && strings.Contains(s, "drop")) {
+ continue
+ }
+ _, err = dtmimp.DBExec(con, s)
+ logger.FatalIfError(err)
+ logger.Infof("sql scripts finished: %s", s)
+ }
+}
diff --git a/common/utils_test.go b/dtmutil/utils_test.go
similarity index 69%
rename from common/utils_test.go
rename to dtmutil/utils_test.go
index 303a8ab..4ce1507 100644
--- a/common/utils_test.go
+++ b/dtmutil/utils_test.go
@@ -4,7 +4,7 @@
* license that can be found in the LICENSE file.
*/
-package common
+package dtmutil
import (
"errors"
@@ -16,33 +16,33 @@ import (
"testing"
"github.com/gin-gonic/gin"
- "github.com/go-playground/assert/v2"
+ "github.com/stretchr/testify/assert"
)
func TestGin(t *testing.T) {
app := GetGinApp()
- app.GET("/api/sample", WrapHandler(func(c *gin.Context) (interface{}, error) {
- return 1, nil
+ app.GET("/api/sample", WrapHandler2(func(c *gin.Context) interface{} {
+ return 1
}))
- app.GET("/api/error", WrapHandler(func(c *gin.Context) (interface{}, error) {
- return nil, errors.New("err1")
+ app.GET("/api/error", WrapHandler2(func(c *gin.Context) interface{} {
+ return errors.New("err1")
}))
getResultString := func(api string, body io.Reader) string {
req, _ := http.NewRequest("GET", api, body)
w := httptest.NewRecorder()
app.ServeHTTP(w, req)
- return string(w.Body.Bytes())
+ return w.Body.String()
}
assert.Equal(t, "{\"msg\":\"pong\"}", getResultString("/api/ping", nil))
assert.Equal(t, "1", getResultString("/api/sample", nil))
- assert.Equal(t, "{\"code\":500,\"message\":\"err1\"}", getResultString("/api/error", strings.NewReader("{}")))
+ assert.Equal(t, "{\"message\":\"err1\"}", getResultString("/api/error", strings.NewReader("{}")))
}
func TestFuncs(t *testing.T) {
wd := MustGetwd()
assert.NotEqual(t, "", wd)
- dir1 := GetSqlDir()
+ dir1 := GetSQLDir()
assert.Equal(t, true, strings.HasSuffix(dir1, "/sqls"))
}
diff --git a/examples/base.go b/examples/base.go
deleted file mode 100644
index d9b8fcf..0000000
--- a/examples/base.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package examples
-
-import "fmt"
-
-func Startup() {
- InitConfig()
- GrpcStartup()
- BaseAppStartup()
-}
-
-func InitConfig() {
- DtmHttpServer = fmt.Sprintf("http://localhost:%d/api/dtmsvr", config.HttpPort)
- DtmGrpcServer = fmt.Sprintf("localhost:%d", config.GrpcPort)
-}
diff --git a/examples/base_http.go b/examples/base_http.go
deleted file mode 100644
index 8aa7f37..0000000
--- a/examples/base_http.go
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package examples
-
-import (
- "database/sql"
- "errors"
- "fmt"
- "strings"
- "time"
-
- "github.com/gin-gonic/gin"
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "gorm.io/driver/mysql"
- "gorm.io/driver/postgres"
- "gorm.io/gorm"
-)
-
-const (
- // BusiAPI busi api prefix
- BusiAPI = "/api/busi"
- // BusiPort busi server port
- BusiPort = 8081
- // BusiGrpcPort busi server port
- BusiGrpcPort = 58081
-)
-
-type setupFunc func(*gin.Engine)
-
-var setupFuncs = map[string]setupFunc{}
-
-// Busi busi service url prefix
-var Busi string = fmt.Sprintf("http://localhost:%d%s", BusiPort, BusiAPI)
-
-// BaseAppStartup base app startup
-func BaseAppStartup() *gin.Engine {
- dtmimp.Logf("examples starting")
- app := common.GetGinApp()
- app.Use(func(c *gin.Context) {
- v := MainSwitch.NextResult.Fetch()
- if v != "" {
- c.JSON(200, gin.H{"dtm_result": v})
- c.Abort()
- return
- }
- c.Next()
- })
-
- BaseAddRoute(app)
- for k, v := range setupFuncs {
- dtmimp.Logf("initing %s", k)
- v(app)
- }
- dtmimp.Logf("Starting busi at: %d", BusiPort)
- go app.Run(fmt.Sprintf(":%d", BusiPort))
-
- time.Sleep(100 * time.Millisecond)
- return app
-}
-
-// AutoEmptyString auto reset to empty when used once
-type AutoEmptyString struct {
- value string
-}
-
-// SetOnce set a value once
-func (s *AutoEmptyString) SetOnce(v string) {
- s.value = v
-}
-
-// Fetch fetch the stored value, then reset the value to empty
-func (s *AutoEmptyString) Fetch() string {
- v := s.value
- s.value = ""
- return v
-}
-
-type mainSwitchType struct {
- TransInResult AutoEmptyString
- TransOutResult AutoEmptyString
- TransInConfirmResult AutoEmptyString
- TransOutConfirmResult AutoEmptyString
- TransInRevertResult AutoEmptyString
- TransOutRevertResult AutoEmptyString
- CanSubmitResult AutoEmptyString
- NextResult AutoEmptyString
-}
-
-// MainSwitch controls busi success or fail
-var MainSwitch mainSwitchType
-
-func handleGeneralBusiness(c *gin.Context, result1 string, result2 string, busi string) (interface{}, error) {
- info := infoFromContext(c)
- res := dtmimp.OrString(result1, result2, dtmcli.ResultSuccess)
- dtmimp.Logf("%s %s result: %s", busi, info.String(), res)
- if res == "ERROR" {
- return nil, errors.New("ERROR from user")
- }
- return map[string]interface{}{"dtm_result": res}, nil
-}
-
-func error2Resp(err error) (interface{}, error) {
- if err != nil {
- s := err.Error()
- if strings.Contains(s, dtmcli.ResultFailure) || strings.Contains(s, dtmcli.ResultOngoing) {
- return gin.H{"dtm_result": s}, nil
- }
- }
- return nil, err
-}
-
-// BaseAddRoute add base route handler
-func BaseAddRoute(app *gin.Engine) {
- app.POST(BusiAPI+"/TransIn", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
- return handleGeneralBusiness(c, MainSwitch.TransInResult.Fetch(), reqFrom(c).TransInResult, "transIn")
- }))
- app.POST(BusiAPI+"/TransOut", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
- return handleGeneralBusiness(c, MainSwitch.TransOutResult.Fetch(), reqFrom(c).TransOutResult, "TransOut")
- }))
- app.POST(BusiAPI+"/TransInConfirm", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
- return handleGeneralBusiness(c, MainSwitch.TransInConfirmResult.Fetch(), "", "TransInConfirm")
- }))
- app.POST(BusiAPI+"/TransOutConfirm", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
- return handleGeneralBusiness(c, MainSwitch.TransOutConfirmResult.Fetch(), "", "TransOutConfirm")
- }))
- app.POST(BusiAPI+"/TransInRevert", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
- return handleGeneralBusiness(c, MainSwitch.TransInRevertResult.Fetch(), "", "TransInRevert")
- }))
- app.POST(BusiAPI+"/TransOutRevert", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
- return handleGeneralBusiness(c, MainSwitch.TransOutRevertResult.Fetch(), "", "TransOutRevert")
- }))
- app.GET(BusiAPI+"/CanSubmit", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
- dtmimp.Logf("%s CanSubmit", c.Query("gid"))
- return dtmimp.OrString(MainSwitch.CanSubmitResult.Fetch(), dtmcli.ResultSuccess), nil
- }))
- app.POST(BusiAPI+"/TransInXa", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
- err := XaClient.XaLocalTransaction(c.Request.URL.Query(), func(db *sql.DB, xa *dtmcli.Xa) error {
- if reqFrom(c).TransInResult == dtmcli.ResultFailure {
- return dtmcli.ErrFailure
- }
- _, err := dtmimp.DBExec(db, "update dtm_busi.user_account set balance=balance+? where user_id=?", reqFrom(c).Amount, 2)
- return err
- })
- return error2Resp(err)
- }))
- app.POST(BusiAPI+"/TransOutXa", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
- err := XaClient.XaLocalTransaction(c.Request.URL.Query(), func(db *sql.DB, xa *dtmcli.Xa) error {
- if reqFrom(c).TransOutResult == dtmcli.ResultFailure {
- return dtmcli.ErrFailure
- }
- _, err := dtmimp.DBExec(db, "update dtm_busi.user_account set balance=balance-? where user_id=?", reqFrom(c).Amount, 1)
- return err
- })
- return error2Resp(err)
- }))
-
- app.POST(BusiAPI+"/TransOutXaGorm", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
- err := XaClient.XaLocalTransaction(c.Request.URL.Query(), func(db *sql.DB, xa *dtmcli.Xa) error {
- if reqFrom(c).TransOutResult == dtmcli.ResultFailure {
- return dtmcli.ErrFailure
- }
- var dia gorm.Dialector = nil
- if dtmcli.GetCurrentDBType() == dtmcli.DBTypeMysql {
- dia = mysql.New(mysql.Config{Conn: db})
- } else if dtmcli.GetCurrentDBType() == dtmcli.DBTypePostgres {
- dia = postgres.New(postgres.Config{Conn: db})
- }
- gdb, err := gorm.Open(dia, &gorm.Config{})
- if err != nil {
- return err
- }
- dbr := gdb.Exec("update dtm_busi.user_account set balance=balance-? where user_id=?", reqFrom(c).Amount, 1)
- return dbr.Error
- })
- return error2Resp(err)
- }))
-
- app.POST(BusiAPI+"/TestPanic", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
- if c.Query("panic_error") != "" {
- panic(errors.New("panic_error"))
- } else if c.Query("panic_string") != "" {
- panic("panic_string")
- }
- return "SUCCESS", nil
- }))
-}
diff --git a/examples/base_types.go b/examples/base_types.go
deleted file mode 100644
index a40f491..0000000
--- a/examples/base_types.go
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package examples
-
-import (
- "context"
- "database/sql"
- "fmt"
-
- "github.com/gin-gonic/gin"
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmgrpc"
-)
-
-// DtmHttpServer dtm service address
-var DtmHttpServer = fmt.Sprintf("http://localhost:%d/api/dtmsvr", 36789)
-
-// DtmGrpcServer dtm grpc service address
-var DtmGrpcServer = fmt.Sprintf("localhost:%d", 36790)
-
-// TransReq transaction request payload
-type TransReq struct {
- Amount int `json:"amount"`
- TransInResult string `json:"transInResult"`
- TransOutResult string `json:"transOutResult"`
-}
-
-func (t *TransReq) String() string {
- return fmt.Sprintf("amount: %d transIn: %s transOut: %s", t.Amount, t.TransInResult, t.TransOutResult)
-}
-
-// GenTransReq 1
-func GenTransReq(amount int, outFailed bool, inFailed bool) *TransReq {
- return &TransReq{
- Amount: amount,
- TransOutResult: dtmimp.If(outFailed, dtmcli.ResultFailure, "").(string),
- TransInResult: dtmimp.If(inFailed, dtmcli.ResultFailure, "").(string),
- }
-}
-
-// GenBusiReq 1
-func GenBusiReq(amount int, outFailed bool, inFailed bool) *BusiReq {
- return &BusiReq{
- Amount: int64(amount),
- TransOutResult: dtmimp.If(outFailed, dtmcli.ResultFailure, "").(string),
- TransInResult: dtmimp.If(inFailed, dtmcli.ResultFailure, "").(string),
- }
-}
-
-func reqFrom(c *gin.Context) *TransReq {
- v, ok := c.Get("trans_req")
- if !ok {
- req := TransReq{}
- err := c.BindJSON(&req)
- dtmimp.FatalIfError(err)
- c.Set("trans_req", &req)
- v = &req
- }
- return v.(*TransReq)
-}
-
-func infoFromContext(c *gin.Context) *dtmcli.BranchBarrier {
- info := dtmcli.BranchBarrier{
- TransType: c.Query("trans_type"),
- Gid: c.Query("gid"),
- BranchID: c.Query("branch_id"),
- Op: c.Query("op"),
- }
- return &info
-}
-
-func dbGet() *common.DB {
- return common.DbGet(config.ExamplesDB)
-}
-
-func sdbGet() *sql.DB {
- db, err := dtmimp.PooledDB(config.ExamplesDB)
- dtmimp.FatalIfError(err)
- return db
-}
-
-func txGet() *sql.Tx {
- db := sdbGet()
- tx, err := db.Begin()
- dtmimp.FatalIfError(err)
- return tx
-}
-
-// MustBarrierFromGin 1
-func MustBarrierFromGin(c *gin.Context) *dtmcli.BranchBarrier {
- ti, err := dtmcli.BarrierFromQuery(c.Request.URL.Query())
- dtmimp.FatalIfError(err)
- return ti
-}
-
-// MustBarrierFromGrpc 1
-func MustBarrierFromGrpc(ctx context.Context) *dtmcli.BranchBarrier {
- ti, err := dtmgrpc.BarrierFromGrpc(ctx)
- dtmimp.FatalIfError(err)
- return ti
-}
diff --git a/examples/busi.pb.go b/examples/busi.pb.go
deleted file mode 100644
index 7746311..0000000
--- a/examples/busi.pb.go
+++ /dev/null
@@ -1,330 +0,0 @@
-// Code generated by protoc-gen-go. DO NOT EDIT.
-// versions:
-// protoc-gen-go v1.27.1
-// protoc v3.17.3
-// source: examples/busi.proto
-
-package examples
-
-import (
- protoreflect "google.golang.org/protobuf/reflect/protoreflect"
- protoimpl "google.golang.org/protobuf/runtime/protoimpl"
- emptypb "google.golang.org/protobuf/types/known/emptypb"
- reflect "reflect"
- sync "sync"
-)
-
-const (
- // Verify that this generated code is sufficiently up-to-date.
- _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
- // Verify that runtime/protoimpl is sufficiently up-to-date.
- _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
-)
-
-// DtmRequest request sent to dtm server
-type BusiReq struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- Amount int64 `protobuf:"varint,1,opt,name=Amount,proto3" json:"Amount,omitempty"`
- TransOutResult string `protobuf:"bytes,2,opt,name=TransOutResult,proto3" json:"TransOutResult,omitempty"`
- TransInResult string `protobuf:"bytes,3,opt,name=TransInResult,proto3" json:"TransInResult,omitempty"`
-}
-
-func (x *BusiReq) Reset() {
- *x = BusiReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_examples_busi_proto_msgTypes[0]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
-}
-
-func (x *BusiReq) String() string {
- return protoimpl.X.MessageStringOf(x)
-}
-
-func (*BusiReq) ProtoMessage() {}
-
-func (x *BusiReq) ProtoReflect() protoreflect.Message {
- mi := &file_examples_busi_proto_msgTypes[0]
- if protoimpl.UnsafeEnabled && x != nil {
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- if ms.LoadMessageInfo() == nil {
- ms.StoreMessageInfo(mi)
- }
- return ms
- }
- return mi.MessageOf(x)
-}
-
-// Deprecated: Use BusiReq.ProtoReflect.Descriptor instead.
-func (*BusiReq) Descriptor() ([]byte, []int) {
- return file_examples_busi_proto_rawDescGZIP(), []int{0}
-}
-
-func (x *BusiReq) GetAmount() int64 {
- if x != nil {
- return x.Amount
- }
- return 0
-}
-
-func (x *BusiReq) GetTransOutResult() string {
- if x != nil {
- return x.TransOutResult
- }
- return ""
-}
-
-func (x *BusiReq) GetTransInResult() string {
- if x != nil {
- return x.TransInResult
- }
- return ""
-}
-
-type BusiReply struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
-}
-
-func (x *BusiReply) Reset() {
- *x = BusiReply{}
- if protoimpl.UnsafeEnabled {
- mi := &file_examples_busi_proto_msgTypes[1]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
-}
-
-func (x *BusiReply) String() string {
- return protoimpl.X.MessageStringOf(x)
-}
-
-func (*BusiReply) ProtoMessage() {}
-
-func (x *BusiReply) ProtoReflect() protoreflect.Message {
- mi := &file_examples_busi_proto_msgTypes[1]
- if protoimpl.UnsafeEnabled && x != nil {
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- if ms.LoadMessageInfo() == nil {
- ms.StoreMessageInfo(mi)
- }
- return ms
- }
- return mi.MessageOf(x)
-}
-
-// Deprecated: Use BusiReply.ProtoReflect.Descriptor instead.
-func (*BusiReply) Descriptor() ([]byte, []int) {
- return file_examples_busi_proto_rawDescGZIP(), []int{1}
-}
-
-func (x *BusiReply) GetMessage() string {
- if x != nil {
- return x.Message
- }
- return ""
-}
-
-var File_examples_busi_proto protoreflect.FileDescriptor
-
-var file_examples_busi_proto_rawDesc = []byte{
- 0x0a, 0x13, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x62, 0x75, 0x73, 0x69, 0x2e,
- 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x1a,
- 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
- 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6f, 0x0a, 0x07,
- 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x41, 0x6d, 0x6f, 0x75, 0x6e,
- 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12,
- 0x26, 0x0a, 0x0e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c,
- 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75,
- 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x72, 0x61, 0x6e, 0x73,
- 0x49, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d,
- 0x54, 0x72, 0x61, 0x6e, 0x73, 0x49, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x25, 0x0a,
- 0x09, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65,
- 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73,
- 0x73, 0x61, 0x67, 0x65, 0x32, 0x97, 0x08, 0x0a, 0x04, 0x42, 0x75, 0x73, 0x69, 0x12, 0x35, 0x0a,
- 0x09, 0x43, 0x61, 0x6e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x12, 0x11, 0x2e, 0x65, 0x78, 0x61,
- 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e,
- 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x70,
- 0x6c, 0x79, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x07, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x49, 0x6e, 0x12,
- 0x11, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52,
- 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
- 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x37, 0x0a, 0x08,
- 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x12, 0x11, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70,
- 0x6c, 0x65, 0x73, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67, 0x6f,
- 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
- 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3c, 0x0a, 0x0d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x49, 0x6e,
- 0x52, 0x65, 0x76, 0x65, 0x72, 0x74, 0x12, 0x11, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
- 0x73, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
- 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
- 0x79, 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x0e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x52,
- 0x65, 0x76, 0x65, 0x72, 0x74, 0x12, 0x11, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73,
- 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
- 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
- 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x0e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x49, 0x6e, 0x43, 0x6f, 0x6e,
- 0x66, 0x69, 0x72, 0x6d, 0x12, 0x11, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e,
- 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
- 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22,
- 0x00, 0x12, 0x3e, 0x0a, 0x0f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x43, 0x6f, 0x6e,
- 0x66, 0x69, 0x72, 0x6d, 0x12, 0x11, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e,
- 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
- 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22,
- 0x00, 0x12, 0x3c, 0x0a, 0x08, 0x58, 0x61, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x12, 0x16, 0x2e,
- 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
- 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
- 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12,
- 0x38, 0x0a, 0x09, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x49, 0x6e, 0x58, 0x61, 0x12, 0x11, 0x2e, 0x65,
- 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a,
- 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
- 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x0a, 0x54, 0x72, 0x61,
- 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x58, 0x61, 0x12, 0x11, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c,
- 0x65, 0x73, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f,
- 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70,
- 0x74, 0x79, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x0a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x49, 0x6e, 0x54,
- 0x63, 0x63, 0x12, 0x11, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x42, 0x75,
- 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
- 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12,
- 0x3a, 0x0a, 0x0b, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x54, 0x63, 0x63, 0x12, 0x11,
- 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65,
- 0x71, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
- 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x10, 0x54,
- 0x72, 0x61, 0x6e, 0x73, 0x49, 0x6e, 0x54, 0x63, 0x63, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x12,
- 0x11, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52,
- 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
- 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x0c,
- 0x54, 0x72, 0x61, 0x6e, 0x73, 0x49, 0x6e, 0x42, 0x53, 0x61, 0x67, 0x61, 0x12, 0x11, 0x2e, 0x65,
- 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a,
- 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
- 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3c, 0x0a, 0x0d, 0x54, 0x72, 0x61,
- 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x42, 0x53, 0x61, 0x67, 0x61, 0x12, 0x11, 0x2e, 0x65, 0x78, 0x61,
- 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e,
- 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
- 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x12, 0x54, 0x72, 0x61, 0x6e, 0x73,
- 0x49, 0x6e, 0x52, 0x65, 0x76, 0x65, 0x72, 0x74, 0x42, 0x53, 0x61, 0x67, 0x61, 0x12, 0x11, 0x2e,
- 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71,
- 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
- 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x13, 0x54, 0x72,
- 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x76, 0x65, 0x72, 0x74, 0x42, 0x53, 0x61, 0x67,
- 0x61, 0x12, 0x11, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x42, 0x75, 0x73,
- 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
- 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x42, 0x0c,
- 0x5a, 0x0a, 0x2e, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72,
- 0x6f, 0x74, 0x6f, 0x33,
-}
-
-var (
- file_examples_busi_proto_rawDescOnce sync.Once
- file_examples_busi_proto_rawDescData = file_examples_busi_proto_rawDesc
-)
-
-func file_examples_busi_proto_rawDescGZIP() []byte {
- file_examples_busi_proto_rawDescOnce.Do(func() {
- file_examples_busi_proto_rawDescData = protoimpl.X.CompressGZIP(file_examples_busi_proto_rawDescData)
- })
- return file_examples_busi_proto_rawDescData
-}
-
-var file_examples_busi_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
-var file_examples_busi_proto_goTypes = []interface{}{
- (*BusiReq)(nil), // 0: examples.BusiReq
- (*BusiReply)(nil), // 1: examples.BusiReply
- (*emptypb.Empty)(nil), // 2: google.protobuf.Empty
-}
-var file_examples_busi_proto_depIdxs = []int32{
- 0, // 0: examples.Busi.CanSubmit:input_type -> examples.BusiReq
- 0, // 1: examples.Busi.TransIn:input_type -> examples.BusiReq
- 0, // 2: examples.Busi.TransOut:input_type -> examples.BusiReq
- 0, // 3: examples.Busi.TransInRevert:input_type -> examples.BusiReq
- 0, // 4: examples.Busi.TransOutRevert:input_type -> examples.BusiReq
- 0, // 5: examples.Busi.TransInConfirm:input_type -> examples.BusiReq
- 0, // 6: examples.Busi.TransOutConfirm:input_type -> examples.BusiReq
- 2, // 7: examples.Busi.XaNotify:input_type -> google.protobuf.Empty
- 0, // 8: examples.Busi.TransInXa:input_type -> examples.BusiReq
- 0, // 9: examples.Busi.TransOutXa:input_type -> examples.BusiReq
- 0, // 10: examples.Busi.TransInTcc:input_type -> examples.BusiReq
- 0, // 11: examples.Busi.TransOutTcc:input_type -> examples.BusiReq
- 0, // 12: examples.Busi.TransInTccNested:input_type -> examples.BusiReq
- 0, // 13: examples.Busi.TransInBSaga:input_type -> examples.BusiReq
- 0, // 14: examples.Busi.TransOutBSaga:input_type -> examples.BusiReq
- 0, // 15: examples.Busi.TransInRevertBSaga:input_type -> examples.BusiReq
- 0, // 16: examples.Busi.TransOutRevertBSaga:input_type -> examples.BusiReq
- 1, // 17: examples.Busi.CanSubmit:output_type -> examples.BusiReply
- 2, // 18: examples.Busi.TransIn:output_type -> google.protobuf.Empty
- 2, // 19: examples.Busi.TransOut:output_type -> google.protobuf.Empty
- 2, // 20: examples.Busi.TransInRevert:output_type -> google.protobuf.Empty
- 2, // 21: examples.Busi.TransOutRevert:output_type -> google.protobuf.Empty
- 2, // 22: examples.Busi.TransInConfirm:output_type -> google.protobuf.Empty
- 2, // 23: examples.Busi.TransOutConfirm:output_type -> google.protobuf.Empty
- 2, // 24: examples.Busi.XaNotify:output_type -> google.protobuf.Empty
- 2, // 25: examples.Busi.TransInXa:output_type -> google.protobuf.Empty
- 2, // 26: examples.Busi.TransOutXa:output_type -> google.protobuf.Empty
- 2, // 27: examples.Busi.TransInTcc:output_type -> google.protobuf.Empty
- 2, // 28: examples.Busi.TransOutTcc:output_type -> google.protobuf.Empty
- 2, // 29: examples.Busi.TransInTccNested:output_type -> google.protobuf.Empty
- 2, // 30: examples.Busi.TransInBSaga:output_type -> google.protobuf.Empty
- 2, // 31: examples.Busi.TransOutBSaga:output_type -> google.protobuf.Empty
- 2, // 32: examples.Busi.TransInRevertBSaga:output_type -> google.protobuf.Empty
- 2, // 33: examples.Busi.TransOutRevertBSaga:output_type -> google.protobuf.Empty
- 17, // [17:34] is the sub-list for method output_type
- 0, // [0:17] is the sub-list for method input_type
- 0, // [0:0] is the sub-list for extension type_name
- 0, // [0:0] is the sub-list for extension extendee
- 0, // [0:0] is the sub-list for field type_name
-}
-
-func init() { file_examples_busi_proto_init() }
-func file_examples_busi_proto_init() {
- if File_examples_busi_proto != nil {
- return
- }
- if !protoimpl.UnsafeEnabled {
- file_examples_busi_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*BusiReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_examples_busi_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*BusiReply); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- }
- type x struct{}
- out := protoimpl.TypeBuilder{
- File: protoimpl.DescBuilder{
- GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
- RawDescriptor: file_examples_busi_proto_rawDesc,
- NumEnums: 0,
- NumMessages: 2,
- NumExtensions: 0,
- NumServices: 1,
- },
- GoTypes: file_examples_busi_proto_goTypes,
- DependencyIndexes: file_examples_busi_proto_depIdxs,
- MessageInfos: file_examples_busi_proto_msgTypes,
- }.Build()
- File_examples_busi_proto = out.File
- file_examples_busi_proto_rawDesc = nil
- file_examples_busi_proto_goTypes = nil
- file_examples_busi_proto_depIdxs = nil
-}
diff --git a/examples/data.go b/examples/data.go
deleted file mode 100644
index b0459c4..0000000
--- a/examples/data.go
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package examples
-
-import (
- "fmt"
-
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli/dtmimp"
-)
-
-var config = &common.Config
-
-func resetXaData() {
- if config.ExamplesDB.Driver != "mysql" {
- return
- }
-
- db := dbGet()
- type XaRow struct {
- Data string
- }
- xas := []XaRow{}
- db.Must().Raw("xa recover").Scan(&xas)
- for _, xa := range xas {
- db.Must().Exec(fmt.Sprintf("xa rollback '%s'", xa.Data))
- }
-}
-
-// PopulateDB populate example mysql data
-func PopulateDB(skipDrop bool) {
- resetXaData()
- file := fmt.Sprintf("%s/examples.%s.sql", common.GetSqlDir(), config.ExamplesDB.Driver)
- common.RunSQLScript(config.ExamplesDB, file, skipDrop)
- file = fmt.Sprintf("%s/dtmcli.barrier.%s.sql", common.GetSqlDir(), config.ExamplesDB.Driver)
- common.RunSQLScript(config.ExamplesDB, file, skipDrop)
-}
-
-type sampleInfo struct {
- Arg string
- Action func() string
- Desc string
-}
-
-// Samples 所有的示例都会注册到这里
-var Samples = map[string]*sampleInfo{}
-
-func addSample(name string, fn func() string) {
- dtmimp.LogIfFatalf(Samples[name] != nil, "%s already exists", name)
- Samples[name] = &sampleInfo{Arg: name, Action: fn}
-}
diff --git a/examples/grpc_msg.go b/examples/grpc_msg.go
deleted file mode 100644
index 670e8e6..0000000
--- a/examples/grpc_msg.go
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package examples
-
-import (
- "github.com/yedf/dtm/dtmcli/dtmimp"
- dtmgrpc "github.com/yedf/dtm/dtmgrpc"
-)
-
-func init() {
- addSample("grpc_msg", func() string {
- req := &BusiReq{Amount: 30}
- gid := dtmgrpc.MustGenGid(DtmGrpcServer)
- msg := dtmgrpc.NewMsgGrpc(DtmGrpcServer, gid).
- Add(BusiGrpc+"/examples.Busi/TransOut", req).
- Add(BusiGrpc+"/examples.Busi/TransIn", req)
- err := msg.Submit()
- dtmimp.FatalIfError(err)
- return msg.Gid
- })
-}
diff --git a/examples/grpc_saga.go b/examples/grpc_saga.go
deleted file mode 100644
index fad3d8f..0000000
--- a/examples/grpc_saga.go
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package examples
-
-import (
- "github.com/yedf/dtm/dtmcli/dtmimp"
- dtmgrpc "github.com/yedf/dtm/dtmgrpc"
-)
-
-func init() {
- addSample("grpc_saga", func() string {
- req := &BusiReq{Amount: 30}
- gid := dtmgrpc.MustGenGid(DtmGrpcServer)
- saga := dtmgrpc.NewSagaGrpc(DtmGrpcServer, gid).
- Add(BusiGrpc+"/examples.Busi/TransOut", BusiGrpc+"/examples.Busi/TransOutRevert", req).
- Add(BusiGrpc+"/examples.Busi/TransIn", BusiGrpc+"/examples.Busi/TransOutRevert", req)
- err := saga.Submit()
- dtmimp.FatalIfError(err)
- return saga.Gid
- })
- addSample("grpc_saga_wait", func() string {
- req := &BusiReq{Amount: 30}
- gid := dtmgrpc.MustGenGid(DtmGrpcServer)
- saga := dtmgrpc.NewSagaGrpc(DtmGrpcServer, gid).
- Add(BusiGrpc+"/examples.Busi/TransOut", BusiGrpc+"/examples.Busi/TransOutRevert", req).
- Add(BusiGrpc+"/examples.Busi/TransIn", BusiGrpc+"/examples.Busi/TransOutRevert", req)
- saga.WaitResult = true
- err := saga.Submit()
- dtmimp.FatalIfError(err)
- return saga.Gid
- })
-}
diff --git a/examples/grpc_saga_barrier.go b/examples/grpc_saga_barrier.go
deleted file mode 100644
index 777b236..0000000
--- a/examples/grpc_saga_barrier.go
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package examples
-
-import (
- "context"
- "database/sql"
-
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmgrpc"
- "google.golang.org/grpc/codes"
- "google.golang.org/grpc/status"
- emptypb "google.golang.org/protobuf/types/known/emptypb"
-)
-
-func init() {
- addSample("grpc_saga_barrier", func() string {
- req := &BusiReq{Amount: 30}
- gid := dtmgrpc.MustGenGid(DtmGrpcServer)
- saga := dtmgrpc.NewSagaGrpc(DtmGrpcServer, gid).
- Add(BusiGrpc+"/examples.Busi/TransOutBSaga", BusiGrpc+"/examples.Busi/TransOutRevertBSaga", req).
- Add(BusiGrpc+"/examples.Busi/TransInBSaga", BusiGrpc+"/examples.Busi/TransInRevertBSaga", req)
- err := saga.Submit()
- dtmimp.FatalIfError(err)
- return saga.Gid
- })
-}
-
-func sagaGrpcBarrierAdjustBalance(db dtmcli.DB, uid int, amount int64, result string) error {
- if result == dtmcli.ResultFailure {
- return status.New(codes.Aborted, dtmcli.ResultFailure).Err()
- }
- _, err := dtmimp.DBExec(db, "update dtm_busi.user_account set balance = balance + ? where user_id = ?", amount, uid)
- return err
-
-}
-
-func (s *busiServer) TransInBSaga(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) {
- barrier := MustBarrierFromGrpc(ctx)
- return &emptypb.Empty{}, barrier.Call(txGet(), func(tx *sql.Tx) error {
- return sagaGrpcBarrierAdjustBalance(tx, 2, in.Amount, in.TransInResult)
- })
-}
-
-func (s *busiServer) TransOutBSaga(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) {
- barrier := MustBarrierFromGrpc(ctx)
- return &emptypb.Empty{}, barrier.Call(txGet(), func(tx *sql.Tx) error {
- return sagaGrpcBarrierAdjustBalance(tx, 1, -in.Amount, in.TransOutResult)
- })
-}
-
-func (s *busiServer) TransInRevertBSaga(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) {
- barrier := MustBarrierFromGrpc(ctx)
- return &emptypb.Empty{}, barrier.Call(txGet(), func(tx *sql.Tx) error {
- return sagaGrpcBarrierAdjustBalance(tx, 2, -in.Amount, "")
- })
-}
-
-func (s *busiServer) TransOutRevertBSaga(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) {
- barrier := MustBarrierFromGrpc(ctx)
- return &emptypb.Empty{}, barrier.Call(txGet(), func(tx *sql.Tx) error {
- return sagaGrpcBarrierAdjustBalance(tx, 1, in.Amount, "")
- })
-}
diff --git a/examples/grpc_tcc.go b/examples/grpc_tcc.go
deleted file mode 100644
index a806983..0000000
--- a/examples/grpc_tcc.go
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package examples
-
-import (
- "github.com/yedf/dtm/dtmcli/dtmimp"
- dtmgrpc "github.com/yedf/dtm/dtmgrpc"
- emptypb "google.golang.org/protobuf/types/known/emptypb"
-)
-
-func init() {
- addSample("grpc_tcc", func() string {
- dtmimp.Logf("tcc simple transaction begin")
- gid := dtmgrpc.MustGenGid(DtmGrpcServer)
- err := dtmgrpc.TccGlobalTransaction(DtmGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {
- data := &BusiReq{Amount: 30}
- r := &emptypb.Empty{}
- err := tcc.CallBranch(data, BusiGrpc+"/examples.Busi/TransOutTcc", BusiGrpc+"/examples.Busi/TransOutConfirm", BusiGrpc+"/examples.Busi/TransOutRevert", r)
- if err != nil {
- return err
- }
- err = tcc.CallBranch(data, BusiGrpc+"/examples.Busi/TransInTcc", BusiGrpc+"/examples.Busi/TransInConfirm", BusiGrpc+"/examples.Busi/TransInRevert", r)
- return err
- })
- dtmimp.FatalIfError(err)
- return gid
- })
-}
diff --git a/examples/grpc_xa.go b/examples/grpc_xa.go
deleted file mode 100644
index 41aba41..0000000
--- a/examples/grpc_xa.go
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package examples
-
-import (
- context "context"
-
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmgrpc"
- "google.golang.org/protobuf/types/known/emptypb"
-)
-
-func init() {
- addSample("grpc_xa", func() string {
- gid := dtmgrpc.MustGenGid(DtmGrpcServer)
- req := &BusiReq{Amount: 30}
- err := XaGrpcClient.XaGlobalTransaction(gid, func(xa *dtmgrpc.XaGrpc) error {
- r := &emptypb.Empty{}
- err := xa.CallBranch(req, BusiGrpc+"/examples.Busi/TransOutXa", r)
- if err != nil {
- return err
- }
- err = xa.CallBranch(req, BusiGrpc+"/examples.Busi/TransInXa", r)
- return err
- })
- dtmimp.FatalIfError(err)
- return gid
- })
-}
-
-func (s *busiServer) XaNotify(ctx context.Context, in *emptypb.Empty) (*emptypb.Empty, error) {
- return XaGrpcClient.HandleCallback(ctx)
-}
diff --git a/examples/http_gorm_xa.go b/examples/http_gorm_xa.go
deleted file mode 100644
index 6034469..0000000
--- a/examples/http_gorm_xa.go
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package examples
-
-import (
- "github.com/go-resty/resty/v2"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
-)
-
-func init() {
- addSample("xa_gorm", func() string {
- gid := dtmcli.MustGenGid(DtmHttpServer)
- err := XaClient.XaGlobalTransaction(gid, func(xa *dtmcli.Xa) (*resty.Response, error) {
- resp, err := xa.CallBranch(&TransReq{Amount: 30}, Busi+"/TransOutXaGorm")
- if err != nil {
- return resp, err
- }
- return xa.CallBranch(&TransReq{Amount: 30}, Busi+"/TransInXa")
- })
- dtmimp.FatalIfError(err)
- return gid
- })
-
-}
diff --git a/examples/http_msg.go b/examples/http_msg.go
deleted file mode 100644
index be0e9ac..0000000
--- a/examples/http_msg.go
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package examples
-
-import (
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
-)
-
-func init() {
- addSample("msg", func() string {
- dtmimp.Logf("a busi transaction begin")
- req := &TransReq{Amount: 30}
- msg := dtmcli.NewMsg(DtmHttpServer, dtmcli.MustGenGid(DtmHttpServer)).
- Add(Busi+"/TransOut", req).
- Add(Busi+"/TransIn", req)
- err := msg.Prepare(Busi + "/query")
- dtmimp.FatalIfError(err)
- dtmimp.Logf("busi trans submit")
- err = msg.Submit()
- dtmimp.FatalIfError(err)
- return msg.Gid
- })
-}
diff --git a/examples/http_saga.go b/examples/http_saga.go
deleted file mode 100644
index 54e2885..0000000
--- a/examples/http_saga.go
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package examples
-
-import (
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
-)
-
-func init() {
- addSample("saga", func() string {
- dtmimp.Logf("a saga busi transaction begin")
- req := &TransReq{Amount: 30}
- saga := dtmcli.NewSaga(DtmHttpServer, dtmcli.MustGenGid(DtmHttpServer)).
- Add(Busi+"/TransOut", Busi+"/TransOutRevert", req).
- Add(Busi+"/TransIn", Busi+"/TransInRevert", req)
- dtmimp.Logf("saga busi trans submit")
- err := saga.Submit()
- dtmimp.Logf("result gid is: %s", saga.Gid)
- dtmimp.FatalIfError(err)
- return saga.Gid
- })
- addSample("saga_wait", func() string {
- dtmimp.Logf("a saga busi transaction begin")
- req := &TransReq{Amount: 30}
- saga := dtmcli.NewSaga(DtmHttpServer, dtmcli.MustGenGid(DtmHttpServer)).
- Add(Busi+"/TransOut", Busi+"/TransOutRevert", req).
- Add(Busi+"/TransIn", Busi+"/TransInRevert", req)
- saga.SetOptions(&dtmcli.TransOptions{WaitResult: true})
- err := saga.Submit()
- dtmimp.Logf("result gid is: %s", saga.Gid)
- dtmimp.FatalIfError(err)
- return saga.Gid
- })
- addSample("concurrent_saga", func() string {
- dtmimp.Logf("a concurrent saga busi transaction begin")
- req := &TransReq{Amount: 30}
- csaga := dtmcli.NewSaga(DtmHttpServer, dtmcli.MustGenGid(DtmHttpServer)).
- Add(Busi+"/TransOut", Busi+"/TransOutRevert", req).
- Add(Busi+"/TransOut", Busi+"/TransOutRevert", req).
- Add(Busi+"/TransIn", Busi+"/TransInRevert", req).
- Add(Busi+"/TransIn", Busi+"/TransInRevert", req).
- EnableConcurrent().
- AddBranchOrder(2, []int{0, 1}).
- AddBranchOrder(3, []int{0, 1})
- dtmimp.Logf("concurrent saga busi trans submit")
- err := csaga.Submit()
- dtmimp.Logf("result gid is: %s", csaga.Gid)
- dtmimp.FatalIfError(err)
- return csaga.Gid
- })
-}
diff --git a/examples/http_saga_barrier.go b/examples/http_saga_barrier.go
deleted file mode 100644
index c68aeb8..0000000
--- a/examples/http_saga_barrier.go
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package examples
-
-import (
- "database/sql"
-
- "github.com/gin-gonic/gin"
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
-)
-
-func init() {
- setupFuncs["SagaBarrierSetup"] = func(app *gin.Engine) {
- app.POST(BusiAPI+"/SagaBTransIn", common.WrapHandler(sagaBarrierTransIn))
- app.POST(BusiAPI+"/SagaBTransInCompensate", common.WrapHandler(sagaBarrierTransInCompensate))
- app.POST(BusiAPI+"/SagaBTransOut", common.WrapHandler(sagaBarrierTransOut))
- app.POST(BusiAPI+"/SagaBTransOutCompensate", common.WrapHandler(sagaBarrierTransOutCompensate))
- }
- addSample("saga_barrier", func() string {
- dtmimp.Logf("a busi transaction begin")
- req := &TransReq{Amount: 30}
- saga := dtmcli.NewSaga(DtmHttpServer, dtmcli.MustGenGid(DtmHttpServer)).
- Add(Busi+"/SagaBTransOut", Busi+"/SagaBTransOutCompensate", req).
- Add(Busi+"/SagaBTransIn", Busi+"/SagaBTransInCompensate", req)
- dtmimp.Logf("busi trans submit")
- err := saga.Submit()
- dtmimp.FatalIfError(err)
- return saga.Gid
- })
-}
-
-func sagaBarrierAdjustBalance(db dtmcli.DB, uid int, amount int) error {
- _, err := dtmimp.DBExec(db, "update dtm_busi.user_account set balance = balance + ? where user_id = ?", amount, uid)
- return err
-
-}
-
-func sagaBarrierTransIn(c *gin.Context) (interface{}, error) {
- req := reqFrom(c)
- if req.TransInResult != "" {
- return req.TransInResult, nil
- }
- barrier := MustBarrierFromGin(c)
- return dtmcli.MapSuccess, barrier.Call(txGet(), func(tx *sql.Tx) error {
- return sagaBarrierAdjustBalance(tx, 1, req.Amount)
- })
-}
-
-func sagaBarrierTransInCompensate(c *gin.Context) (interface{}, error) {
- barrier := MustBarrierFromGin(c)
- return dtmcli.MapSuccess, barrier.Call(txGet(), func(tx *sql.Tx) error {
- return sagaBarrierAdjustBalance(tx, 1, -reqFrom(c).Amount)
- })
-}
-
-func sagaBarrierTransOut(c *gin.Context) (interface{}, error) {
- req := reqFrom(c)
- if req.TransOutResult != "" {
- return req.TransOutResult, nil
- }
- barrier := MustBarrierFromGin(c)
- return dtmcli.MapSuccess, barrier.Call(txGet(), func(tx *sql.Tx) error {
- return sagaBarrierAdjustBalance(tx, 2, -req.Amount)
- })
-}
-
-func sagaBarrierTransOutCompensate(c *gin.Context) (interface{}, error) {
- barrier := MustBarrierFromGin(c)
- return dtmcli.MapSuccess, barrier.Call(txGet(), func(tx *sql.Tx) error {
- return sagaBarrierAdjustBalance(tx, 2, reqFrom(c).Amount)
- })
-}
diff --git a/examples/http_saga_gorm_barrier.go b/examples/http_saga_gorm_barrier.go
deleted file mode 100644
index 1f81c44..0000000
--- a/examples/http_saga_gorm_barrier.go
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package examples
-
-import (
- "database/sql"
-
- "github.com/gin-gonic/gin"
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
-)
-
-func init() {
- setupFuncs["SagaGormBarrierSetup"] = func(app *gin.Engine) {
- app.POST(BusiAPI+"/SagaBTransOutGorm", common.WrapHandler(sagaGormBarrierTransOut))
- }
- addSample("saga_gorm_barrier", func() string {
- dtmimp.Logf("a busi transaction begin")
- req := &TransReq{Amount: 30}
- saga := dtmcli.NewSaga(DtmHttpServer, dtmcli.MustGenGid(DtmHttpServer)).
- Add(Busi+"/SagaBTransOutGorm", Busi+"/SagaBTransOutCompensate", req).
- Add(Busi+"/SagaBTransIn", Busi+"/SagaBTransInCompensate", req)
- dtmimp.Logf("busi trans submit")
- err := saga.Submit()
- dtmimp.FatalIfError(err)
- return saga.Gid
- })
-
-}
-
-func sagaGormBarrierTransOut(c *gin.Context) (interface{}, error) {
- req := reqFrom(c)
- barrier := MustBarrierFromGin(c)
- tx := dbGet().DB.Begin()
- return dtmcli.MapSuccess, barrier.Call(tx.Statement.ConnPool.(*sql.Tx), func(tx1 *sql.Tx) error {
- return tx.Exec("update dtm_busi.user_account set balance = balance + ? where user_id = ?", -req.Amount, 2).Error
- })
-}
diff --git a/examples/http_tcc.go b/examples/http_tcc.go
deleted file mode 100644
index 49c949f..0000000
--- a/examples/http_tcc.go
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package examples
-
-import (
- "github.com/gin-gonic/gin"
- "github.com/go-resty/resty/v2"
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
-)
-
-func init() {
- setupFuncs["TccSetupSetup"] = func(app *gin.Engine) {
- app.POST(BusiAPI+"/TransInTccParent", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
- tcc, err := dtmcli.TccFromQuery(c.Request.URL.Query())
- dtmimp.FatalIfError(err)
- dtmimp.Logf("TransInTccParent ")
- return tcc.CallBranch(&TransReq{Amount: reqFrom(c).Amount}, Busi+"/TransIn", Busi+"/TransInConfirm", Busi+"/TransInRevert")
- }))
- }
- addSample("tcc_nested", func() string {
- gid := dtmcli.MustGenGid(DtmHttpServer)
- err := dtmcli.TccGlobalTransaction(DtmHttpServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
- resp, err := tcc.CallBranch(&TransReq{Amount: 30}, Busi+"/TransOut", Busi+"/TransOutConfirm", Busi+"/TransOutRevert")
- if err != nil {
- return resp, err
- }
- return tcc.CallBranch(&TransReq{Amount: 30}, Busi+"/TransInTccParent", Busi+"/TransInConfirm", Busi+"/TransInRevert")
- })
- dtmimp.FatalIfError(err)
- return gid
- })
- addSample("tcc", func() string {
- dtmimp.Logf("tcc simple transaction begin")
- gid := dtmcli.MustGenGid(DtmHttpServer)
- err := dtmcli.TccGlobalTransaction(DtmHttpServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
- resp, err := tcc.CallBranch(&TransReq{Amount: 30}, Busi+"/TransOut", Busi+"/TransOutConfirm", Busi+"/TransOutRevert")
- if err != nil {
- return resp, err
- }
- return tcc.CallBranch(&TransReq{Amount: 30}, Busi+"/TransIn", Busi+"/TransInConfirm", Busi+"/TransInRevert")
- })
- dtmimp.FatalIfError(err)
- return gid
- })
-}
diff --git a/examples/http_tcc_barrier.go b/examples/http_tcc_barrier.go
deleted file mode 100644
index 85e65da..0000000
--- a/examples/http_tcc_barrier.go
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package examples
-
-import (
- "database/sql"
- "fmt"
-
- "github.com/gin-gonic/gin"
- "github.com/go-resty/resty/v2"
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
-)
-
-func init() {
- setupFuncs["TccBarrierSetup"] = func(app *gin.Engine) {
- app.POST(BusiAPI+"/TccBTransInTry", common.WrapHandler(tccBarrierTransInTry))
- app.POST(BusiAPI+"/TccBTransInConfirm", common.WrapHandler(tccBarrierTransInConfirm))
- app.POST(BusiAPI+"/TccBTransInCancel", common.WrapHandler(tccBarrierTransInCancel))
- app.POST(BusiAPI+"/TccBTransOutTry", common.WrapHandler(tccBarrierTransOutTry))
- app.POST(BusiAPI+"/TccBTransOutConfirm", common.WrapHandler(tccBarrierTransOutConfirm))
- app.POST(BusiAPI+"/TccBTransOutCancel", common.WrapHandler(TccBarrierTransOutCancel))
- }
- addSample("tcc_barrier", func() string {
- dtmimp.Logf("tcc transaction begin")
- gid := dtmcli.MustGenGid(DtmHttpServer)
- err := dtmcli.TccGlobalTransaction(DtmHttpServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
- resp, err := tcc.CallBranch(&TransReq{Amount: 30}, Busi+"/TccBTransOutTry",
- Busi+"/TccBTransOutConfirm", Busi+"/TccBTransOutCancel")
- if err != nil {
- return resp, err
- }
- return tcc.CallBranch(&TransReq{Amount: 30}, Busi+"/TccBTransInTry", Busi+"/TccBTransInConfirm", Busi+"/TccBTransInCancel")
- })
- dtmimp.FatalIfError(err)
- return gid
- })
-}
-
-const transInUID = 1
-const transOutUID = 2
-
-func adjustTrading(db dtmcli.DB, uid int, amount int) error {
- affected, err := dtmimp.DBExec(db, `update dtm_busi.user_account set trading_balance=trading_balance+?
- where user_id=? and trading_balance + ? + balance >= 0`, amount, uid, amount)
- if err == nil && affected == 0 {
- return fmt.Errorf("update error, maybe balance not enough")
- }
- return err
-}
-
-func adjustBalance(db dtmcli.DB, uid int, amount int) error {
- affected, err := dtmimp.DBExec(db, `update dtm_busi.user_account set trading_balance=trading_balance-?,
- balance=balance+? where user_id=?`, amount, amount, uid)
- if err == nil && affected == 0 {
- return fmt.Errorf("update user_account 0 rows")
- }
- return err
-}
-
-// TCC下,转入
-func tccBarrierTransInTry(c *gin.Context) (interface{}, error) {
- req := reqFrom(c) // 去重构一下,改成可以重复使用的输入
- if req.TransInResult != "" {
- return req.TransInResult, nil
- }
- return dtmcli.MapSuccess, MustBarrierFromGin(c).Call(txGet(), func(tx *sql.Tx) error {
- return adjustTrading(tx, transInUID, req.Amount)
- })
-}
-
-func tccBarrierTransInConfirm(c *gin.Context) (interface{}, error) {
- return dtmcli.MapSuccess, MustBarrierFromGin(c).Call(txGet(), func(tx *sql.Tx) error {
- return adjustBalance(tx, transInUID, reqFrom(c).Amount)
- })
-}
-
-func tccBarrierTransInCancel(c *gin.Context) (interface{}, error) {
- return dtmcli.MapSuccess, MustBarrierFromGin(c).Call(txGet(), func(tx *sql.Tx) error {
- return adjustTrading(tx, transInUID, -reqFrom(c).Amount)
- })
-}
-
-func tccBarrierTransOutTry(c *gin.Context) (interface{}, error) {
- req := reqFrom(c)
- if req.TransOutResult != "" {
- return req.TransOutResult, nil
- }
- return dtmcli.MapSuccess, MustBarrierFromGin(c).Call(txGet(), func(tx *sql.Tx) error {
- return adjustTrading(tx, transOutUID, -req.Amount)
- })
-}
-
-func tccBarrierTransOutConfirm(c *gin.Context) (interface{}, error) {
- return dtmcli.MapSuccess, MustBarrierFromGin(c).Call(txGet(), func(tx *sql.Tx) error {
- return adjustBalance(tx, transOutUID, -reqFrom(c).Amount)
- })
-}
-
-// TccBarrierTransOutCancel will be use in test
-func TccBarrierTransOutCancel(c *gin.Context) (interface{}, error) {
- return dtmcli.MapSuccess, MustBarrierFromGin(c).Call(txGet(), func(tx *sql.Tx) error {
- return adjustTrading(tx, transOutUID, reqFrom(c).Amount)
- })
-}
diff --git a/examples/http_xa.go b/examples/http_xa.go
deleted file mode 100644
index 5ed0159..0000000
--- a/examples/http_xa.go
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package examples
-
-import (
- "github.com/gin-gonic/gin"
- "github.com/go-resty/resty/v2"
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
-)
-
-// XaClient XA client connection
-var XaClient *dtmcli.XaClient = nil
-
-func init() {
- setupFuncs["XaSetup"] = func(app *gin.Engine) {
- var err error
- XaClient, err = dtmcli.NewXaClient(DtmHttpServer, config.ExamplesDB, Busi+"/xa", func(path string, xa *dtmcli.XaClient) {
- app.POST(path, common.WrapHandler(func(c *gin.Context) (interface{}, error) {
- return xa.HandleCallback(c.Query("gid"), c.Query("branch_id"), c.Query("op"))
- }))
- })
- dtmimp.FatalIfError(err)
- }
- addSample("xa", func() string {
- gid := dtmcli.MustGenGid(DtmHttpServer)
- err := XaClient.XaGlobalTransaction(gid, func(xa *dtmcli.Xa) (*resty.Response, error) {
- resp, err := xa.CallBranch(&TransReq{Amount: 30}, Busi+"/TransOutXa")
- if err != nil {
- return resp, err
- }
- return xa.CallBranch(&TransReq{Amount: 30}, Busi+"/TransInXa")
- })
- dtmimp.FatalIfError(err)
- return gid
- })
-}
diff --git a/go.mod b/go.mod
index 5982ea1..baf57ea 100644
--- a/go.mod
+++ b/go.mod
@@ -1,24 +1,25 @@
-module github.com/yedf/dtm
+module github.com/dtm-labs/dtm
-go 1.15
+go 1.16
require (
- github.com/gin-gonic/gin v1.6.3
- github.com/go-playground/assert/v2 v2.0.1
+ bou.ke/monkey v1.0.2
+ github.com/dtm-labs/dtmdriver v0.0.1
+ github.com/dtm-labs/dtmdriver-gozero v0.0.1
+ github.com/dtm-labs/dtmdriver-polaris v0.0.2
+ github.com/dtm-labs/dtmdriver-protocol1 v0.0.1
+ github.com/gin-gonic/gin v1.7.0
github.com/go-redis/redis/v8 v8.11.4
github.com/go-resty/resty/v2 v2.7.0
github.com/go-sql-driver/mysql v1.6.0
- github.com/google/uuid v1.3.0
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/lib/pq v1.10.3
+ github.com/lithammer/shortuuid v2.0.3+incompatible
+ github.com/lithammer/shortuuid/v3 v3.0.7
+ github.com/natefinch/lumberjack v2.0.0+incompatible // indirect
github.com/onsi/gomega v1.16.0
- github.com/polarismesh/grpc-go-polaris v0.0.0-20211128162137-1a59cd7b5733 // indirect
github.com/prometheus/client_golang v1.11.0
github.com/stretchr/testify v1.7.0
- github.com/ychensha/dtmdriver-polaris v0.0.1
- github.com/yedf/dtmdriver v0.0.0-20211203060147-29426c663b6e
- github.com/yedf/dtmdriver-gozero v0.0.0-20211204083751-a14485949435
- github.com/yedf/dtmdriver-protocol1 v0.0.0-20211205112411-d7a7052dc90e
go.etcd.io/bbolt v1.3.6
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/automaxprocs v1.4.1-0.20210525221652-0180b04c18a7
diff --git a/go.sum b/go.sum
index 6d8821e..297f49a 100644
--- a/go.sum
+++ b/go.sum
@@ -1,3 +1,5 @@
+bou.ke/monkey v1.0.2 h1:kWcnsrCNUatbxncxR/ThdYqbytgOIArtYWqcQLQzKLI=
+bou.ke/monkey v1.0.2/go.mod h1:OqickVX3tNx6t33n1xvtTtu85YN5s6cKwVug+oHMaIA=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
@@ -29,8 +31,9 @@ github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
-github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=
+github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/ClickHouse/clickhouse-go v1.5.1/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
@@ -92,6 +95,14 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
+github.com/dtm-labs/dtmdriver v0.0.1 h1:dHUZQ6g2ZN6eRUqds9kKq/3K7u9bcUGatUlbthD92fA=
+github.com/dtm-labs/dtmdriver v0.0.1/go.mod h1:fLiEeD2BPwM9Yq96TfcP9KpbTwFsn5nTxa/PP0jmFuk=
+github.com/dtm-labs/dtmdriver-gozero v0.0.1 h1:ExDhMsn3MJCd+psSmIJVDgGUqbFrQGP0IpKL8x900AE=
+github.com/dtm-labs/dtmdriver-gozero v0.0.1/go.mod h1:uIiAMkG/Vp4jvINk5XfVMT0mSCzRqIYyXgpmqAQfqbA=
+github.com/dtm-labs/dtmdriver-polaris v0.0.2 h1:bh8u7bLWhairwpiA688dZMAX/OWcRoQk7a5bVcGzsjo=
+github.com/dtm-labs/dtmdriver-polaris v0.0.2/go.mod h1:vyXDTRj3CpROiRnI0dqM/lHFfZaKY9JAS1MSey2azIQ=
+github.com/dtm-labs/dtmdriver-protocol1 v0.0.1 h1:nwGpGWi7XlUAcDhEw1qZ3TheBskqCvfE96n1uVjmDf4=
+github.com/dtm-labs/dtmdriver-protocol1 v0.0.1/go.mod h1:x3bRe8x7pAfHIIQBTK+LibVq96gBSzMzsgRYe85zxAc=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
@@ -116,8 +127,8 @@ github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
-github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
-github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
+github.com/gin-gonic/gin v1.7.0 h1:jGB9xAJQ12AIGNB4HguylppmDK1Am9ppF7XnGXXJuoU=
+github.com/gin-gonic/gin v1.7.0/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -144,8 +155,8 @@ github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8c
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
-github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
-github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
+github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
+github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis/v8 v8.11.4 h1:kHoYkfZP6+pe04aFTnhDH6GDROa5yJdHJVNxV3F46Tg=
@@ -225,6 +236,7 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@@ -353,6 +365,10 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg=
github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/lithammer/shortuuid v2.0.3+incompatible h1:ao1r3cQ9AUX+c6dZXwbCM/ELGf10EoO4SyqqxBXTyHc=
+github.com/lithammer/shortuuid v2.0.3+incompatible/go.mod h1:FR74pbAuElzOUuenUHTK2Tciko1/vKuIKS9dSkDrA4w=
+github.com/lithammer/shortuuid/v3 v3.0.7 h1:trX0KTHy4Pbwo/6ia8fscyHoGA+mf1jWbPJVuvyJQQ8=
+github.com/lithammer/shortuuid/v3 v3.0.7/go.mod h1:vMk8ke37EmiewwolSO1NLW8vP4ZaKlRuDIi8tWWmAts=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -451,6 +467,7 @@ github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OK
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
@@ -493,14 +510,6 @@ github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/X
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
-github.com/ychensha/dtmdriver-polaris v0.0.1 h1:+gYWFma5pflxNyV1HkcXmhzVHpijMSW9G4qTDcs63xc=
-github.com/ychensha/dtmdriver-polaris v0.0.1/go.mod h1:0BdQvxXlGOlF6YVlsDoVvu8jyxdTlJZ9Kyh5t9lRA94=
-github.com/yedf/dtmdriver v0.0.0-20211203060147-29426c663b6e h1:xXyb4mr46T5KqR5SXd37ivXNJ33k/MSAAwsGP6btmh8=
-github.com/yedf/dtmdriver v0.0.0-20211203060147-29426c663b6e/go.mod h1:aeo6ZWiVI0x8P8O18r6uB1cG2uw9BCQyYZaH15MlRDI=
-github.com/yedf/dtmdriver-gozero v0.0.0-20211204083751-a14485949435 h1:qLBNU16bK1ZeaZ6tmfarf49qx9O9sUJAe7/IzoXpuEg=
-github.com/yedf/dtmdriver-gozero v0.0.0-20211204083751-a14485949435/go.mod h1:RYtA6oZny6LzlIRb1tPGt5bHfgqws/JaU6ogFly8ByQ=
-github.com/yedf/dtmdriver-protocol1 v0.0.0-20211205112411-d7a7052dc90e h1:hNSfnVdE46i45HS6aneN+6umk+12EqI3AErbbzeLsoo=
-github.com/yedf/dtmdriver-protocol1 v0.0.0-20211205112411-d7a7052dc90e/go.mod h1:kB3NPnDKSGioVjgdfj6qgbqYJinOml45GnlHqR46Ycc=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
@@ -593,7 +602,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
@@ -603,7 +611,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -765,7 +772,6 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/helper/.goreleaser.yml b/helper/.goreleaser.yml
index 39ca3ad..83e0158 100644
--- a/helper/.goreleaser.yml
+++ b/helper/.goreleaser.yml
@@ -1,15 +1,25 @@
# .goreleaser.yml
project_name: dtm
builds:
- - env: [CGO_ENABLED=0]
+ - id: dtm_amd64
+ env: [CGO_ENABLED=0]
goos:
- linux
- windows
- darwin
goarch:
- amd64
- id: 'dtm'
dir: .
- main: ./app/main.go
+ main: main.go
ldflags:
- - -s -w -X main.Version={{.Version}} -X main.Commit={{.Commit}} -X main.Date={{.Date}}
+ - -s -w -X main.Version={{.Version}}
+ - id: dtm_arm64
+ env: [ CGO_ENABLED=0 ]
+ goos:
+ - darwin
+ goarch:
+ - arm64
+ dir: .
+ main: main.go
+ ldflags:
+ - -s -w -X main.Version={{.Version}}
diff --git a/helper/Dockerfile-release b/helper/Dockerfile-release
index b922185..2b7ed79 100644
--- a/helper/Dockerfile-release
+++ b/helper/Dockerfile-release
@@ -1,15 +1,15 @@
# syntax=docker/dockerfile:1
-FROM --platform=$TARGETPLATFORM golang:1.16.6-alpine3.14 as builder
+FROM --platform=$TARGETPLATFORM golang:1.16-alpine as builder
ARG TARGETARCH
ARG TARGETOS
WORKDIR /app/dtm
# RUN go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct
EXPOSE 8080
COPY . .
-RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags="-s -w" app/main.go
+RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags="-s -w"
-FROM --platform=$TARGETPLATFORM alpine:3.14
-COPY --from=builder /app/dtm/main /app/dtm/
+FROM --platform=$TARGETPLATFORM alpine
+COPY --from=builder /app/dtm/dtm /app/dtm/
ENV IS_DOCKER=1
WORKDIR /app/dtm
-CMD ["/app/dtm/main", "dtmsvr"]
+ENTRYPOINT ["/app/dtm/dtm"]
diff --git a/helper/sync-dtmcli.sh b/helper/sync-dtmcli.sh
index 7f809d0..dd70a5a 100755
--- a/helper/sync-dtmcli.sh
+++ b/helper/sync-dtmcli.sh
@@ -14,7 +14,7 @@ fi
cd ../dtmcli
cp -rf ../dtm/dtmcli/* ./
rm -f *_test.go
-sed -i '' -e 's/yedf\/dtm\//yedf\//g' *.go
+sed -i '' -e 's/dtm-labs\/dtm\//dtm-labs\//g' *.go */**.go
go mod tidy
go build || exit 1
git add .
@@ -24,22 +24,19 @@ git tag $ver
git push --tags
cd ../dtmcli-go-sample
-go get -u github.com/yedf/dtmcli@$ver
+go get -u github.com/dtm-labs/dtmcli@$ver
go mod tidy
go build || exit 1
git add .
git commit -m"update from dtm to version $ver"
git push
-git tag $ver
-git push --tags
cd ../dtmgrpc
rm -rf *.go dtmgimp
cp -r ../dtm/dtmgrpc/* ./
-go get github.com/yedf/dtmcli@$ver
-sed -i '' -e 's/yedf\/dtm\//yedf\//g' *.go
-sed -i '' -e 's/yedf\/dtm\//yedf\//g' dtmgimp/*.go dtmgimp/*.proto
+go get github.com/dtm-labs/dtmcli@$ver
+sed -i '' -e 's/dtm-labs\/dtm\//dtm-labs\//g' *.go */**.go
rm -rf *_test.go
go mod tidy
go build || exit 1
@@ -50,8 +47,8 @@ git tag $ver
git push --tags
cd ../dtmgrpc-go-sample
-go get github.com/yedf/dtmcli@$ver
-go get github.com/yedf/dtmgrpc@$ver
+go get github.com/dtm-labs/dtmcli@$ver
+go get github.com/dtm-labs/dtmgrpc@$ver
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative busi/*.proto || exit 1
go build || exit 1
git add .
diff --git a/helper/test-cover.sh b/helper/test-cover.sh
new file mode 100644
index 0000000..13bd654
--- /dev/null
+++ b/helper/test-cover.sh
@@ -0,0 +1,13 @@
+set -x
+echo "" > coverage.txt
+for store in redis mysql boltdb; do
+ for d in $(go list ./... | grep -v vendor); do
+ TEST_STORE=$store go test -covermode count -coverprofile=profile.out -coverpkg=github.com/dtm-labs/dtm/dtmcli,github.com/dtm-labs/dtm/dtmcli/dtmimp,github.com/dtm-labs/dtm/dtmcli/logger,github.com/dtm-labs/dtm/dtmgrpc,github.com/dtm-labs/dtm/dtmgrpc/dtmgimp,github.com/dtm-labs/dtm/dtmsvr,github.com/dtm-labs/dtm/dtmsvr/config,github.com/dtm-labs/dtm/dtmsvr/storage,github.com/dtm-labs/dtm/dtmsvr/storage/boltdb,github.com/dtm-labs/dtm/dtmsvr/storage/redis,github.com/dtm-labs/dtm/dtmsvr/storage/registry,github.com/dtm-labs/dtm/dtmsvr/storage/sql,github.com/dtm-labs/dtm/dtmutil -gcflags=-l $d || exit 1
+ if [ -f profile.out ]; then
+ cat profile.out >> coverage.txt
+ echo > profile.out
+ fi
+ done
+done
+
+curl -s https://codecov.io/bash | bash
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..4c6263f
--- /dev/null
+++ b/main.go
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2021 yedf. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "go.uber.org/automaxprocs/maxprocs"
+
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/dtmsvr"
+ "github.com/dtm-labs/dtm/dtmsvr/config"
+ "github.com/dtm-labs/dtm/dtmsvr/storage/registry"
+ "github.com/natefinch/lumberjack"
+
+ // load the microserver driver
+ _ "github.com/dtm-labs/dtmdriver-gozero"
+ _ "github.com/dtm-labs/dtmdriver-polaris"
+ _ "github.com/dtm-labs/dtmdriver-protocol1"
+)
+
+// Version declares version info
+var Version string
+
+func version() {
+ if Version == "" {
+ Version = "0.0.0-dev"
+ }
+ fmt.Printf("dtm version: %s\n", Version)
+}
+
+func usage() {
+ cmd := filepath.Base(os.Args[0])
+ s := "Usage: %s [options]\n\n"
+ fmt.Fprintf(os.Stderr, s, cmd)
+ flag.PrintDefaults()
+}
+
+var isVersion = flag.Bool("v", false, "Show the version of dtm.")
+var isDebug = flag.Bool("d", false, "Set log level to debug.")
+var isHelp = flag.Bool("h", false, "Show the help information about etcd.")
+var isReset = flag.Bool("r", false, "Reset dtm server data.")
+var confFile = flag.String("c", "", "Path to the server configuration file.")
+
+func main() {
+ flag.Parse()
+ if flag.NArg() > 0 || *isHelp {
+ usage()
+ return
+ } else if *isVersion {
+ version()
+ return
+ }
+ config.MustLoadConfig(*confFile)
+ conf := &config.Config
+ if *isDebug {
+ conf.Log.Level = "debug"
+ }
+ if conf.Log.Output == "file" {
+ ll := lumberjack.Logger{
+ Filename: conf.Log.FileName,
+ MaxSize: int(conf.Log.FileMaxSize),
+ MaxBackups: int(conf.Log.FileMaxBackups),
+ MaxAge: int(conf.Log.FileMaxAge),
+ Compress: conf.Log.FileCompress != 0,
+ }
+ logger.InitRotateLog(conf.Log.Level, &ll)
+ } else {
+ logger.InitLog(conf.Log.Level)
+ }
+
+ if *isReset {
+ dtmsvr.PopulateDB(false)
+ }
+ _, _ = maxprocs.Set(maxprocs.Logger(logger.Infof))
+ registry.WaitStoreUp()
+ dtmsvr.StartSvr() // 启动dtmsvr的api服务
+ go dtmsvr.CronExpiredTrans(-1) // 启动dtmsvr的定时过期查询
+ select {}
+}
diff --git a/qs/main.go b/qs/main.go
new file mode 100644
index 0000000..4cf0586
--- /dev/null
+++ b/qs/main.go
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2021 yedf. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+package main
+
+import (
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/test/busi"
+)
+
+func main() {
+ busi.QsStartSvr()
+ gid := busi.QsFireRequest()
+ logger.Infof("transaction: %s succeed", gid)
+}
diff --git a/sqls/examples.mysql.sql b/sqls/busi.mysql.sql
similarity index 100%
rename from sqls/examples.mysql.sql
rename to sqls/busi.mysql.sql
diff --git a/sqls/examples.postgres.sql b/sqls/busi.postgres.sql
similarity index 100%
rename from sqls/examples.postgres.sql
rename to sqls/busi.postgres.sql
diff --git a/sqls/dtmsvr.storage.mysql.sql b/sqls/dtmsvr.storage.mysql.sql
index 59d61dd..09c82f5 100644
--- a/sqls/dtmsvr.storage.mysql.sql
+++ b/sqls/dtmsvr.storage.mysql.sql
@@ -12,14 +12,14 @@ CREATE TABLE if not EXISTS dtm.trans_global (
`protocol` varchar(45) not null comment '通信协议 http | grpc',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
- `commit_time` datetime DEFAULT NULL,
`finish_time` datetime DEFAULT NULL,
`rollback_time` datetime DEFAULT NULL,
- `options` varchar(256) DEFAULT '',
+ `options` varchar(1024) DEFAULT '',
`custom_data` varchar(256) DEFAULT '',
`next_cron_interval` int(11) default null comment '下次定时处理的间隔',
`next_cron_time` datetime default null comment '下次定时处理的时间',
`owner` varchar(128) not null default '' comment '正在处理全局事务的锁定者',
+ `ext_data` TEXT comment 'global扩展字段的数据',
PRIMARY KEY (`id`),
UNIQUE KEY `gid` (`gid`),
key `owner`(`owner`),
diff --git a/sqls/dtmsvr.storage.postgres.sql b/sqls/dtmsvr.storage.postgres.sql
index bccb8be..f19d25e 100644
--- a/sqls/dtmsvr.storage.postgres.sql
+++ b/sqls/dtmsvr.storage.postgres.sql
@@ -13,14 +13,14 @@ CREATE TABLE if not EXISTS dtm.trans_global (
protocol varchar(45) not null,
create_time timestamp(0) with time zone DEFAULT NULL,
update_time timestamp(0) with time zone DEFAULT NULL,
- commit_time timestamp(0) with time zone DEFAULT NULL,
finish_time timestamp(0) with time zone DEFAULT NULL,
rollback_time timestamp(0) with time zone DEFAULT NULL,
- options varchar(256) DEFAULT '',
+ options varchar(1024) DEFAULT '',
custom_data varchar(256) DEFAULT '',
next_cron_interval int default null,
next_cron_time timestamp(0) with time zone default null,
owner varchar(128) not null default '',
+ ext_data text,
PRIMARY KEY (id),
CONSTRAINT gid UNIQUE (gid)
);
diff --git a/sqls/dtmsvr.storage.tdsql.sql b/sqls/dtmsvr.storage.tdsql.sql
new file mode 100644
index 0000000..09cf816
--- /dev/null
+++ b/sqls/dtmsvr.storage.tdsql.sql
@@ -0,0 +1,46 @@
+CREATE DATABASE IF NOT EXISTS dtm
+/*!40100 DEFAULT CHARACTER SET utf8mb4 */
+;
+drop table IF EXISTS dtm.trans_global;
+CREATE TABLE if not EXISTS dtm.trans_global (
+ `id` bigint(22) NOT NULL AUTO_INCREMENT,
+ `gid` varchar(128) NOT NULL COMMENT '事务全局id',
+ `trans_type` varchar(45) not null COMMENT '事务类型: saga | xa | tcc | msg',
+ -- `data` TEXT COMMENT '事务携带的数据', -- 影响性能,不必要存储
+ `status` varchar(12) NOT NULL COMMENT '全局事务的状态 prepared | submitted | aborting | finished | rollbacked',
+ `query_prepared` varchar(128) NOT NULL COMMENT 'prepared状态事务的查询api',
+ `protocol` varchar(45) not null comment '通信协议 http | grpc',
+ `create_time` datetime DEFAULT NULL,
+ `update_time` datetime DEFAULT NULL,
+ `commit_time` datetime DEFAULT NULL,
+ `finish_time` datetime DEFAULT NULL,
+ `rollback_time` datetime DEFAULT NULL,
+ `options` varchar(256) DEFAULT '',
+ `custom_data` varchar(256) DEFAULT '',
+ `next_cron_interval` int(11) default null comment '下次定时处理的间隔',
+ `next_cron_time` datetime default null comment '下次定时处理的时间',
+ `owner` varchar(128) not null default '' comment '正在处理全局事务的锁定者',
+ PRIMARY KEY (`id`,`gid`),
+ UNIQUE KEY `id` (`id`,`gid`),
+ UNIQUE KEY `gid` (`gid`),
+ key `owner`(`owner`),
+ key `status_next_cron_time` (`status`, `next_cron_time`) comment '这个索引用于查询超时的全局事务,能够合理的走索引'
+) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 shardkey=gid;
+drop table IF EXISTS dtm.trans_branch_op;
+CREATE TABLE IF NOT EXISTS dtm.trans_branch_op (
+ `id` bigint(22) NOT NULL AUTO_INCREMENT,
+ `gid` varchar(128) NOT NULL COMMENT '事务全局id',
+ `url` varchar(128) NOT NULL COMMENT '动作关联的url',
+ `data` TEXT COMMENT '请求所携带的数据',
+ `bin_data` BLOB COMMENT 'grpc的二进制数据',
+ `branch_id` VARCHAR(128) NOT NULL COMMENT '事务分支ID',
+ `op` varchar(45) NOT NULL COMMENT '事务分支类型 saga_action | saga_compensate | xa',
+ `status` varchar(45) NOT NULL COMMENT '步骤的状态 submitted | finished | rollbacked',
+ `finish_time` datetime DEFAULT NULL,
+ `rollback_time` datetime DEFAULT NULL,
+ `create_time` datetime DEFAULT NULL,
+ `update_time` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`,`gid`),
+ UNIQUE KEY `id` (`id`,`gid`),
+ UNIQUE KEY `gid_uniq` (`gid`, `branch_id`, `op`)
+) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 shardkey=gid;
diff --git a/test/api_test.go b/test/api_test.go
index e44ed14..c864101 100644
--- a/test/api_test.go
+++ b/test/api_test.go
@@ -10,9 +10,9 @@ import (
"fmt"
"testing"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmutil"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/examples"
)
func TestAPIQuery(t *testing.T) {
@@ -20,7 +20,7 @@ func TestAPIQuery(t *testing.T) {
err := genMsg(gid).Submit()
assert.Nil(t, err)
waitTransProcessed(gid)
- resp, err := dtmimp.RestyClient.R().SetQueryParam("gid", gid).Get(examples.DtmHttpServer + "/query")
+ resp, err := dtmimp.RestyClient.R().SetQueryParam("gid", gid).Get(dtmutil.DefaultHTTPServer + "/query")
assert.Nil(t, err)
m := map[string]interface{}{}
assert.Equal(t, resp.StatusCode(), 200)
@@ -28,11 +28,11 @@ func TestAPIQuery(t *testing.T) {
assert.NotEqual(t, nil, m["transaction"])
assert.Equal(t, 2, len(m["branches"].([]interface{})))
- resp, err = dtmimp.RestyClient.R().SetQueryParam("gid", "").Get(examples.DtmHttpServer + "/query")
+ resp, err = dtmimp.RestyClient.R().SetQueryParam("gid", "").Get(dtmutil.DefaultHTTPServer + "/query")
e2p(err)
assert.Equal(t, resp.StatusCode(), 500)
- resp, err = dtmimp.RestyClient.R().SetQueryParam("gid", "1").Get(examples.DtmHttpServer + "/query")
+ resp, err = dtmimp.RestyClient.R().SetQueryParam("gid", "1").Get(dtmutil.DefaultHTTPServer + "/query")
e2p(err)
assert.Equal(t, resp.StatusCode(), 200)
dtmimp.MustUnmarshalString(resp.String(), &m)
@@ -47,7 +47,7 @@ func TestAPIAll(t *testing.T) {
assert.Nil(t, err)
waitTransProcessed(gid)
}
- resp, err := dtmimp.RestyClient.R().SetQueryParam("limit", "1").Get(examples.DtmHttpServer + "/all")
+ resp, err := dtmimp.RestyClient.R().SetQueryParam("limit", "1").Get(dtmutil.DefaultHTTPServer + "/all")
assert.Nil(t, err)
m := map[string]interface{}{}
dtmimp.MustUnmarshalString(resp.String(), &m)
@@ -57,7 +57,7 @@ func TestAPIAll(t *testing.T) {
resp, err = dtmimp.RestyClient.R().SetQueryParams(map[string]string{
"limit": "1",
"position": nextPos,
- }).Get(examples.DtmHttpServer + "/all")
+ }).Get(dtmutil.DefaultHTTPServer + "/all")
assert.Nil(t, err)
dtmimp.MustUnmarshalString(resp.String(), &m)
nextPos2 := m["next_position"].(string)
@@ -67,9 +67,15 @@ func TestAPIAll(t *testing.T) {
resp, err = dtmimp.RestyClient.R().SetQueryParams(map[string]string{
"limit": "1000",
"position": nextPos,
- }).Get(examples.DtmHttpServer + "/all")
+ }).Get(dtmutil.DefaultHTTPServer + "/all")
assert.Nil(t, err)
dtmimp.MustUnmarshalString(resp.String(), &m)
nextPos3 := m["next_position"].(string)
assert.Equal(t, "", nextPos3)
}
+
+func TestDtmMetrics(t *testing.T) {
+ rest, err := dtmimp.RestyClient.R().Get("http://localhost:36789/api/metrics")
+ assert.Nil(t, err)
+ assert.Equal(t, rest.StatusCode(), 200)
+}
diff --git a/test/base_test.go b/test/base_test.go
index 7dbc192..b33bfd8 100644
--- a/test/base_test.go
+++ b/test/base_test.go
@@ -11,16 +11,17 @@ import (
"fmt"
"testing"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/dtmutil"
+ "github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/examples"
)
// BarrierModel barrier model for gorm
type BarrierModel struct {
- common.ModelBase
+ dtmutil.ModelBase
dtmcli.BranchBarrier
}
@@ -29,7 +30,7 @@ func (BarrierModel) TableName() string { return "dtm_barrier.barrier" }
func TestBaseSqlDB(t *testing.T) {
asserts := assert.New(t)
- db := common.DbGet(config.ExamplesDB)
+ db := dtmutil.DbGet(busi.BusiConf)
barrier := &dtmcli.BranchBarrier{
TransType: "saga",
Gid: "gid2",
@@ -40,7 +41,7 @@ func TestBaseSqlDB(t *testing.T) {
tx, err := db.ToSQLDB().Begin()
asserts.Nil(err)
err = barrier.Call(tx, func(tx *sql.Tx) error {
- dtmimp.Logf("rollback gid2")
+ logger.Debugf("rollback gid2")
return fmt.Errorf("gid2 error")
})
asserts.Error(err, fmt.Errorf("gid2 error"))
@@ -50,7 +51,7 @@ func TestBaseSqlDB(t *testing.T) {
asserts.Equal(dbr.RowsAffected, int64(0))
barrier.BarrierID = 0
err = barrier.CallWithDB(db.ToSQLDB(), func(tx *sql.Tx) error {
- dtmimp.Logf("submit gid2")
+ logger.Debugf("submit gid2")
return nil
})
asserts.Nil(err)
@@ -59,10 +60,10 @@ func TestBaseSqlDB(t *testing.T) {
}
func TestBaseHttp(t *testing.T) {
- resp, err := dtmimp.RestyClient.R().SetQueryParam("panic_string", "1").Post(examples.Busi + "/TestPanic")
+ resp, err := dtmimp.RestyClient.R().SetQueryParam("panic_string", "1").Post(busi.Busi + "/TestPanic")
assert.Nil(t, err)
assert.Contains(t, resp.String(), "panic_string")
- resp, err = dtmimp.RestyClient.R().SetQueryParam("panic_error", "1").Post(examples.Busi + "/TestPanic")
+ resp, err = dtmimp.RestyClient.R().SetQueryParam("panic_error", "1").Post(busi.Busi + "/TestPanic")
assert.Nil(t, err)
assert.Contains(t, resp.String(), "panic_error")
}
diff --git a/test/busi/barrier.go b/test/busi/barrier.go
new file mode 100644
index 0000000..9d8c30f
--- /dev/null
+++ b/test/busi/barrier.go
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2021 yedf. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+package busi
+
+import (
+ "context"
+ "database/sql"
+
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmutil"
+ "github.com/gin-gonic/gin"
+ emptypb "google.golang.org/protobuf/types/known/emptypb"
+)
+
+func init() {
+ setupFuncs["BarrierSetup"] = func(app *gin.Engine) {
+ app.POST(BusiAPI+"/SagaBTransIn", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ barrier := MustBarrierFromGin(c)
+ return barrier.Call(txGet(), func(tx *sql.Tx) error {
+ return SagaAdjustBalance(tx, TransInUID, reqFrom(c).Amount, reqFrom(c).TransInResult)
+ })
+ }))
+ app.POST(BusiAPI+"/SagaBTransInCompensate", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ barrier := MustBarrierFromGin(c)
+ return barrier.Call(txGet(), func(tx *sql.Tx) error {
+ return SagaAdjustBalance(tx, TransInUID, -reqFrom(c).Amount, "")
+ })
+ }))
+ app.POST(BusiAPI+"/SagaBTransOut", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ barrier := MustBarrierFromGin(c)
+ return barrier.Call(txGet(), func(tx *sql.Tx) error {
+ return SagaAdjustBalance(tx, TransOutUID, -reqFrom(c).Amount, reqFrom(c).TransOutResult)
+ })
+ }))
+ app.POST(BusiAPI+"/SagaBTransOutCompensate", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ barrier := MustBarrierFromGin(c)
+ return barrier.Call(txGet(), func(tx *sql.Tx) error {
+ return SagaAdjustBalance(tx, TransOutUID, reqFrom(c).Amount, "")
+ })
+ }))
+ app.POST(BusiAPI+"/SagaBTransOutGorm", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ req := reqFrom(c)
+ barrier := MustBarrierFromGin(c)
+ tx := dbGet().DB.Begin()
+ return barrier.Call(tx.Statement.ConnPool.(*sql.Tx), func(tx1 *sql.Tx) error {
+ return tx.Exec("update dtm_busi.user_account set balance = balance + ? where user_id = ?", -req.Amount, TransOutUID).Error
+ })
+ }))
+
+ app.POST(BusiAPI+"/TccBTransInTry", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ req := reqFrom(c)
+ if req.TransInResult != "" {
+ return dtmcli.String2DtmError(req.TransInResult)
+ }
+ return MustBarrierFromGin(c).Call(txGet(), func(tx *sql.Tx) error {
+ return tccAdjustTrading(tx, TransInUID, req.Amount)
+ })
+ }))
+ app.POST(BusiAPI+"/TccBTransInConfirm", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ return MustBarrierFromGin(c).Call(txGet(), func(tx *sql.Tx) error {
+ return tccAdjustBalance(tx, TransInUID, reqFrom(c).Amount)
+ })
+ }))
+ app.POST(BusiAPI+"/TccBTransInCancel", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ return MustBarrierFromGin(c).Call(txGet(), func(tx *sql.Tx) error {
+ return tccAdjustTrading(tx, TransInUID, -reqFrom(c).Amount)
+ })
+ }))
+ app.POST(BusiAPI+"/SagaRedisTransIn", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ return MustBarrierFromGin(c).RedisCheckAdjustAmount(RedisGet(), getRedisAccountKey(TransInUID), reqFrom(c).Amount, 7*86400)
+ }))
+ app.POST(BusiAPI+"/SagaRedisTransInCom", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ return MustBarrierFromGin(c).RedisCheckAdjustAmount(RedisGet(), getRedisAccountKey(TransInUID), -reqFrom(c).Amount, 7*86400)
+ }))
+ app.POST(BusiAPI+"/SagaRedisTransOut", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ return MustBarrierFromGin(c).RedisCheckAdjustAmount(RedisGet(), getRedisAccountKey(TransOutUID), -reqFrom(c).Amount, 7*86400)
+ }))
+ app.POST(BusiAPI+"/SagaRedisTransOutCom", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ return MustBarrierFromGin(c).RedisCheckAdjustAmount(RedisGet(), getRedisAccountKey(TransOutUID), reqFrom(c).Amount, 7*86400)
+ }))
+ app.POST(BusiAPI+"/TccBTransOutTry", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ req := reqFrom(c)
+ if req.TransOutResult != "" {
+ return dtmcli.String2DtmError(req.TransOutResult)
+ }
+ if req.Store == "redis" {
+ return MustBarrierFromGin(c).RedisCheckAdjustAmount(RedisGet(), getRedisAccountKey(TransOutUID), req.Amount, 7*86400)
+ }
+
+ return MustBarrierFromGin(c).Call(txGet(), func(tx *sql.Tx) error {
+ return tccAdjustTrading(tx, TransOutUID, -req.Amount)
+ })
+ }))
+ app.POST(BusiAPI+"/TccBTransOutConfirm", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ if reqFrom(c).Store == "redis" {
+ return nil
+ }
+ return MustBarrierFromGin(c).Call(txGet(), func(tx *sql.Tx) error {
+ return tccAdjustBalance(tx, TransOutUID, -reqFrom(c).Amount)
+ })
+ }))
+ app.POST(BusiAPI+"/TccBTransOutCancel", dtmutil.WrapHandler2(TccBarrierTransOutCancel))
+ }
+}
+
+// TccBarrierTransOutCancel will be use in test
+func TccBarrierTransOutCancel(c *gin.Context) interface{} {
+ req := reqFrom(c)
+ if req.Store == "redis" {
+ return MustBarrierFromGin(c).RedisCheckAdjustAmount(RedisGet(), getRedisAccountKey(TransOutUID), -req.Amount, 7*86400)
+ }
+ return MustBarrierFromGin(c).Call(txGet(), func(tx *sql.Tx) error {
+ return tccAdjustTrading(tx, TransOutUID, reqFrom(c).Amount)
+ })
+}
+
+func (s *busiServer) TransInBSaga(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) {
+ barrier := MustBarrierFromGrpc(ctx)
+ return &emptypb.Empty{}, barrier.Call(txGet(), func(tx *sql.Tx) error {
+ return sagaGrpcAdjustBalance(tx, TransInUID, in.Amount, in.TransInResult)
+ })
+}
+
+func (s *busiServer) TransOutBSaga(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) {
+ barrier := MustBarrierFromGrpc(ctx)
+ return &emptypb.Empty{}, barrier.Call(txGet(), func(tx *sql.Tx) error {
+ return sagaGrpcAdjustBalance(tx, TransOutUID, -in.Amount, in.TransOutResult)
+ })
+}
+
+func (s *busiServer) TransInRevertBSaga(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) {
+ barrier := MustBarrierFromGrpc(ctx)
+ return &emptypb.Empty{}, barrier.Call(txGet(), func(tx *sql.Tx) error {
+ return sagaGrpcAdjustBalance(tx, TransInUID, -in.Amount, "")
+ })
+}
+
+func (s *busiServer) TransOutRevertBSaga(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) {
+ barrier := MustBarrierFromGrpc(ctx)
+ return &emptypb.Empty{}, barrier.Call(txGet(), func(tx *sql.Tx) error {
+ return sagaGrpcAdjustBalance(tx, TransOutUID, in.Amount, "")
+ })
+}
+
+func (s *busiServer) QueryPreparedB(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) {
+ barrier := MustBarrierFromGrpc(ctx)
+ return &emptypb.Empty{}, barrier.QueryPrepared(dbGet().ToSQLDB())
+}
diff --git a/examples/base_grpc.go b/test/busi/base_grpc.go
similarity index 59%
rename from examples/base_grpc.go
rename to test/busi/base_grpc.go
index d0002b4..63099e1 100644
--- a/examples/base_grpc.go
+++ b/test/busi/base_grpc.go
@@ -4,24 +4,25 @@
* license that can be found in the LICENSE file.
*/
-package examples
+package busi
import (
"context"
"database/sql"
+ "errors"
"fmt"
"net"
- "time"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/dtmgrpc"
+ "github.com/dtm-labs/dtm/dtmutil"
"github.com/gin-gonic/gin"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmgrpc"
- "github.com/yedf/dtm/dtmgrpc/dtmgimp"
+ "github.com/dtm-labs/dtm/dtmgrpc/dtmgimp"
+ "github.com/dtm-labs/dtm/dtmgrpc/dtmgpb"
grpc "google.golang.org/grpc"
- codes "google.golang.org/grpc/codes"
- status "google.golang.org/grpc/status"
emptypb "google.golang.org/protobuf/types/known/emptypb"
)
@@ -29,57 +30,45 @@ import (
var BusiGrpc string = fmt.Sprintf("localhost:%d", BusiGrpcPort)
// DtmClient grpc client for dtm
-var DtmClient dtmgimp.DtmClient = nil
+var DtmClient dtmgpb.DtmClient = nil
// XaGrpcClient XA client connection
var XaGrpcClient *dtmgrpc.XaGrpcClient = nil
func init() {
setupFuncs["XaGrpcSetup"] = func(app *gin.Engine) {
- XaGrpcClient = dtmgrpc.NewXaGrpcClient(DtmGrpcServer, config.ExamplesDB, BusiGrpc+"/examples.Busi/XaNotify")
+ XaGrpcClient = dtmgrpc.NewXaGrpcClient(dtmutil.DefaultGrpcServer, BusiConf, BusiGrpc+"/busi.Busi/XaNotify")
}
}
// GrpcStartup for grpc
func GrpcStartup() {
- conn, err := grpc.Dial(DtmGrpcServer, grpc.WithInsecure(), grpc.WithUnaryInterceptor(dtmgimp.GrpcClientLog))
- dtmimp.FatalIfError(err)
- DtmClient = dtmgimp.NewDtmClient(conn)
- dtmimp.Logf("dtm client inited")
+ conn, err := grpc.Dial(dtmutil.DefaultGrpcServer, grpc.WithInsecure(), grpc.WithUnaryInterceptor(dtmgimp.GrpcClientLog))
+ logger.FatalIfError(err)
+ DtmClient = dtmgpb.NewDtmClient(conn)
+ logger.Debugf("dtm client inited")
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", BusiGrpcPort))
- dtmimp.FatalIfError(err)
+ logger.FatalIfError(err)
s := grpc.NewServer(grpc.UnaryInterceptor(dtmgimp.GrpcServerLog))
RegisterBusiServer(s, &busiServer{})
go func() {
- dtmimp.Logf("busi grpc listening at %v", lis.Addr())
+ logger.Debugf("busi grpc listening at %v", lis.Addr())
err := s.Serve(lis)
- dtmimp.FatalIfError(err)
+ logger.FatalIfError(err)
}()
- time.Sleep(100 * time.Millisecond)
-}
-
-func handleGrpcBusiness(in *BusiReq, result1 string, result2 string, busi string) error {
- res := dtmimp.OrString(result1, result2, dtmcli.ResultSuccess)
- dtmimp.Logf("grpc busi %s %v %s %s result: %s", busi, in, result1, result2, res)
- if res == dtmcli.ResultSuccess {
- return nil
- } else if res == dtmcli.ResultFailure {
- return status.New(codes.Aborted, dtmcli.ResultFailure).Err()
- } else if res == dtmcli.ResultOngoing {
- return status.New(codes.Aborted, dtmcli.ResultOngoing).Err()
- }
- return status.New(codes.Internal, fmt.Sprintf("unknow result %s", res)).Err()
}
-// busiServer is used to implement examples.BusiServer.
+// busiServer is used to implement busi.BusiServer.
type busiServer struct {
UnimplementedBusiServer
}
-func (s *busiServer) CanSubmit(ctx context.Context, in *BusiReq) (*BusiReply, error) {
- res := MainSwitch.CanSubmitResult.Fetch()
- return &BusiReply{Message: "a sample"}, dtmgimp.Result2Error(res, nil)
+func (s *busiServer) QueryPrepared(ctx context.Context, in *BusiReq) (*BusiReply, error) {
+ res := MainSwitch.QueryPreparedResult.Fetch()
+ err := dtmcli.String2DtmError(res)
+
+ return &BusiReply{Message: "a sample data"}, dtmgrpc.DtmError2GrpcError(err)
}
func (s *busiServer) TransIn(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) {
@@ -116,29 +105,41 @@ func (s *busiServer) TransOutTcc(ctx context.Context, in *BusiReq) (*emptypb.Emp
func (s *busiServer) TransInXa(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) {
return &emptypb.Empty{}, XaGrpcClient.XaLocalTransaction(ctx, in, func(db *sql.DB, xa *dtmgrpc.XaGrpc) error {
- if in.TransInResult == dtmcli.ResultFailure {
- return status.New(codes.Aborted, dtmcli.ResultFailure).Err()
- }
- _, err := dtmimp.DBExec(db, "update dtm_busi.user_account set balance=balance+? where user_id=?", in.Amount, 2)
- return err
+ return sagaGrpcAdjustBalance(db, TransInUID, in.Amount, in.TransInResult)
})
}
func (s *busiServer) TransOutXa(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) {
return &emptypb.Empty{}, XaGrpcClient.XaLocalTransaction(ctx, in, func(db *sql.DB, xa *dtmgrpc.XaGrpc) error {
- if in.TransOutResult == dtmcli.ResultFailure {
- return status.New(codes.Aborted, dtmcli.ResultFailure).Err()
- }
- _, err := dtmimp.DBExec(db, "update dtm_busi.user_account set balance=balance-? where user_id=?", in.Amount, 1)
- return err
+ return sagaGrpcAdjustBalance(db, TransOutUID, in.Amount, in.TransOutResult)
})
}
func (s *busiServer) TransInTccNested(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) {
tcc, err := dtmgrpc.TccFromGrpc(ctx)
- dtmimp.FatalIfError(err)
+ logger.FatalIfError(err)
r := &emptypb.Empty{}
- err = tcc.CallBranch(in, BusiGrpc+"/examples.Busi/TransIn", BusiGrpc+"/examples.Busi/TransInConfirm", BusiGrpc+"/examples.Busi/TransInRevert", r)
- dtmimp.FatalIfError(err)
+ err = tcc.CallBranch(in, BusiGrpc+"/busi.Busi/TransIn", BusiGrpc+"/busi.Busi/TransInConfirm", BusiGrpc+"/busi.Busi/TransInRevert", r)
+ logger.FatalIfError(err)
return r, handleGrpcBusiness(in, MainSwitch.TransInResult.Fetch(), in.TransInResult, dtmimp.GetFuncName())
}
+
+func (s *busiServer) XaNotify(ctx context.Context, in *emptypb.Empty) (*emptypb.Empty, error) {
+ return XaGrpcClient.HandleCallback(ctx)
+}
+
+func (s *busiServer) TransOutHeaderYes(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) {
+ meta := dtmgimp.GetMetaFromContext(ctx, "test_header")
+ if meta == "" {
+ return &emptypb.Empty{}, errors.New("no header found in HeaderYes")
+ }
+ return &emptypb.Empty{}, handleGrpcBusiness(in, MainSwitch.TransOutResult.Fetch(), in.TransOutResult, dtmimp.GetFuncName())
+}
+
+func (s *busiServer) TransOutHeaderNo(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) {
+ meta := dtmgimp.GetMetaFromContext(ctx, "test_header")
+ if meta != "" {
+ return &emptypb.Empty{}, errors.New("header found in HeaderNo")
+ }
+ return &emptypb.Empty{}, nil
+}
diff --git a/test/busi/base_http.go b/test/busi/base_http.go
new file mode 100644
index 0000000..0feee1e
--- /dev/null
+++ b/test/busi/base_http.go
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2021 yedf. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+package busi
+
+import (
+ "database/sql"
+ "errors"
+ "fmt"
+
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/dtmutil"
+ "github.com/gin-gonic/gin"
+ "gorm.io/driver/mysql"
+ "gorm.io/driver/postgres"
+ "gorm.io/gorm"
+)
+
+const (
+ // BusiAPI busi api prefix
+ BusiAPI = "/api/busi"
+ // BusiPort busi server port
+ BusiPort = 8081
+ // BusiGrpcPort busi server port
+ BusiGrpcPort = 58081
+)
+
+type setupFunc func(*gin.Engine)
+
+var setupFuncs = map[string]setupFunc{}
+
+// Busi busi service url prefix
+var Busi string = fmt.Sprintf("http://localhost:%d%s", BusiPort, BusiAPI)
+
+var XaClient *dtmcli.XaClient = nil
+
+type SleepCancelHandler func(c *gin.Context) interface{}
+
+var sleepCancelHandler SleepCancelHandler = nil
+
+func SetSleepCancelHandler(handler SleepCancelHandler) {
+ sleepCancelHandler = handler
+}
+
+// BaseAppStartup base app startup
+func BaseAppStartup() *gin.Engine {
+ logger.Infof("examples starting")
+ app := dtmutil.GetGinApp()
+ app.Use(func(c *gin.Context) {
+ v := MainSwitch.NextResult.Fetch()
+ if v != "" {
+ c.JSON(200, gin.H{"dtm_result": v})
+ c.Abort()
+ return
+ }
+ c.Next()
+ })
+ var err error
+ XaClient, err = dtmcli.NewXaClient(dtmutil.DefaultHTTPServer, BusiConf, Busi+"/xa", func(path string, xa *dtmcli.XaClient) {
+ app.POST(path, dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ return xa.HandleCallback(c.Query("gid"), c.Query("branch_id"), c.Query("op"))
+ }))
+ })
+ logger.FatalIfError(err)
+
+ BaseAddRoute(app)
+ for k, v := range setupFuncs {
+ logger.Debugf("initing %s", k)
+ v(app)
+ }
+ logger.Debugf("Starting busi at: %d", BusiPort)
+ go app.Run(fmt.Sprintf(":%d", BusiPort))
+
+ return app
+}
+
+// BaseAddRoute add base route handler
+func BaseAddRoute(app *gin.Engine) {
+ app.POST(BusiAPI+"/TransIn", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ return handleGeneralBusiness(c, MainSwitch.TransInResult.Fetch(), reqFrom(c).TransInResult, "transIn")
+ }))
+ app.POST(BusiAPI+"/TransOut", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ return handleGeneralBusiness(c, MainSwitch.TransOutResult.Fetch(), reqFrom(c).TransOutResult, "TransOut")
+ }))
+ app.POST(BusiAPI+"/TransInConfirm", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ return handleGeneralBusiness(c, MainSwitch.TransInConfirmResult.Fetch(), "", "TransInConfirm")
+ }))
+ app.POST(BusiAPI+"/TransOutConfirm", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ return handleGeneralBusiness(c, MainSwitch.TransOutConfirmResult.Fetch(), "", "TransOutConfirm")
+ }))
+ app.POST(BusiAPI+"/TransInRevert", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ return handleGeneralBusiness(c, MainSwitch.TransInRevertResult.Fetch(), "", "TransInRevert")
+ }))
+ app.POST(BusiAPI+"/TransOutRevert", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ return handleGeneralBusiness(c, MainSwitch.TransOutRevertResult.Fetch(), "", "TransOutRevert")
+ }))
+ app.POST(BusiAPI+"/TransInOld", oldWrapHandler(func(c *gin.Context) (interface{}, error) {
+ return handleGeneralBusinessCompatible(c, MainSwitch.TransInResult.Fetch(), reqFrom(c).TransInResult, "transIn")
+ }))
+ app.POST(BusiAPI+"/TransOutOld", oldWrapHandler(func(c *gin.Context) (interface{}, error) {
+ return handleGeneralBusinessCompatible(c, MainSwitch.TransOutResult.Fetch(), reqFrom(c).TransOutResult, "TransOut")
+ }))
+ app.POST(BusiAPI+"/TransInConfirmOld", oldWrapHandler(func(c *gin.Context) (interface{}, error) {
+ return handleGeneralBusinessCompatible(c, MainSwitch.TransInConfirmResult.Fetch(), "", "TransInConfirm")
+ }))
+ app.POST(BusiAPI+"/TransOutConfirmOld", oldWrapHandler(func(c *gin.Context) (interface{}, error) {
+ return handleGeneralBusinessCompatible(c, MainSwitch.TransOutConfirmResult.Fetch(), "", "TransOutConfirm")
+ }))
+ app.POST(BusiAPI+"/TransInRevertOld", oldWrapHandler(func(c *gin.Context) (interface{}, error) {
+ return handleGeneralBusinessCompatible(c, MainSwitch.TransInRevertResult.Fetch(), "", "TransInRevert")
+ }))
+ app.POST(BusiAPI+"/TransOutRevertOld", oldWrapHandler(func(c *gin.Context) (interface{}, error) {
+ return handleGeneralBusinessCompatible(c, MainSwitch.TransOutRevertResult.Fetch(), "", "TransOutRevert")
+ }))
+
+ app.GET(BusiAPI+"/QueryPrepared", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ logger.Debugf("%s QueryPrepared", c.Query("gid"))
+ return dtmcli.String2DtmError(dtmimp.OrString(MainSwitch.QueryPreparedResult.Fetch(), dtmcli.ResultSuccess))
+ }))
+ app.GET(BusiAPI+"/QueryPreparedB", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ logger.Debugf("%s QueryPreparedB", c.Query("gid"))
+ bb := MustBarrierFromGin(c)
+ db := dbGet().ToSQLDB()
+ return bb.QueryPrepared(db)
+ }))
+ app.POST(BusiAPI+"/TransInXa", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ return XaClient.XaLocalTransaction(c.Request.URL.Query(), func(db *sql.DB, xa *dtmcli.Xa) error {
+ return SagaAdjustBalance(db, TransInUID, reqFrom(c).Amount, reqFrom(c).TransInResult)
+ })
+ }))
+ app.POST(BusiAPI+"/TransOutXa", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ return XaClient.XaLocalTransaction(c.Request.URL.Query(), func(db *sql.DB, xa *dtmcli.Xa) error {
+ return SagaAdjustBalance(db, TransOutUID, reqFrom(c).Amount, reqFrom(c).TransOutResult)
+ })
+ }))
+
+ app.POST(BusiAPI+"/TransInTccNested", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ tcc, err := dtmcli.TccFromQuery(c.Request.URL.Query())
+ logger.FatalIfError(err)
+ logger.Debugf("TransInTccNested ")
+ resp, err := tcc.CallBranch(&TransReq{Amount: reqFrom(c).Amount}, Busi+"/TransIn", Busi+"/TransInConfirm", Busi+"/TransInRevert")
+ if err != nil {
+ return err
+ }
+ return resp
+ }))
+ app.POST(BusiAPI+"/TransOutXaGorm", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ return XaClient.XaLocalTransaction(c.Request.URL.Query(), func(db *sql.DB, xa *dtmcli.Xa) error {
+ if reqFrom(c).TransOutResult == dtmcli.ResultFailure {
+ return dtmcli.ErrFailure
+ }
+ var dia gorm.Dialector = nil
+ if dtmcli.GetCurrentDBType() == dtmcli.DBTypeMysql {
+ dia = mysql.New(mysql.Config{Conn: db})
+ } else if dtmcli.GetCurrentDBType() == dtmcli.DBTypePostgres {
+ dia = postgres.New(postgres.Config{Conn: db})
+ }
+ gdb, err := gorm.Open(dia, &gorm.Config{})
+ if err != nil {
+ return err
+ }
+ dbr := gdb.Exec("update dtm_busi.user_account set balance=balance-? where user_id=?", reqFrom(c).Amount, TransOutUID)
+ return dbr.Error
+ })
+ }))
+
+ app.POST(BusiAPI+"/TestPanic", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ if c.Query("panic_error") != "" {
+ panic(errors.New("panic_error"))
+ } else if c.Query("panic_string") != "" {
+ panic("panic_string")
+ }
+ return nil
+ }))
+ app.POST(BusiAPI+"/TccBSleepCancel", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ return sleepCancelHandler(c)
+ }))
+ app.POST(BusiAPI+"/TransOutHeaderYes", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ h := c.GetHeader("test_header")
+ if h == "" {
+ return errors.New("no test_header found in TransOutHeaderYes")
+ }
+ return handleGeneralBusiness(c, MainSwitch.TransOutResult.Fetch(), reqFrom(c).TransOutResult, "TransOut")
+ }))
+ app.POST(BusiAPI+"/TransOutHeaderNo", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ h := c.GetHeader("test_header")
+ if h != "" {
+ return errors.New("test_header found in TransOutHeaderNo")
+ }
+ return nil
+ }))
+}
diff --git a/test/busi/base_types.go b/test/busi/base_types.go
new file mode 100644
index 0000000..38b332d
--- /dev/null
+++ b/test/busi/base_types.go
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2021 yedf. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+package busi
+
+import (
+ "fmt"
+
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/gin-gonic/gin"
+)
+
+var BusiConf = dtmcli.DBConf{
+ Driver: "mysql",
+ Host: "localhost",
+ Port: 3306,
+ User: "root",
+}
+
+type UserAccount struct {
+ UserId int
+ Balance string
+ TradingBalance string
+}
+
+func (*UserAccount) TableName() string {
+ return "dtm_busi.user_account"
+}
+
+func GetBalanceByUid(uid int, store string) int {
+ if store == "redis" {
+ rd := RedisGet()
+ accA, err := rd.Get(rd.Context(), getRedisAccountKey(uid)).Result()
+ dtmimp.E2P(err)
+ return dtmimp.MustAtoi(accA)
+ }
+ ua := UserAccount{}
+ _ = dbGet().Must().Model(&ua).Where("user_id=?", uid).First(&ua)
+ return dtmimp.MustAtoi(ua.Balance[:len(ua.Balance)-3])
+}
+
+// TransReq transaction request payload
+type TransReq struct {
+ Amount int `json:"amount"`
+ TransInResult string `json:"trans_in_result"`
+ TransOutResult string `json:"trans_out_Result"`
+ Store string `json:"store"` // default mysql, value can be mysql|redis
+}
+
+func (t *TransReq) String() string {
+ return fmt.Sprintf("amount: %d transIn: %s transOut: %s", t.Amount, t.TransInResult, t.TransOutResult)
+}
+
+// GenTransReq 1
+func GenTransReq(amount int, outFailed bool, inFailed bool) *TransReq {
+ return &TransReq{
+ Amount: amount,
+ TransOutResult: dtmimp.If(outFailed, dtmcli.ResultFailure, "").(string),
+ TransInResult: dtmimp.If(inFailed, dtmcli.ResultFailure, "").(string),
+ }
+}
+
+// GenBusiReq 1
+func GenBusiReq(amount int, outFailed bool, inFailed bool) *BusiReq {
+ return &BusiReq{
+ Amount: int64(amount),
+ TransOutResult: dtmimp.If(outFailed, dtmcli.ResultFailure, "").(string),
+ TransInResult: dtmimp.If(inFailed, dtmcli.ResultFailure, "").(string),
+ }
+}
+
+func reqFrom(c *gin.Context) *TransReq {
+ v, ok := c.Get("trans_req")
+ if !ok {
+ req := TransReq{}
+ err := c.BindJSON(&req)
+ logger.FatalIfError(err)
+ c.Set("trans_req", &req)
+ v = &req
+ }
+ return v.(*TransReq)
+}
+
+func infoFromContext(c *gin.Context) *dtmcli.BranchBarrier {
+ info := dtmcli.BranchBarrier{
+ TransType: c.Query("trans_type"),
+ Gid: c.Query("gid"),
+ BranchID: c.Query("branch_id"),
+ Op: c.Query("op"),
+ }
+ return &info
+}
+
+// AutoEmptyString auto reset to empty when used once
+type AutoEmptyString struct {
+ value string
+}
+
+// SetOnce set a value once
+func (s *AutoEmptyString) SetOnce(v string) {
+ s.value = v
+}
+
+// Fetch fetch the stored value, then reset the value to empty
+func (s *AutoEmptyString) Fetch() string {
+ v := s.value
+ s.value = ""
+ return v
+}
+
+type mainSwitchType struct {
+ TransInResult AutoEmptyString
+ TransOutResult AutoEmptyString
+ TransInConfirmResult AutoEmptyString
+ TransOutConfirmResult AutoEmptyString
+ TransInRevertResult AutoEmptyString
+ TransOutRevertResult AutoEmptyString
+ QueryPreparedResult AutoEmptyString
+ NextResult AutoEmptyString
+}
+
+// MainSwitch controls busi success or fail
+var MainSwitch mainSwitchType
+
+func getRedisAccountKey(uid int) string {
+ return fmt.Sprintf("{a}-redis-account-key-%d", uid)
+}
diff --git a/test/busi/busi.go b/test/busi/busi.go
new file mode 100644
index 0000000..3fda2f2
--- /dev/null
+++ b/test/busi/busi.go
@@ -0,0 +1,84 @@
+package busi
+
+import (
+ "errors"
+ "fmt"
+ "strings"
+
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/gin-gonic/gin"
+ codes "google.golang.org/grpc/codes"
+ status "google.golang.org/grpc/status"
+)
+
+const TransOutUID = 1
+const TransInUID = 2
+
+func handleGrpcBusiness(in *BusiReq, result1 string, result2 string, busi string) error {
+ res := dtmimp.OrString(result1, result2, dtmcli.ResultSuccess)
+ logger.Debugf("grpc busi %s %v %s %s result: %s", busi, in, result1, result2, res)
+ if res == dtmcli.ResultSuccess {
+ return nil
+ } else if res == dtmcli.ResultFailure {
+ return status.New(codes.Aborted, dtmcli.ResultFailure).Err()
+ } else if res == dtmcli.ResultOngoing {
+ return status.New(codes.FailedPrecondition, dtmcli.ResultOngoing).Err()
+ }
+ return status.New(codes.Internal, fmt.Sprintf("unknow result %s", res)).Err()
+}
+
+func handleGeneralBusiness(c *gin.Context, result1 string, result2 string, busi string) interface{} {
+ info := infoFromContext(c)
+ res := dtmimp.OrString(result1, result2, dtmcli.ResultSuccess)
+ logger.Debugf("%s %s result: %s", busi, info.String(), res)
+ if res == "ERROR" {
+ return errors.New("ERROR from user")
+ }
+ return dtmcli.String2DtmError(res)
+}
+
+// old business handler. for compatible usage
+func handleGeneralBusinessCompatible(c *gin.Context, result1 string, result2 string, busi string) (interface{}, error) {
+ info := infoFromContext(c)
+ res := dtmimp.OrString(result1, result2, dtmcli.ResultSuccess)
+ logger.Debugf("%s %s result: %s", busi, info.String(), res)
+ if res == "ERROR" {
+ return nil, errors.New("ERROR from user")
+ }
+ return map[string]interface{}{"dtm_result": res}, nil
+}
+
+func sagaGrpcAdjustBalance(db dtmcli.DB, uid int, amount int64, result string) error {
+ if result == dtmcli.ResultFailure {
+ return status.New(codes.Aborted, dtmcli.ResultFailure).Err()
+ }
+ _, err := dtmimp.DBExec(db, "update dtm_busi.user_account set balance = balance + ? where user_id = ?", amount, uid)
+ return err
+}
+
+func SagaAdjustBalance(db dtmcli.DB, uid int, amount int, result string) error {
+ if strings.Contains(result, dtmcli.ResultFailure) {
+ return dtmcli.ErrFailure
+ }
+ _, err := dtmimp.DBExec(db, "update dtm_busi.user_account set balance = balance + ? where user_id = ?", amount, uid)
+ return err
+}
+func tccAdjustTrading(db dtmcli.DB, uid int, amount int) error {
+ affected, err := dtmimp.DBExec(db, `update dtm_busi.user_account set trading_balance=trading_balance+?
+ where user_id=? and trading_balance + ? + balance >= 0`, amount, uid, amount)
+ if err == nil && affected == 0 {
+ return fmt.Errorf("update error, maybe balance not enough")
+ }
+ return err
+}
+
+func tccAdjustBalance(db dtmcli.DB, uid int, amount int) error {
+ affected, err := dtmimp.DBExec(db, `update dtm_busi.user_account set trading_balance=trading_balance-?,
+ balance=balance+? where user_id=?`, amount, amount, uid)
+ if err == nil && affected == 0 {
+ return fmt.Errorf("update user_account 0 rows")
+ }
+ return err
+}
diff --git a/test/busi/busi.pb.go b/test/busi/busi.pb.go
new file mode 100644
index 0000000..759886f
--- /dev/null
+++ b/test/busi/busi.pb.go
@@ -0,0 +1,343 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.27.1
+// protoc v3.17.3
+// source: test/busi/busi.proto
+
+package busi
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ emptypb "google.golang.org/protobuf/types/known/emptypb"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// DtmRequest request sent to dtm server
+type BusiReq struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Amount int64 `protobuf:"varint,1,opt,name=Amount,proto3" json:"Amount,omitempty"`
+ TransOutResult string `protobuf:"bytes,2,opt,name=TransOutResult,proto3" json:"TransOutResult,omitempty"`
+ TransInResult string `protobuf:"bytes,3,opt,name=TransInResult,proto3" json:"TransInResult,omitempty"`
+}
+
+func (x *BusiReq) Reset() {
+ *x = BusiReq{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_test_busi_busi_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *BusiReq) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BusiReq) ProtoMessage() {}
+
+func (x *BusiReq) ProtoReflect() protoreflect.Message {
+ mi := &file_test_busi_busi_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use BusiReq.ProtoReflect.Descriptor instead.
+func (*BusiReq) Descriptor() ([]byte, []int) {
+ return file_test_busi_busi_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *BusiReq) GetAmount() int64 {
+ if x != nil {
+ return x.Amount
+ }
+ return 0
+}
+
+func (x *BusiReq) GetTransOutResult() string {
+ if x != nil {
+ return x.TransOutResult
+ }
+ return ""
+}
+
+func (x *BusiReq) GetTransInResult() string {
+ if x != nil {
+ return x.TransInResult
+ }
+ return ""
+}
+
+type BusiReply struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
+}
+
+func (x *BusiReply) Reset() {
+ *x = BusiReply{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_test_busi_busi_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *BusiReply) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BusiReply) ProtoMessage() {}
+
+func (x *BusiReply) ProtoReflect() protoreflect.Message {
+ mi := &file_test_busi_busi_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use BusiReply.ProtoReflect.Descriptor instead.
+func (*BusiReply) Descriptor() ([]byte, []int) {
+ return file_test_busi_busi_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *BusiReply) GetMessage() string {
+ if x != nil {
+ return x.Message
+ }
+ return ""
+}
+
+var File_test_busi_busi_proto protoreflect.FileDescriptor
+
+var file_test_busi_busi_proto_rawDesc = []byte{
+ 0x0a, 0x14, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x62, 0x75, 0x73, 0x69, 0x2f, 0x62, 0x75, 0x73, 0x69,
+ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x62, 0x75, 0x73, 0x69, 0x1a, 0x1b, 0x67, 0x6f,
+ 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d,
+ 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6f, 0x0a, 0x07, 0x42, 0x75, 0x73,
+ 0x69, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0e,
+ 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x52, 0x65,
+ 0x73, 0x75, 0x6c, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x49, 0x6e, 0x52,
+ 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x54, 0x72, 0x61,
+ 0x6e, 0x73, 0x49, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x25, 0x0a, 0x09, 0x42, 0x75,
+ 0x73, 0x69, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61,
+ 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
+ 0x65, 0x32, 0x8d, 0x09, 0x0a, 0x04, 0x42, 0x75, 0x73, 0x69, 0x12, 0x32, 0x0a, 0x07, 0x54, 0x72,
+ 0x61, 0x6e, 0x73, 0x49, 0x6e, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x42, 0x75, 0x73,
+ 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x33,
+ 0x0a, 0x08, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73,
+ 0x69, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+ 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
+ 0x79, 0x22, 0x00, 0x12, 0x38, 0x0a, 0x0d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x49, 0x6e, 0x52, 0x65,
+ 0x76, 0x65, 0x72, 0x74, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x42, 0x75, 0x73, 0x69,
+ 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x39, 0x0a,
+ 0x0e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x76, 0x65, 0x72, 0x74, 0x12,
+ 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16,
+ 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+ 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x0e, 0x54, 0x72, 0x61, 0x6e,
+ 0x73, 0x49, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73,
+ 0x69, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+ 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
+ 0x79, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x0f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x43,
+ 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x42, 0x75,
+ 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12,
+ 0x3c, 0x0a, 0x08, 0x58, 0x61, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x12, 0x16, 0x2e, 0x67, 0x6f,
+ 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
+ 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x34, 0x0a,
+ 0x09, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x49, 0x6e, 0x58, 0x61, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73,
+ 0x69, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+ 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
+ 0x79, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x0a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x58,
+ 0x61, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71,
+ 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+ 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x0a, 0x54, 0x72,
+ 0x61, 0x6e, 0x73, 0x49, 0x6e, 0x54, 0x63, 0x63, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e,
+ 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22,
+ 0x00, 0x12, 0x36, 0x0a, 0x0b, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x54, 0x63, 0x63,
+ 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a,
+ 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
+ 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x10, 0x54, 0x72, 0x61,
+ 0x6e, 0x73, 0x49, 0x6e, 0x54, 0x63, 0x63, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x12, 0x0d, 0x2e,
+ 0x62, 0x75, 0x73, 0x69, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67,
+ 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,
+ 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x37, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x49,
+ 0x6e, 0x42, 0x53, 0x61, 0x67, 0x61, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x42, 0x75,
+ 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12,
+ 0x38, 0x0a, 0x0d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x42, 0x53, 0x61, 0x67, 0x61,
+ 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a,
+ 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
+ 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x12, 0x54, 0x72, 0x61,
+ 0x6e, 0x73, 0x49, 0x6e, 0x52, 0x65, 0x76, 0x65, 0x72, 0x74, 0x42, 0x53, 0x61, 0x67, 0x61, 0x12,
+ 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16,
+ 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+ 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x13, 0x54, 0x72, 0x61, 0x6e,
+ 0x73, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x76, 0x65, 0x72, 0x74, 0x42, 0x53, 0x61, 0x67, 0x61, 0x12,
+ 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16,
+ 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+ 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3c, 0x0a, 0x11, 0x54, 0x72, 0x61, 0x6e,
+ 0x73, 0x4f, 0x75, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x59, 0x65, 0x73, 0x12, 0x0d, 0x2e,
+ 0x62, 0x75, 0x73, 0x69, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67,
+ 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,
+ 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x10, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f,
+ 0x75, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x4e, 0x6f, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73,
+ 0x69, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+ 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
+ 0x79, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x65, 0x70,
+ 0x61, 0x72, 0x65, 0x64, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x42, 0x75, 0x73, 0x69,
+ 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x42, 0x75, 0x73, 0x69, 0x52,
+ 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x0e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50,
+ 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x64, 0x42, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e,
+ 0x42, 0x75, 0x73, 0x69, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22,
+ 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2e, 0x2f, 0x62, 0x75, 0x73, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x33,
+}
+
+var (
+ file_test_busi_busi_proto_rawDescOnce sync.Once
+ file_test_busi_busi_proto_rawDescData = file_test_busi_busi_proto_rawDesc
+)
+
+func file_test_busi_busi_proto_rawDescGZIP() []byte {
+ file_test_busi_busi_proto_rawDescOnce.Do(func() {
+ file_test_busi_busi_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_busi_busi_proto_rawDescData)
+ })
+ return file_test_busi_busi_proto_rawDescData
+}
+
+var file_test_busi_busi_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_test_busi_busi_proto_goTypes = []interface{}{
+ (*BusiReq)(nil), // 0: busi.BusiReq
+ (*BusiReply)(nil), // 1: busi.BusiReply
+ (*emptypb.Empty)(nil), // 2: google.protobuf.Empty
+}
+var file_test_busi_busi_proto_depIdxs = []int32{
+ 0, // 0: busi.Busi.TransIn:input_type -> busi.BusiReq
+ 0, // 1: busi.Busi.TransOut:input_type -> busi.BusiReq
+ 0, // 2: busi.Busi.TransInRevert:input_type -> busi.BusiReq
+ 0, // 3: busi.Busi.TransOutRevert:input_type -> busi.BusiReq
+ 0, // 4: busi.Busi.TransInConfirm:input_type -> busi.BusiReq
+ 0, // 5: busi.Busi.TransOutConfirm:input_type -> busi.BusiReq
+ 2, // 6: busi.Busi.XaNotify:input_type -> google.protobuf.Empty
+ 0, // 7: busi.Busi.TransInXa:input_type -> busi.BusiReq
+ 0, // 8: busi.Busi.TransOutXa:input_type -> busi.BusiReq
+ 0, // 9: busi.Busi.TransInTcc:input_type -> busi.BusiReq
+ 0, // 10: busi.Busi.TransOutTcc:input_type -> busi.BusiReq
+ 0, // 11: busi.Busi.TransInTccNested:input_type -> busi.BusiReq
+ 0, // 12: busi.Busi.TransInBSaga:input_type -> busi.BusiReq
+ 0, // 13: busi.Busi.TransOutBSaga:input_type -> busi.BusiReq
+ 0, // 14: busi.Busi.TransInRevertBSaga:input_type -> busi.BusiReq
+ 0, // 15: busi.Busi.TransOutRevertBSaga:input_type -> busi.BusiReq
+ 0, // 16: busi.Busi.TransOutHeaderYes:input_type -> busi.BusiReq
+ 0, // 17: busi.Busi.TransOutHeaderNo:input_type -> busi.BusiReq
+ 0, // 18: busi.Busi.QueryPrepared:input_type -> busi.BusiReq
+ 0, // 19: busi.Busi.QueryPreparedB:input_type -> busi.BusiReq
+ 2, // 20: busi.Busi.TransIn:output_type -> google.protobuf.Empty
+ 2, // 21: busi.Busi.TransOut:output_type -> google.protobuf.Empty
+ 2, // 22: busi.Busi.TransInRevert:output_type -> google.protobuf.Empty
+ 2, // 23: busi.Busi.TransOutRevert:output_type -> google.protobuf.Empty
+ 2, // 24: busi.Busi.TransInConfirm:output_type -> google.protobuf.Empty
+ 2, // 25: busi.Busi.TransOutConfirm:output_type -> google.protobuf.Empty
+ 2, // 26: busi.Busi.XaNotify:output_type -> google.protobuf.Empty
+ 2, // 27: busi.Busi.TransInXa:output_type -> google.protobuf.Empty
+ 2, // 28: busi.Busi.TransOutXa:output_type -> google.protobuf.Empty
+ 2, // 29: busi.Busi.TransInTcc:output_type -> google.protobuf.Empty
+ 2, // 30: busi.Busi.TransOutTcc:output_type -> google.protobuf.Empty
+ 2, // 31: busi.Busi.TransInTccNested:output_type -> google.protobuf.Empty
+ 2, // 32: busi.Busi.TransInBSaga:output_type -> google.protobuf.Empty
+ 2, // 33: busi.Busi.TransOutBSaga:output_type -> google.protobuf.Empty
+ 2, // 34: busi.Busi.TransInRevertBSaga:output_type -> google.protobuf.Empty
+ 2, // 35: busi.Busi.TransOutRevertBSaga:output_type -> google.protobuf.Empty
+ 2, // 36: busi.Busi.TransOutHeaderYes:output_type -> google.protobuf.Empty
+ 2, // 37: busi.Busi.TransOutHeaderNo:output_type -> google.protobuf.Empty
+ 1, // 38: busi.Busi.QueryPrepared:output_type -> busi.BusiReply
+ 2, // 39: busi.Busi.QueryPreparedB:output_type -> google.protobuf.Empty
+ 20, // [20:40] is the sub-list for method output_type
+ 0, // [0:20] is the sub-list for method input_type
+ 0, // [0:0] is the sub-list for extension type_name
+ 0, // [0:0] is the sub-list for extension extendee
+ 0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_test_busi_busi_proto_init() }
+func file_test_busi_busi_proto_init() {
+ if File_test_busi_busi_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_test_busi_busi_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*BusiReq); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_test_busi_busi_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*BusiReply); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_test_busi_busi_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 2,
+ NumExtensions: 0,
+ NumServices: 1,
+ },
+ GoTypes: file_test_busi_busi_proto_goTypes,
+ DependencyIndexes: file_test_busi_busi_proto_depIdxs,
+ MessageInfos: file_test_busi_busi_proto_msgTypes,
+ }.Build()
+ File_test_busi_busi_proto = out.File
+ file_test_busi_busi_proto_rawDesc = nil
+ file_test_busi_busi_proto_goTypes = nil
+ file_test_busi_busi_proto_depIdxs = nil
+}
diff --git a/examples/busi.proto b/test/busi/busi.proto
similarity index 81%
rename from examples/busi.proto
rename to test/busi/busi.proto
index 8e34fa8..c94d8df 100644
--- a/examples/busi.proto
+++ b/test/busi/busi.proto
@@ -1,9 +1,9 @@
syntax = "proto3";
-package examples;
+package busi;
import "google/protobuf/empty.proto";
-option go_package = "./examples";
+option go_package = "./busi";
// DtmRequest request sent to dtm server
message BusiReq {
@@ -17,7 +17,6 @@ message BusiReply {
}
// The dtm service definition.
service Busi {
- rpc CanSubmit(BusiReq) returns (BusiReply) {}
rpc TransIn(BusiReq) returns (google.protobuf.Empty) {}
rpc TransOut(BusiReq) returns (google.protobuf.Empty) {}
rpc TransInRevert(BusiReq) returns (google.protobuf.Empty) {}
@@ -36,5 +35,9 @@ service Busi {
rpc TransOutBSaga(BusiReq) returns (google.protobuf.Empty) {}
rpc TransInRevertBSaga(BusiReq) returns (google.protobuf.Empty) {}
rpc TransOutRevertBSaga(BusiReq) returns (google.protobuf.Empty) {}
+ rpc TransOutHeaderYes(BusiReq) returns (google.protobuf.Empty) {}
+ rpc TransOutHeaderNo(BusiReq) returns (google.protobuf.Empty) {}
+ rpc QueryPrepared(BusiReq) returns (BusiReply) {}
+ rpc QueryPreparedB(BusiReq) returns (google.protobuf.Empty) {}
}
diff --git a/examples/busi_grpc.pb.go b/test/busi/busi_grpc.pb.go
similarity index 76%
rename from examples/busi_grpc.pb.go
rename to test/busi/busi_grpc.pb.go
index f9e9427..f7db854 100644
--- a/examples/busi_grpc.pb.go
+++ b/test/busi/busi_grpc.pb.go
@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
-package examples
+package busi
import (
context "context"
@@ -19,7 +19,6 @@ const _ = grpc.SupportPackageIsVersion7
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type BusiClient interface {
- CanSubmit(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*BusiReply, error)
TransIn(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error)
TransOut(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error)
TransInRevert(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error)
@@ -36,6 +35,10 @@ type BusiClient interface {
TransOutBSaga(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error)
TransInRevertBSaga(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error)
TransOutRevertBSaga(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error)
+ TransOutHeaderYes(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error)
+ TransOutHeaderNo(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error)
+ QueryPrepared(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*BusiReply, error)
+ QueryPreparedB(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error)
}
type busiClient struct {
@@ -46,18 +49,9 @@ func NewBusiClient(cc grpc.ClientConnInterface) BusiClient {
return &busiClient{cc}
}
-func (c *busiClient) CanSubmit(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*BusiReply, error) {
- out := new(BusiReply)
- err := c.cc.Invoke(ctx, "/examples.Busi/CanSubmit", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
func (c *busiClient) TransIn(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
- err := c.cc.Invoke(ctx, "/examples.Busi/TransIn", in, out, opts...)
+ err := c.cc.Invoke(ctx, "/busi.Busi/TransIn", in, out, opts...)
if err != nil {
return nil, err
}
@@ -66,7 +60,7 @@ func (c *busiClient) TransIn(ctx context.Context, in *BusiReq, opts ...grpc.Call
func (c *busiClient) TransOut(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
- err := c.cc.Invoke(ctx, "/examples.Busi/TransOut", in, out, opts...)
+ err := c.cc.Invoke(ctx, "/busi.Busi/TransOut", in, out, opts...)
if err != nil {
return nil, err
}
@@ -75,7 +69,7 @@ func (c *busiClient) TransOut(ctx context.Context, in *BusiReq, opts ...grpc.Cal
func (c *busiClient) TransInRevert(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
- err := c.cc.Invoke(ctx, "/examples.Busi/TransInRevert", in, out, opts...)
+ err := c.cc.Invoke(ctx, "/busi.Busi/TransInRevert", in, out, opts...)
if err != nil {
return nil, err
}
@@ -84,7 +78,7 @@ func (c *busiClient) TransInRevert(ctx context.Context, in *BusiReq, opts ...grp
func (c *busiClient) TransOutRevert(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
- err := c.cc.Invoke(ctx, "/examples.Busi/TransOutRevert", in, out, opts...)
+ err := c.cc.Invoke(ctx, "/busi.Busi/TransOutRevert", in, out, opts...)
if err != nil {
return nil, err
}
@@ -93,7 +87,7 @@ func (c *busiClient) TransOutRevert(ctx context.Context, in *BusiReq, opts ...gr
func (c *busiClient) TransInConfirm(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
- err := c.cc.Invoke(ctx, "/examples.Busi/TransInConfirm", in, out, opts...)
+ err := c.cc.Invoke(ctx, "/busi.Busi/TransInConfirm", in, out, opts...)
if err != nil {
return nil, err
}
@@ -102,7 +96,7 @@ func (c *busiClient) TransInConfirm(ctx context.Context, in *BusiReq, opts ...gr
func (c *busiClient) TransOutConfirm(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
- err := c.cc.Invoke(ctx, "/examples.Busi/TransOutConfirm", in, out, opts...)
+ err := c.cc.Invoke(ctx, "/busi.Busi/TransOutConfirm", in, out, opts...)
if err != nil {
return nil, err
}
@@ -111,7 +105,7 @@ func (c *busiClient) TransOutConfirm(ctx context.Context, in *BusiReq, opts ...g
func (c *busiClient) XaNotify(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
- err := c.cc.Invoke(ctx, "/examples.Busi/XaNotify", in, out, opts...)
+ err := c.cc.Invoke(ctx, "/busi.Busi/XaNotify", in, out, opts...)
if err != nil {
return nil, err
}
@@ -120,7 +114,7 @@ func (c *busiClient) XaNotify(ctx context.Context, in *emptypb.Empty, opts ...gr
func (c *busiClient) TransInXa(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
- err := c.cc.Invoke(ctx, "/examples.Busi/TransInXa", in, out, opts...)
+ err := c.cc.Invoke(ctx, "/busi.Busi/TransInXa", in, out, opts...)
if err != nil {
return nil, err
}
@@ -129,7 +123,7 @@ func (c *busiClient) TransInXa(ctx context.Context, in *BusiReq, opts ...grpc.Ca
func (c *busiClient) TransOutXa(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
- err := c.cc.Invoke(ctx, "/examples.Busi/TransOutXa", in, out, opts...)
+ err := c.cc.Invoke(ctx, "/busi.Busi/TransOutXa", in, out, opts...)
if err != nil {
return nil, err
}
@@ -138,7 +132,7 @@ func (c *busiClient) TransOutXa(ctx context.Context, in *BusiReq, opts ...grpc.C
func (c *busiClient) TransInTcc(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
- err := c.cc.Invoke(ctx, "/examples.Busi/TransInTcc", in, out, opts...)
+ err := c.cc.Invoke(ctx, "/busi.Busi/TransInTcc", in, out, opts...)
if err != nil {
return nil, err
}
@@ -147,7 +141,7 @@ func (c *busiClient) TransInTcc(ctx context.Context, in *BusiReq, opts ...grpc.C
func (c *busiClient) TransOutTcc(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
- err := c.cc.Invoke(ctx, "/examples.Busi/TransOutTcc", in, out, opts...)
+ err := c.cc.Invoke(ctx, "/busi.Busi/TransOutTcc", in, out, opts...)
if err != nil {
return nil, err
}
@@ -156,7 +150,7 @@ func (c *busiClient) TransOutTcc(ctx context.Context, in *BusiReq, opts ...grpc.
func (c *busiClient) TransInTccNested(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
- err := c.cc.Invoke(ctx, "/examples.Busi/TransInTccNested", in, out, opts...)
+ err := c.cc.Invoke(ctx, "/busi.Busi/TransInTccNested", in, out, opts...)
if err != nil {
return nil, err
}
@@ -165,7 +159,7 @@ func (c *busiClient) TransInTccNested(ctx context.Context, in *BusiReq, opts ...
func (c *busiClient) TransInBSaga(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
- err := c.cc.Invoke(ctx, "/examples.Busi/TransInBSaga", in, out, opts...)
+ err := c.cc.Invoke(ctx, "/busi.Busi/TransInBSaga", in, out, opts...)
if err != nil {
return nil, err
}
@@ -174,7 +168,7 @@ func (c *busiClient) TransInBSaga(ctx context.Context, in *BusiReq, opts ...grpc
func (c *busiClient) TransOutBSaga(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
- err := c.cc.Invoke(ctx, "/examples.Busi/TransOutBSaga", in, out, opts...)
+ err := c.cc.Invoke(ctx, "/busi.Busi/TransOutBSaga", in, out, opts...)
if err != nil {
return nil, err
}
@@ -183,7 +177,7 @@ func (c *busiClient) TransOutBSaga(ctx context.Context, in *BusiReq, opts ...grp
func (c *busiClient) TransInRevertBSaga(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
- err := c.cc.Invoke(ctx, "/examples.Busi/TransInRevertBSaga", in, out, opts...)
+ err := c.cc.Invoke(ctx, "/busi.Busi/TransInRevertBSaga", in, out, opts...)
if err != nil {
return nil, err
}
@@ -192,7 +186,43 @@ func (c *busiClient) TransInRevertBSaga(ctx context.Context, in *BusiReq, opts .
func (c *busiClient) TransOutRevertBSaga(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
- err := c.cc.Invoke(ctx, "/examples.Busi/TransOutRevertBSaga", in, out, opts...)
+ err := c.cc.Invoke(ctx, "/busi.Busi/TransOutRevertBSaga", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *busiClient) TransOutHeaderYes(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+ out := new(emptypb.Empty)
+ err := c.cc.Invoke(ctx, "/busi.Busi/TransOutHeaderYes", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *busiClient) TransOutHeaderNo(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+ out := new(emptypb.Empty)
+ err := c.cc.Invoke(ctx, "/busi.Busi/TransOutHeaderNo", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *busiClient) QueryPrepared(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*BusiReply, error) {
+ out := new(BusiReply)
+ err := c.cc.Invoke(ctx, "/busi.Busi/QueryPrepared", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *busiClient) QueryPreparedB(ctx context.Context, in *BusiReq, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+ out := new(emptypb.Empty)
+ err := c.cc.Invoke(ctx, "/busi.Busi/QueryPreparedB", in, out, opts...)
if err != nil {
return nil, err
}
@@ -203,7 +233,6 @@ func (c *busiClient) TransOutRevertBSaga(ctx context.Context, in *BusiReq, opts
// All implementations must embed UnimplementedBusiServer
// for forward compatibility
type BusiServer interface {
- CanSubmit(context.Context, *BusiReq) (*BusiReply, error)
TransIn(context.Context, *BusiReq) (*emptypb.Empty, error)
TransOut(context.Context, *BusiReq) (*emptypb.Empty, error)
TransInRevert(context.Context, *BusiReq) (*emptypb.Empty, error)
@@ -220,6 +249,10 @@ type BusiServer interface {
TransOutBSaga(context.Context, *BusiReq) (*emptypb.Empty, error)
TransInRevertBSaga(context.Context, *BusiReq) (*emptypb.Empty, error)
TransOutRevertBSaga(context.Context, *BusiReq) (*emptypb.Empty, error)
+ TransOutHeaderYes(context.Context, *BusiReq) (*emptypb.Empty, error)
+ TransOutHeaderNo(context.Context, *BusiReq) (*emptypb.Empty, error)
+ QueryPrepared(context.Context, *BusiReq) (*BusiReply, error)
+ QueryPreparedB(context.Context, *BusiReq) (*emptypb.Empty, error)
mustEmbedUnimplementedBusiServer()
}
@@ -227,9 +260,6 @@ type BusiServer interface {
type UnimplementedBusiServer struct {
}
-func (UnimplementedBusiServer) CanSubmit(context.Context, *BusiReq) (*BusiReply, error) {
- return nil, status.Errorf(codes.Unimplemented, "method CanSubmit not implemented")
-}
func (UnimplementedBusiServer) TransIn(context.Context, *BusiReq) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method TransIn not implemented")
}
@@ -278,6 +308,18 @@ func (UnimplementedBusiServer) TransInRevertBSaga(context.Context, *BusiReq) (*e
func (UnimplementedBusiServer) TransOutRevertBSaga(context.Context, *BusiReq) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method TransOutRevertBSaga not implemented")
}
+func (UnimplementedBusiServer) TransOutHeaderYes(context.Context, *BusiReq) (*emptypb.Empty, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method TransOutHeaderYes not implemented")
+}
+func (UnimplementedBusiServer) TransOutHeaderNo(context.Context, *BusiReq) (*emptypb.Empty, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method TransOutHeaderNo not implemented")
+}
+func (UnimplementedBusiServer) QueryPrepared(context.Context, *BusiReq) (*BusiReply, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method QueryPrepared not implemented")
+}
+func (UnimplementedBusiServer) QueryPreparedB(context.Context, *BusiReq) (*emptypb.Empty, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method QueryPreparedB not implemented")
+}
func (UnimplementedBusiServer) mustEmbedUnimplementedBusiServer() {}
// UnsafeBusiServer may be embedded to opt out of forward compatibility for this service.
@@ -291,24 +333,6 @@ func RegisterBusiServer(s grpc.ServiceRegistrar, srv BusiServer) {
s.RegisterService(&Busi_ServiceDesc, srv)
}
-func _Busi_CanSubmit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(BusiReq)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(BusiServer).CanSubmit(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/examples.Busi/CanSubmit",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(BusiServer).CanSubmit(ctx, req.(*BusiReq))
- }
- return interceptor(ctx, in, info, handler)
-}
-
func _Busi_TransIn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(BusiReq)
if err := dec(in); err != nil {
@@ -319,7 +343,7 @@ func _Busi_TransIn_Handler(srv interface{}, ctx context.Context, dec func(interf
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/examples.Busi/TransIn",
+ FullMethod: "/busi.Busi/TransIn",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BusiServer).TransIn(ctx, req.(*BusiReq))
@@ -337,7 +361,7 @@ func _Busi_TransOut_Handler(srv interface{}, ctx context.Context, dec func(inter
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/examples.Busi/TransOut",
+ FullMethod: "/busi.Busi/TransOut",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BusiServer).TransOut(ctx, req.(*BusiReq))
@@ -355,7 +379,7 @@ func _Busi_TransInRevert_Handler(srv interface{}, ctx context.Context, dec func(
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/examples.Busi/TransInRevert",
+ FullMethod: "/busi.Busi/TransInRevert",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BusiServer).TransInRevert(ctx, req.(*BusiReq))
@@ -373,7 +397,7 @@ func _Busi_TransOutRevert_Handler(srv interface{}, ctx context.Context, dec func
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/examples.Busi/TransOutRevert",
+ FullMethod: "/busi.Busi/TransOutRevert",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BusiServer).TransOutRevert(ctx, req.(*BusiReq))
@@ -391,7 +415,7 @@ func _Busi_TransInConfirm_Handler(srv interface{}, ctx context.Context, dec func
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/examples.Busi/TransInConfirm",
+ FullMethod: "/busi.Busi/TransInConfirm",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BusiServer).TransInConfirm(ctx, req.(*BusiReq))
@@ -409,7 +433,7 @@ func _Busi_TransOutConfirm_Handler(srv interface{}, ctx context.Context, dec fun
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/examples.Busi/TransOutConfirm",
+ FullMethod: "/busi.Busi/TransOutConfirm",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BusiServer).TransOutConfirm(ctx, req.(*BusiReq))
@@ -427,7 +451,7 @@ func _Busi_XaNotify_Handler(srv interface{}, ctx context.Context, dec func(inter
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/examples.Busi/XaNotify",
+ FullMethod: "/busi.Busi/XaNotify",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BusiServer).XaNotify(ctx, req.(*emptypb.Empty))
@@ -445,7 +469,7 @@ func _Busi_TransInXa_Handler(srv interface{}, ctx context.Context, dec func(inte
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/examples.Busi/TransInXa",
+ FullMethod: "/busi.Busi/TransInXa",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BusiServer).TransInXa(ctx, req.(*BusiReq))
@@ -463,7 +487,7 @@ func _Busi_TransOutXa_Handler(srv interface{}, ctx context.Context, dec func(int
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/examples.Busi/TransOutXa",
+ FullMethod: "/busi.Busi/TransOutXa",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BusiServer).TransOutXa(ctx, req.(*BusiReq))
@@ -481,7 +505,7 @@ func _Busi_TransInTcc_Handler(srv interface{}, ctx context.Context, dec func(int
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/examples.Busi/TransInTcc",
+ FullMethod: "/busi.Busi/TransInTcc",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BusiServer).TransInTcc(ctx, req.(*BusiReq))
@@ -499,7 +523,7 @@ func _Busi_TransOutTcc_Handler(srv interface{}, ctx context.Context, dec func(in
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/examples.Busi/TransOutTcc",
+ FullMethod: "/busi.Busi/TransOutTcc",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BusiServer).TransOutTcc(ctx, req.(*BusiReq))
@@ -517,7 +541,7 @@ func _Busi_TransInTccNested_Handler(srv interface{}, ctx context.Context, dec fu
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/examples.Busi/TransInTccNested",
+ FullMethod: "/busi.Busi/TransInTccNested",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BusiServer).TransInTccNested(ctx, req.(*BusiReq))
@@ -535,7 +559,7 @@ func _Busi_TransInBSaga_Handler(srv interface{}, ctx context.Context, dec func(i
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/examples.Busi/TransInBSaga",
+ FullMethod: "/busi.Busi/TransInBSaga",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BusiServer).TransInBSaga(ctx, req.(*BusiReq))
@@ -553,7 +577,7 @@ func _Busi_TransOutBSaga_Handler(srv interface{}, ctx context.Context, dec func(
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/examples.Busi/TransOutBSaga",
+ FullMethod: "/busi.Busi/TransOutBSaga",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BusiServer).TransOutBSaga(ctx, req.(*BusiReq))
@@ -571,7 +595,7 @@ func _Busi_TransInRevertBSaga_Handler(srv interface{}, ctx context.Context, dec
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/examples.Busi/TransInRevertBSaga",
+ FullMethod: "/busi.Busi/TransInRevertBSaga",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BusiServer).TransInRevertBSaga(ctx, req.(*BusiReq))
@@ -589,7 +613,7 @@ func _Busi_TransOutRevertBSaga_Handler(srv interface{}, ctx context.Context, dec
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/examples.Busi/TransOutRevertBSaga",
+ FullMethod: "/busi.Busi/TransOutRevertBSaga",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BusiServer).TransOutRevertBSaga(ctx, req.(*BusiReq))
@@ -597,17 +621,85 @@ func _Busi_TransOutRevertBSaga_Handler(srv interface{}, ctx context.Context, dec
return interceptor(ctx, in, info, handler)
}
+func _Busi_TransOutHeaderYes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(BusiReq)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(BusiServer).TransOutHeaderYes(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/busi.Busi/TransOutHeaderYes",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(BusiServer).TransOutHeaderYes(ctx, req.(*BusiReq))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Busi_TransOutHeaderNo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(BusiReq)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(BusiServer).TransOutHeaderNo(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/busi.Busi/TransOutHeaderNo",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(BusiServer).TransOutHeaderNo(ctx, req.(*BusiReq))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Busi_QueryPrepared_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(BusiReq)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(BusiServer).QueryPrepared(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/busi.Busi/QueryPrepared",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(BusiServer).QueryPrepared(ctx, req.(*BusiReq))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Busi_QueryPreparedB_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(BusiReq)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(BusiServer).QueryPreparedB(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/busi.Busi/QueryPreparedB",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(BusiServer).QueryPreparedB(ctx, req.(*BusiReq))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
// Busi_ServiceDesc is the grpc.ServiceDesc for Busi service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Busi_ServiceDesc = grpc.ServiceDesc{
- ServiceName: "examples.Busi",
+ ServiceName: "busi.Busi",
HandlerType: (*BusiServer)(nil),
Methods: []grpc.MethodDesc{
- {
- MethodName: "CanSubmit",
- Handler: _Busi_CanSubmit_Handler,
- },
{
MethodName: "TransIn",
Handler: _Busi_TransIn_Handler,
@@ -672,7 +764,23 @@ var Busi_ServiceDesc = grpc.ServiceDesc{
MethodName: "TransOutRevertBSaga",
Handler: _Busi_TransOutRevertBSaga_Handler,
},
+ {
+ MethodName: "TransOutHeaderYes",
+ Handler: _Busi_TransOutHeaderYes_Handler,
+ },
+ {
+ MethodName: "TransOutHeaderNo",
+ Handler: _Busi_TransOutHeaderNo_Handler,
+ },
+ {
+ MethodName: "QueryPrepared",
+ Handler: _Busi_QueryPrepared_Handler,
+ },
+ {
+ MethodName: "QueryPreparedB",
+ Handler: _Busi_QueryPreparedB_Handler,
+ },
},
Streams: []grpc.StreamDesc{},
- Metadata: "examples/busi.proto",
+ Metadata: "test/busi/busi.proto",
}
diff --git a/examples/quick_start.go b/test/busi/quick_start.go
similarity index 52%
rename from examples/quick_start.go
rename to test/busi/quick_start.go
index e71d816..da939ec 100644
--- a/examples/quick_start.go
+++ b/test/busi/quick_start.go
@@ -4,16 +4,16 @@
* license that can be found in the LICENSE file.
*/
-package examples
+package busi
import (
"fmt"
"time"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/dtmutil"
"github.com/gin-gonic/gin"
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
)
// 启动命令:go run app/main.go qs
@@ -24,46 +24,45 @@ const qsBusiPort = 8082
var qsBusi = fmt.Sprintf("http://localhost:%d%s", qsBusiPort, qsBusiAPI)
-// QsStartSvr 1
func QsStartSvr() {
- app := common.GetGinApp()
+ app := dtmutil.GetGinApp()
qsAddRoute(app)
- dtmimp.Logf("quick qs examples listening at %d", qsBusiPort)
+ logger.Infof("quick start examples listening at %d", qsBusiPort)
go app.Run(fmt.Sprintf(":%d", qsBusiPort))
time.Sleep(100 * time.Millisecond)
}
-// QsFireRequest 1
func QsFireRequest() string {
req := &gin.H{"amount": 30} // 微服务的载荷
// DtmServer为DTM服务的地址
- saga := dtmcli.NewSaga(DtmHttpServer, dtmcli.MustGenGid(DtmHttpServer)).
+ saga := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, dtmcli.MustGenGid(dtmutil.DefaultHTTPServer)).
// 添加一个TransOut的子事务,正向操作为url: qsBusi+"/TransOut", 逆向操作为url: qsBusi+"/TransOutCompensate"
Add(qsBusi+"/TransOut", qsBusi+"/TransOutCompensate", req).
// 添加一个TransIn的子事务,正向操作为url: qsBusi+"/TransOut", 逆向操作为url: qsBusi+"/TransInCompensate"
Add(qsBusi+"/TransIn", qsBusi+"/TransInCompensate", req)
+ // 等待事务全部完成后再返回,可选
+ saga.WaitResult = true
// 提交saga事务,dtm会完成所有的子事务/回滚所有的子事务
err := saga.Submit()
- dtmimp.FatalIfError(err)
+ logger.FatalIfError(err)
return saga.Gid
}
-func qsAdjustBalance(uid int, amount int) (interface{}, error) {
- _, err := dtmimp.DBExec(sdbGet(), "update dtm_busi.user_account set balance = balance + ? where user_id = ?", amount, uid)
- return dtmcli.MapSuccess, err
-}
-
func qsAddRoute(app *gin.Engine) {
- app.POST(qsBusiAPI+"/TransIn", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
- return qsAdjustBalance(2, 30)
+ app.POST(qsBusiAPI+"/TransIn", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ logger.Infof("TransIn")
+ return nil
}))
- app.POST(qsBusiAPI+"/TransInCompensate", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
- return qsAdjustBalance(2, -30)
+ app.POST(qsBusiAPI+"/TransInCompensate", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ logger.Infof("TransInCompensate")
+ return nil
}))
- app.POST(qsBusiAPI+"/TransOut", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
- return qsAdjustBalance(1, -30)
+ app.POST(qsBusiAPI+"/TransOut", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ logger.Infof("TransOut")
+ return nil
}))
- app.POST(qsBusiAPI+"/TransOutCompensate", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
- return qsAdjustBalance(1, 30)
+ app.POST(qsBusiAPI+"/TransOutCompensate", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
+ logger.Infof("TransOutCompensate")
+ return nil
}))
}
diff --git a/test/busi/startup.go b/test/busi/startup.go
new file mode 100644
index 0000000..2b40a5c
--- /dev/null
+++ b/test/busi/startup.go
@@ -0,0 +1,27 @@
+package busi
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmutil"
+ "github.com/gin-gonic/gin"
+)
+
+// Startup startup the busi's grpc and http service
+func Startup() *gin.Engine {
+ GrpcStartup()
+ return BaseAppStartup()
+}
+
+// PopulateDB populate example mysql data
+func PopulateDB(skipDrop bool) {
+ resetXaData()
+ file := fmt.Sprintf("%s/busi.%s.sql", dtmutil.GetSQLDir(), BusiConf.Driver)
+ dtmutil.RunSQLScript(BusiConf, file, skipDrop)
+ file = fmt.Sprintf("%s/dtmcli.barrier.%s.sql", dtmutil.GetSQLDir(), BusiConf.Driver)
+ dtmutil.RunSQLScript(BusiConf, file, skipDrop)
+ _, err := RedisGet().FlushAll(context.Background()).Result() // redis barrier need clear
+ dtmimp.E2P(err)
+}
diff --git a/test/busi/utils.go b/test/busi/utils.go
new file mode 100644
index 0000000..9441d07
--- /dev/null
+++ b/test/busi/utils.go
@@ -0,0 +1,142 @@
+package busi
+
+import (
+ "context"
+ "database/sql"
+ "encoding/json"
+ "fmt"
+ "strings"
+ sync "sync"
+ "time"
+
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/dtmgrpc"
+ "github.com/dtm-labs/dtm/dtmgrpc/dtmgpb"
+ "github.com/dtm-labs/dtm/dtmutil"
+ "github.com/gin-gonic/gin"
+ "github.com/go-redis/redis/v8"
+ "github.com/go-resty/resty/v2"
+ grpc "google.golang.org/grpc"
+ "google.golang.org/grpc/metadata"
+)
+
+func dbGet() *dtmutil.DB {
+ return dtmutil.DbGet(BusiConf)
+}
+
+func pdbGet() *sql.DB {
+ db, err := dtmimp.PooledDB(BusiConf)
+ logger.FatalIfError(err)
+ return db
+}
+
+func txGet() *sql.Tx {
+ db := pdbGet()
+ tx, err := db.Begin()
+ logger.FatalIfError(err)
+ return tx
+}
+
+func resetXaData() {
+ if BusiConf.Driver != "mysql" {
+ return
+ }
+
+ db := dbGet()
+ type XaRow struct {
+ Data string
+ }
+ xas := []XaRow{}
+ db.Must().Raw("xa recover").Scan(&xas)
+ for _, xa := range xas {
+ db.Must().Exec(fmt.Sprintf("xa rollback '%s'", xa.Data))
+ }
+}
+
+// MustBarrierFromGin 1
+func MustBarrierFromGin(c *gin.Context) *dtmcli.BranchBarrier {
+ ti, err := dtmcli.BarrierFromQuery(c.Request.URL.Query())
+ logger.FatalIfError(err)
+ return ti
+}
+
+// MustBarrierFromGrpc 1
+func MustBarrierFromGrpc(ctx context.Context) *dtmcli.BranchBarrier {
+ ti, err := dtmgrpc.BarrierFromGrpc(ctx)
+ logger.FatalIfError(err)
+ return ti
+}
+
+// SetGrpcHeaderForHeadersYes interceptor to set head for HeadersYes
+func SetGrpcHeaderForHeadersYes(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
+ if r, ok := req.(*dtmgpb.DtmRequest); ok && strings.HasSuffix(r.Gid, "HeadersYes") {
+ logger.Debugf("writing test_header:test to ctx")
+ md := metadata.New(map[string]string{"test_header": "test"})
+ ctx = metadata.NewOutgoingContext(ctx, md)
+ }
+ return invoker(ctx, method, req, reply, cc, opts...)
+}
+
+// SetHttpHeaderForHeadersYes interceptor to set head for HeadersYes
+func SetHttpHeaderForHeadersYes(c *resty.Client, r *resty.Request) error {
+ if b, ok := r.Body.(*dtmcli.Saga); ok && strings.HasSuffix(b.Gid, "HeadersYes") {
+ logger.Debugf("set test_header for url: %s", r.URL)
+ r.SetHeader("test_header", "yes")
+ }
+ return nil
+}
+
+// oldWrapHandler old wrap handler for test use of dtm
+func oldWrapHandler(fn func(*gin.Context) (interface{}, error)) gin.HandlerFunc {
+ return func(c *gin.Context) {
+ began := time.Now()
+ r, err := func() (r interface{}, rerr error) {
+ defer dtmimp.P2E(&rerr)
+ return fn(c)
+ }()
+ var b = []byte{}
+ if resp, ok := r.(*resty.Response); ok { // 如果是response,则取出body直接处理
+ b = resp.Body()
+ } else if err == nil {
+ b, err = json.Marshal(r)
+ }
+
+ if err != nil {
+ logger.Errorf("%2dms 500 %s %s %s %s", time.Since(began).Milliseconds(), err.Error(), c.Request.Method, c.Request.RequestURI, string(b))
+ c.JSON(500, map[string]interface{}{"code": 500, "message": err.Error()})
+ } else {
+ logger.Infof("%2dms 200 %s %s %s", time.Since(began).Milliseconds(), c.Request.Method, c.Request.RequestURI, string(b))
+ c.Status(200)
+ c.Writer.Header().Add("Content-Type", "application/json")
+ _, err = c.Writer.Write(b)
+ dtmimp.E2P(err)
+ }
+ }
+}
+
+var (
+ rdb *redis.Client
+ once sync.Once
+)
+
+func RedisGet() *redis.Client {
+ once.Do(func() {
+ logger.Debugf("connecting to client redis")
+ rdb = redis.NewClient(&redis.Options{
+ Addr: "localhost:6379",
+ Username: "root",
+ Password: "",
+ })
+ })
+ return rdb
+}
+
+func SetRedisBothAccount(accountA int, accountB int) {
+ rd := RedisGet()
+ _, err := rd.Set(rd.Context(), getRedisAccountKey(TransOutUID), accountA, 0).Result()
+ dtmimp.E2P(err)
+ _, err = rd.Set(rd.Context(), getRedisAccountKey(TransInUID), accountB, 0).Result()
+ dtmimp.E2P(err)
+}
diff --git a/test/common_test.go b/test/common_test.go
new file mode 100644
index 0000000..2a9b917
--- /dev/null
+++ b/test/common_test.go
@@ -0,0 +1,48 @@
+package test
+
+import (
+ "testing"
+
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmgrpc"
+ "github.com/dtm-labs/dtm/dtmsvr/storage/sql"
+ "github.com/dtm-labs/dtm/dtmutil"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGeneralDB(t *testing.T) {
+ if conf.Store.IsDB() {
+ testSql(t)
+ testDbAlone(t)
+ }
+}
+
+func testSql(t *testing.T) {
+ conf := conf.Store.GetDBConf()
+ conf.Host = "127.0.0.1" // use a new host to trigger SetDBConn called
+ db := dtmutil.DbGet(conf, sql.SetDBConn)
+ err := func() (rerr error) {
+ defer dtmimp.P2E(&rerr)
+ db.Must().Exec("select a")
+ return nil
+ }()
+ assert.NotEqual(t, nil, err)
+}
+
+func testDbAlone(t *testing.T) {
+ db, err := dtmimp.StandaloneDB(conf.Store.GetDBConf())
+ assert.Nil(t, err)
+ _, err = dtmimp.DBExec(db, "select 1")
+ assert.Equal(t, nil, err)
+ _, err = dtmimp.DBExec(db, "")
+ assert.Equal(t, nil, err)
+ db.Close()
+ _, err = dtmimp.DBExec(db, "select 1")
+ assert.NotEqual(t, nil, err)
+}
+
+func TestMustGenGid(t *testing.T) {
+ dtmgrpc.MustGenGid(dtmutil.DefaultGrpcServer)
+ dtmcli.MustGenGid(dtmutil.DefaultHTTPServer)
+}
diff --git a/test/dtmsvr_test.go b/test/dtmsvr_test.go
index 52ac6ae..6078e5b 100644
--- a/test/dtmsvr_test.go
+++ b/test/dtmsvr_test.go
@@ -10,18 +10,16 @@ import (
"testing"
"time"
- "github.com/gin-gonic/gin"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmsvr"
+ "github.com/dtm-labs/dtm/dtmutil"
+ "github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmsvr"
- "github.com/yedf/dtm/examples"
)
-var DtmServer = examples.DtmHttpServer
-var Busi = examples.Busi
-var app *gin.Engine
+var DtmServer = dtmutil.DefaultHTTPServer
+var DtmGrpcServer = dtmutil.DefaultGrpcServer
+var Busi = busi.Busi
func getTransStatus(gid string) string {
return dtmsvr.GetTransGlobal(gid).Status
@@ -42,17 +40,17 @@ func assertSucceed(t *testing.T, gid string) {
}
func TestUpdateBranchAsync(t *testing.T) {
- if config.Store.Driver != "mysql" {
+ if conf.Store.Driver != "mysql" {
return
}
- common.Config.UpdateBranchSync = 0
+ conf.UpdateBranchSync = 0
saga := genSaga1(dtmimp.GetFuncName(), false, false)
- saga.SetOptions(&dtmcli.TransOptions{WaitResult: true})
+ saga.WaitResult = true
err := saga.Submit()
assert.Nil(t, err)
waitTransProcessed(saga.Gid)
time.Sleep(dtmsvr.UpdateBranchAsyncInterval)
assert.Equal(t, []string{StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))
assert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))
- common.Config.UpdateBranchSync = 1
+ conf.UpdateBranchSync = 1
}
diff --git a/test/examples_test.go b/test/examples_test.go
deleted file mode 100644
index e7c70cf..0000000
--- a/test/examples_test.go
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (c) 2021 yedf. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package test
-
-import (
- "testing"
-
- "github.com/yedf/dtm/examples"
-)
-
-func TestExamples(t *testing.T) {
- examples.QsStartSvr()
- for _, s := range examples.Samples {
- assertSucceed(t, s.Action())
- }
-}
diff --git a/test/main_test.go b/test/main_test.go
index 306a10c..69d29cf 100644
--- a/test/main_test.go
+++ b/test/main_test.go
@@ -11,11 +11,14 @@ import (
"testing"
"time"
- "github.com/gin-gonic/gin"
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmsvr"
- "github.com/yedf/dtm/examples"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/dtmgrpc"
+ "github.com/dtm-labs/dtm/dtmsvr"
+ "github.com/dtm-labs/dtm/dtmsvr/config"
+ "github.com/dtm-labs/dtm/dtmsvr/storage/registry"
+ "github.com/dtm-labs/dtm/test/busi"
+ "github.com/go-resty/resty/v2"
)
func exitIf(code int) {
@@ -25,38 +28,44 @@ func exitIf(code int) {
}
func TestMain(m *testing.M) {
- common.MustLoadConfig()
- dtmcli.SetCurrentDBType(common.Config.ExamplesDB.Driver)
+ config.MustLoadConfig("")
+ logger.InitLog("debug")
+ dtmcli.SetCurrentDBType(busi.BusiConf.Driver)
dtmsvr.TransProcessedTestChan = make(chan string, 1)
dtmsvr.NowForwardDuration = 0 * time.Second
dtmsvr.CronForwardDuration = 180 * time.Second
- common.Config.UpdateBranchSync = 1
+ conf.UpdateBranchSync = 1
+
+ dtmgrpc.AddUnaryInterceptor(busi.SetGrpcHeaderForHeadersYes)
+ dtmcli.GetRestyClient().OnBeforeRequest(busi.SetHttpHeaderForHeadersYes)
+ dtmcli.GetRestyClient().OnAfterResponse(func(c *resty.Client, resp *resty.Response) error { return nil })
- // 启动组件
- go dtmsvr.StartSvr()
- examples.GrpcStartup()
- app = examples.BaseAppStartup()
- app.POST(examples.BusiAPI+"/TccBSleepCancel", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
- return disorderHandler(c)
- }))
tenv := os.Getenv("TEST_STORE")
if tenv == "boltdb" {
- config.Store.Driver = "boltdb"
+ conf.Store.Driver = "boltdb"
} else if tenv == "mysql" {
- config.Store.Driver = "mysql"
- config.Store.Host = "localhost"
- config.Store.Port = 3306
- config.Store.User = "root"
- config.Store.Password = ""
+ conf.Store.Driver = "mysql"
+ conf.Store.Host = "localhost"
+ conf.Store.Port = 3306
+ conf.Store.User = "root"
+ conf.Store.Password = ""
} else {
- config.Store.Driver = "redis"
- config.Store.Host = "localhost"
- config.Store.User = ""
- config.Store.Password = ""
- config.Store.Port = 6379
+ conf.Store.Driver = "redis"
+ conf.Store.Host = "localhost"
+ conf.Store.User = ""
+ conf.Store.Password = ""
+ conf.Store.Port = 6379
}
+ registry.WaitStoreUp()
+
dtmsvr.PopulateDB(false)
- examples.PopulateDB(false)
- exitIf(m.Run())
+ go dtmsvr.StartSvr()
+ busi.PopulateDB(false)
+ _ = busi.Startup()
+ r := m.Run()
+ exitIf(r)
+ close(dtmsvr.TransProcessedTestChan)
+ gid, more := <-dtmsvr.TransProcessedTestChan
+ logger.FatalfIf(more, "extra gid: %s in test chan", gid)
}
diff --git a/test/msg_barrier_test.go b/test/msg_barrier_test.go
new file mode 100644
index 0000000..cd274fb
--- /dev/null
+++ b/test/msg_barrier_test.go
@@ -0,0 +1,105 @@
+package test
+
+import (
+ "database/sql"
+ "errors"
+ "reflect"
+ "testing"
+
+ "bou.ke/monkey"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/test/busi"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestMsgPrepareAndSubmit(t *testing.T) {
+ before := getBeforeBalances("mysql")
+ gid := dtmimp.GetFuncName()
+ req := busi.GenTransReq(30, false, false)
+ msg := dtmcli.NewMsg(DtmServer, gid).
+ Add(busi.Busi+"/SagaBTransIn", req)
+ err := msg.PrepareAndSubmit(Busi+"/QueryPreparedB", dbGet().ToSQLDB(), func(tx *sql.Tx) error {
+ return busi.SagaAdjustBalance(tx, busi.TransOutUID, -req.Amount, "SUCCESS")
+ })
+ assert.Nil(t, err)
+ waitTransProcessed(msg.Gid)
+ assert.Equal(t, []string{StatusSucceed}, getBranchesStatus(msg.Gid))
+ assert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))
+ assertNotSameBalance(t, before, "mysql")
+}
+
+func TestMsgPrepareAndSubmitBusiFailed(t *testing.T) {
+ before := getBeforeBalances("mysql")
+ gid := dtmimp.GetFuncName()
+ req := busi.GenTransReq(30, false, false)
+ msg := dtmcli.NewMsg(DtmServer, gid).
+ Add(busi.Busi+"/SagaBTransIn", req)
+ err := msg.PrepareAndSubmit(Busi+"/QueryPreparedB", dbGet().ToSQLDB(), func(tx *sql.Tx) error {
+ return errors.New("an error")
+ })
+ assert.Error(t, err)
+ assertSameBalance(t, before, "mysql")
+}
+
+func TestMsgPrepareAndSubmitPrepareFailed(t *testing.T) {
+ before := getBeforeBalances("mysql")
+ gid := dtmimp.GetFuncName()
+ req := busi.GenTransReq(30, false, false)
+ msg := dtmcli.NewMsg(DtmServer+"not-exists", gid).
+ Add(busi.Busi+"/SagaBTransIn", req)
+ err := msg.PrepareAndSubmit(Busi+"/QueryPreparedB", dbGet().ToSQLDB(), func(tx *sql.Tx) error {
+ return busi.SagaAdjustBalance(tx, busi.TransOutUID, -req.Amount, "SUCCESS")
+ })
+ assert.Error(t, err)
+ assertSameBalance(t, before, "mysql")
+}
+
+func TestMsgPrepareAndSubmitCommitFailed(t *testing.T) {
+ if conf.Store.IsDB() { // cannot patch tx.Commit, because Prepare also do Commit
+ return
+ }
+ before := getBeforeBalances("mysql")
+ gid := dtmimp.GetFuncName()
+ req := busi.GenTransReq(30, false, false)
+ msg := dtmcli.NewMsg(DtmServer, gid).
+ Add(busi.Busi+"/SagaBTransIn", req)
+ var g *monkey.PatchGuard
+ err := msg.PrepareAndSubmit(Busi+"/QueryPreparedB", dbGet().ToSQLDB(), func(tx *sql.Tx) error {
+ g = monkey.PatchInstanceMethod(reflect.TypeOf(tx), "Commit", func(tx *sql.Tx) error {
+ logger.Debugf("tx.Commit rollback and return error in test")
+ _ = tx.Rollback()
+ return errors.New("test error for patch")
+ })
+ return busi.SagaAdjustBalance(tx, busi.TransOutUID, -req.Amount, "SUCCESS")
+ })
+ g.Unpatch()
+ assert.Error(t, err)
+ cronTransOnceForwardNow(180)
+ assertSameBalance(t, before, "mysql")
+}
+
+func TestMsgPrepareAndSubmitCommitAfterFailed(t *testing.T) {
+ if conf.Store.IsDB() { // cannot patch tx.Commit, because Prepare also do Commit
+ return
+ }
+ before := getBeforeBalances("mysql")
+ gid := dtmimp.GetFuncName()
+ req := busi.GenTransReq(30, false, false)
+ msg := dtmcli.NewMsg(DtmServer, gid).
+ Add(busi.Busi+"/SagaBTransIn", req)
+ var guard *monkey.PatchGuard
+ err := msg.PrepareAndSubmit(Busi+"/QueryPreparedB", dbGet().ToSQLDB(), func(tx *sql.Tx) error {
+ err := busi.SagaAdjustBalance(tx, busi.TransOutUID, -req.Amount, "SUCCESS")
+ guard = monkey.PatchInstanceMethod(reflect.TypeOf(tx), "Commit", func(tx *sql.Tx) error {
+ guard.Unpatch()
+ _ = tx.Commit()
+ return errors.New("test error for patch")
+ })
+ return err
+ })
+ assert.Error(t, err)
+ cronTransOnceForwardNow(180)
+ assertNotSameBalance(t, before, "mysql")
+}
diff --git a/test/msg_grpc_barrier_test.go b/test/msg_grpc_barrier_test.go
new file mode 100644
index 0000000..50edd6c
--- /dev/null
+++ b/test/msg_grpc_barrier_test.go
@@ -0,0 +1,54 @@
+package test
+
+import (
+ "database/sql"
+ "errors"
+ "reflect"
+ "testing"
+
+ "bou.ke/monkey"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmgrpc"
+ "github.com/dtm-labs/dtm/test/busi"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestMsgGrpcPrepareAndSubmit(t *testing.T) {
+ before := getBeforeBalances("mysql")
+ gid := dtmimp.GetFuncName()
+ req := busi.GenBusiReq(30, false, false)
+ msg := dtmgrpc.NewMsgGrpc(DtmGrpcServer, gid).
+ Add(busi.BusiGrpc+"/busi.Busi/TransInBSaga", req)
+ err := msg.PrepareAndSubmit(busi.BusiGrpc+"/busi.Busi/QueryPreparedB", dbGet().ToSQLDB(), func(tx *sql.Tx) error {
+ return busi.SagaAdjustBalance(tx, busi.TransOutUID, -int(req.Amount), "SUCCESS")
+ })
+ assert.Nil(t, err)
+ waitTransProcessed(msg.Gid)
+ assert.Equal(t, []string{StatusSucceed}, getBranchesStatus(msg.Gid))
+ assert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))
+ assertNotSameBalance(t, before, "mysql")
+}
+
+func TestMsgGrpcPrepareAndSubmitCommitAfterFailed(t *testing.T) {
+ if conf.Store.IsDB() { // cannot patch tx.Commit, because Prepare also do Commit
+ return
+ }
+ before := getBeforeBalances("mysql")
+ gid := dtmimp.GetFuncName()
+ req := busi.GenBusiReq(30, false, false)
+ msg := dtmgrpc.NewMsgGrpc(DtmGrpcServer, gid).
+ Add(busi.BusiGrpc+"/busi.Busi/TransInBSaga", req)
+ var guard *monkey.PatchGuard
+ err := msg.PrepareAndSubmit(busi.BusiGrpc+"/busi.Busi/QueryPreparedB", dbGet().ToSQLDB(), func(tx *sql.Tx) error {
+ err := busi.SagaAdjustBalance(tx, busi.TransOutUID, -int(req.Amount), "SUCCESS")
+ guard = monkey.PatchInstanceMethod(reflect.TypeOf(tx), "Commit", func(tx *sql.Tx) error {
+ guard.Unpatch()
+ _ = tx.Commit()
+ return errors.New("test error for patch")
+ })
+ return err
+ })
+ assert.Error(t, err)
+ cronTransOnceForwardNow(180)
+ assertNotSameBalance(t, before, "mysql")
+}
diff --git a/test/msg_grpc_test.go b/test/msg_grpc_test.go
index 3ae048d..4701903 100644
--- a/test/msg_grpc_test.go
+++ b/test/msg_grpc_test.go
@@ -10,11 +10,12 @@ import (
"fmt"
"testing"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmgrpc"
+ "github.com/dtm-labs/dtm/dtmutil"
+ "github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmgrpc"
- "github.com/yedf/dtm/examples"
)
func TestMsgGrpcNormal(t *testing.T) {
@@ -30,14 +31,15 @@ func TestMsgGrpcTimeoutSuccess(t *testing.T) {
msg := genGrpcMsg(dtmimp.GetFuncName())
err := msg.Prepare("")
assert.Nil(t, err)
- examples.MainSwitch.CanSubmitResult.SetOnce(dtmcli.ResultOngoing)
+ busi.MainSwitch.QueryPreparedResult.SetOnce(dtmcli.ResultOngoing)
cronTransOnceForwardNow(180)
assert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))
- examples.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
+ busi.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
cronTransOnceForwardNow(180)
assert.Equal(t, StatusSubmitted, getTransStatus(msg.Gid))
assert.Equal(t, []string{StatusPrepared, StatusPrepared}, getBranchesStatus(msg.Gid))
- cronTransOnce()
+ g := cronTransOnce()
+ assert.Equal(t, msg.Gid, g)
assert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))
assert.Equal(t, []string{StatusSucceed, StatusSucceed}, getBranchesStatus(msg.Gid))
}
@@ -46,20 +48,20 @@ func TestMsgGrpcTimeoutFailed(t *testing.T) {
msg := genGrpcMsg(dtmimp.GetFuncName())
msg.Prepare("")
assert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))
- examples.MainSwitch.CanSubmitResult.SetOnce(dtmcli.ResultOngoing)
+ busi.MainSwitch.QueryPreparedResult.SetOnce(dtmcli.ResultOngoing)
cronTransOnceForwardNow(180)
assert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))
- examples.MainSwitch.CanSubmitResult.SetOnce(dtmcli.ResultFailure)
+ busi.MainSwitch.QueryPreparedResult.SetOnce(dtmcli.ResultFailure)
cronTransOnceForwardNow(180)
assert.Equal(t, StatusFailed, getTransStatus(msg.Gid))
assert.Equal(t, []string{StatusPrepared, StatusPrepared}, getBranchesStatus(msg.Gid))
}
func genGrpcMsg(gid string) *dtmgrpc.MsgGrpc {
- req := &examples.BusiReq{Amount: 30}
- msg := dtmgrpc.NewMsgGrpc(examples.DtmGrpcServer, gid).
- Add(examples.BusiGrpc+"/examples.Busi/TransOut", req).
- Add(examples.BusiGrpc+"/examples.Busi/TransIn", req)
- msg.QueryPrepared = fmt.Sprintf("%s/examples.Busi/CanSubmit", examples.BusiGrpc)
+ req := &busi.BusiReq{Amount: 30}
+ msg := dtmgrpc.NewMsgGrpc(dtmutil.DefaultGrpcServer, gid).
+ Add(busi.BusiGrpc+"/busi.Busi/TransOut", req).
+ Add(busi.BusiGrpc+"/busi.Busi/TransIn", req)
+ msg.QueryPrepared = fmt.Sprintf("%s/busi.Busi/QueryPrepared", busi.BusiGrpc)
return msg
}
diff --git a/test/msg_options_test.go b/test/msg_options_test.go
index 85e57ff..1f60810 100644
--- a/test/msg_options_test.go
+++ b/test/msg_options_test.go
@@ -9,16 +9,17 @@ package test
import (
"testing"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/examples"
)
func TestMsgOptionsTimeout(t *testing.T) {
msg := genMsg(dtmimp.GetFuncName())
msg.Prepare("")
- cronTransOnce()
+ g := cronTransOnce()
+ assert.Equal(t, msg.Gid, g)
assert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))
cronTransOnceForwardNow(60)
assert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))
@@ -28,7 +29,8 @@ func TestMsgOptionsTimeoutCustom(t *testing.T) {
msg := genMsg(dtmimp.GetFuncName())
msg.TimeoutToFail = 120
msg.Prepare("")
- cronTransOnce()
+ g := cronTransOnce()
+ assert.Equal(t, msg.Gid, g)
assert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))
cronTransOnceForwardNow(60)
assert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))
@@ -40,11 +42,12 @@ func TestMsgOptionsTimeoutFailed(t *testing.T) {
msg := genMsg(dtmimp.GetFuncName())
msg.TimeoutToFail = 120
msg.Prepare("")
- cronTransOnce()
+ g := cronTransOnce()
+ assert.Equal(t, msg.Gid, g)
assert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))
cronTransOnceForwardNow(60)
assert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))
- examples.MainSwitch.CanSubmitResult.SetOnce(dtmcli.ResultFailure)
+ busi.MainSwitch.QueryPreparedResult.SetOnce(dtmcli.ResultFailure)
cronTransOnceForwardNow(180)
assert.Equal(t, StatusFailed, getTransStatus(msg.Gid))
}
diff --git a/test/msg_test.go b/test/msg_test.go
index 1fd9c4f..8f26ad8 100644
--- a/test/msg_test.go
+++ b/test/msg_test.go
@@ -9,10 +9,11 @@ package test
import (
"testing"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmutil"
+ "github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/examples"
)
func TestMsgNormal(t *testing.T) {
@@ -28,13 +29,14 @@ func TestMsgTimeoutSuccess(t *testing.T) {
msg := genMsg(dtmimp.GetFuncName())
msg.Prepare("")
assert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))
- examples.MainSwitch.CanSubmitResult.SetOnce(dtmcli.ResultOngoing)
+ busi.MainSwitch.QueryPreparedResult.SetOnce(dtmcli.ResultOngoing)
cronTransOnceForwardNow(180)
assert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))
- examples.MainSwitch.TransInResult.SetOnce(dtmcli.ResultOngoing)
+ busi.MainSwitch.TransInResult.SetOnce(dtmcli.ResultOngoing)
cronTransOnceForwardNow(180)
assert.Equal(t, StatusSubmitted, getTransStatus(msg.Gid))
- cronTransOnce()
+ g := cronTransOnce()
+ assert.Equal(t, msg.Gid, g)
assert.Equal(t, []string{StatusSucceed, StatusSucceed}, getBranchesStatus(msg.Gid))
assert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))
}
@@ -43,10 +45,10 @@ func TestMsgTimeoutFailed(t *testing.T) {
msg := genMsg(dtmimp.GetFuncName())
msg.Prepare("")
assert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))
- examples.MainSwitch.CanSubmitResult.SetOnce(dtmcli.ResultOngoing)
- cronTransOnceForwardNow(180)
+ busi.MainSwitch.QueryPreparedResult.SetOnce(dtmcli.ResultOngoing)
+ cronTransOnceForwardNow(360)
assert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))
- examples.MainSwitch.CanSubmitResult.SetOnce(dtmcli.ResultFailure)
+ busi.MainSwitch.QueryPreparedResult.SetOnce(dtmcli.ResultFailure)
cronTransOnceForwardNow(180)
assert.Equal(t, []string{StatusPrepared, StatusPrepared}, getBranchesStatus(msg.Gid))
assert.Equal(t, StatusFailed, getTransStatus(msg.Gid))
@@ -59,16 +61,16 @@ func TestMsgAbnormal(t *testing.T) {
assert.Nil(t, err)
err = msg.Submit()
assert.Nil(t, err)
-
+ waitTransProcessed(msg.Gid)
err = msg.Prepare("")
assert.Error(t, err)
}
func genMsg(gid string) *dtmcli.Msg {
- req := examples.GenTransReq(30, false, false)
- msg := dtmcli.NewMsg(examples.DtmHttpServer, gid).
- Add(examples.Busi+"/TransOut", &req).
- Add(examples.Busi+"/TransIn", &req)
- msg.QueryPrepared = examples.Busi + "/CanSubmit"
+ req := busi.GenTransReq(30, false, false)
+ msg := dtmcli.NewMsg(dtmutil.DefaultHTTPServer, gid).
+ Add(busi.Busi+"/TransOut", &req).
+ Add(busi.Busi+"/TransIn", &req)
+ msg.QueryPrepared = busi.Busi + "/QueryPrepared"
return msg
}
diff --git a/test/saga_barrier_redis_test.go b/test/saga_barrier_redis_test.go
new file mode 100644
index 0000000..c37dd29
--- /dev/null
+++ b/test/saga_barrier_redis_test.go
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2021 yedf. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+package test
+
+import (
+ "testing"
+
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/test/busi"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestSagaBarrierRedisNormal(t *testing.T) {
+ busi.SetRedisBothAccount(100, 100)
+ before := getBeforeBalances("redis")
+ saga := genSagaBarrierRedis(dtmimp.GetFuncName())
+ err := saga.Submit()
+ assert.Nil(t, err)
+ waitTransProcessed(saga.Gid)
+ assert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))
+ assert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))
+ assertNotSameBalance(t, before, "redis")
+}
+
+func TestSagaBarrierRedisRollback(t *testing.T) {
+ busi.SetRedisBothAccount(20, 20)
+ before := getBeforeBalances("redis")
+ saga := genSagaBarrierRedis(dtmimp.GetFuncName())
+ err := saga.Submit()
+ assert.Nil(t, err)
+ waitTransProcessed(saga.Gid)
+ assert.Equal(t, StatusFailed, getTransStatus(saga.Gid))
+ assert.Equal(t, []string{StatusSucceed, StatusSucceed, StatusSucceed, StatusFailed}, getBranchesStatus(saga.Gid))
+ assertSameBalance(t, before, "redis")
+}
+
+func genSagaBarrierRedis(gid string) *dtmcli.Saga {
+ req := busi.GenTransReq(30, false, false)
+ req.Store = "redis"
+ return dtmcli.NewSaga(DtmServer, gid).
+ Add(Busi+"/SagaRedisTransIn", Busi+"/SagaRedisTransInCom", req).
+ Add(Busi+"/SagaRedisTransOut", Busi+"/SagaRedisTransOutCom", req)
+}
diff --git a/test/saga_barrier_test.go b/test/saga_barrier_test.go
index 6d8842c..ecb2194 100644
--- a/test/saga_barrier_test.go
+++ b/test/saga_barrier_test.go
@@ -9,10 +9,10 @@ package test
import (
"testing"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/examples"
)
func TestSagaBarrierNormal(t *testing.T) {
@@ -34,7 +34,7 @@ func TestSagaBarrierRollback(t *testing.T) {
}
func genSagaBarrier(gid string, outFailed, inFailed bool) *dtmcli.Saga {
- req := examples.GenTransReq(30, outFailed, inFailed)
+ req := busi.GenTransReq(30, outFailed, inFailed)
return dtmcli.NewSaga(DtmServer, gid).
Add(Busi+"/SagaBTransOut", Busi+"/SagaBTransOutCompensate", req).
Add(Busi+"/SagaBTransIn", Busi+"/SagaBTransInCompensate", req)
diff --git a/test/saga_compatible_test.go b/test/saga_compatible_test.go
index c7e29f9..98ebf75 100644
--- a/test/saga_compatible_test.go
+++ b/test/saga_compatible_test.go
@@ -10,16 +10,17 @@ import (
"fmt"
"testing"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmutil"
+ "github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/examples"
)
func TestSagaCompatibleNormal(t *testing.T) { // compatible with old http, which put payload in steps.data
gid := dtmimp.GetFuncName()
body := fmt.Sprintf(`{"gid":"%s","trans_type":"saga","steps":[{"action":"%s/TransOut","compensate":"%s/TransOutRevert","data":"{\"amount\":30,\"transInResult\":\"SUCCESS\",\"transOutResult\":\"SUCCESS\"}"},{"action":"%s/TransIn","compensate":"%s/TransInRevert","data":"{\"amount\":30,\"transInResult\":\"SUCCESS\",\"transOutResult\":\"SUCCESS\"}"}]}`,
- gid, examples.Busi, examples.Busi, examples.Busi, examples.Busi)
- dtmimp.RestyClient.R().SetBody(body).Post(fmt.Sprintf("%s/submit", examples.DtmHttpServer))
+ gid, busi.Busi, busi.Busi, busi.Busi, busi.Busi)
+ dtmimp.RestyClient.R().SetBody(body).Post(fmt.Sprintf("%s/submit", dtmutil.DefaultHTTPServer))
waitTransProcessed(gid)
assert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(gid))
assert.Equal(t, StatusSucceed, getTransStatus(gid))
diff --git a/test/saga_concurrent_test.go b/test/saga_concurrent_test.go
index dda6aaf..5c33770 100644
--- a/test/saga_concurrent_test.go
+++ b/test/saga_concurrent_test.go
@@ -9,10 +9,10 @@ package test
import (
"testing"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/examples"
)
func genSagaCon(gid string, outFailed bool, inFailed bool) *dtmcli.Saga {
@@ -29,12 +29,13 @@ func TestSagaConNormal(t *testing.T) {
func TestSagaConRollbackNormal(t *testing.T) {
sagaCon := genSagaCon(dtmimp.GetFuncName(), true, false)
- examples.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing)
+ busi.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing)
err := sagaCon.Submit()
assert.Nil(t, err)
waitTransProcessed(sagaCon.Gid)
assert.Equal(t, StatusAborting, getTransStatus(sagaCon.Gid))
- cronTransOnce()
+ g := cronTransOnce()
+ assert.Equal(t, sagaCon.Gid, g)
assert.Equal(t, StatusFailed, getTransStatus(sagaCon.Gid))
// TODO should fix this
// assert.Equal(t, []string{StatusSucceed, StatusFailed, StatusSucceed, StatusSucceed}, getBranchesStatus(sagaCon.Gid))
@@ -50,15 +51,25 @@ func TestSagaConRollbackOrder(t *testing.T) {
assert.Equal(t, []string{StatusSucceed, StatusFailed, StatusPrepared, StatusPrepared}, getBranchesStatus(sagaCon.Gid))
}
+func TestSagaConRollbackOrder2(t *testing.T) {
+ sagaCon := genSagaCon(dtmimp.GetFuncName(), false, true)
+ sagaCon.AddBranchOrder(1, []int{0})
+ err := sagaCon.Submit()
+ assert.Nil(t, err)
+ waitTransProcessed(sagaCon.Gid)
+ assert.Equal(t, StatusFailed, getTransStatus(sagaCon.Gid))
+ assert.Equal(t, []string{StatusSucceed, StatusSucceed, StatusSucceed, StatusFailed}, getBranchesStatus(sagaCon.Gid))
+}
func TestSagaConCommittedOngoing(t *testing.T) {
sagaCon := genSagaCon(dtmimp.GetFuncName(), false, false)
- examples.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
+ busi.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
sagaCon.Submit()
waitTransProcessed(sagaCon.Gid)
assert.Equal(t, []string{StatusPrepared, StatusPrepared, StatusPrepared, StatusSucceed}, getBranchesStatus(sagaCon.Gid))
assert.Equal(t, StatusSubmitted, getTransStatus(sagaCon.Gid))
- cronTransOnce()
+ g := cronTransOnce()
+ assert.Equal(t, sagaCon.Gid, g)
assert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(sagaCon.Gid))
assert.Equal(t, StatusSucceed, getTransStatus(sagaCon.Gid))
}
diff --git a/test/saga_cover_test.go b/test/saga_cover_test.go
new file mode 100644
index 0000000..500f424
--- /dev/null
+++ b/test/saga_cover_test.go
@@ -0,0 +1,11 @@
+package test
+
+import (
+ "testing"
+
+ "github.com/dtm-labs/dtm/dtmcli"
+)
+
+func TestSagaCover(t *testing.T) {
+ dtmcli.SetPassthroughHeaders([]string{})
+}
diff --git a/test/saga_grpc_barrier_test.go b/test/saga_grpc_barrier_test.go
index 589eb32..ebb5e8a 100644
--- a/test/saga_grpc_barrier_test.go
+++ b/test/saga_grpc_barrier_test.go
@@ -9,10 +9,11 @@ package test
import (
"testing"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmgrpc"
+ "github.com/dtm-labs/dtm/dtmutil"
+ "github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmgrpc"
- "github.com/yedf/dtm/examples"
)
func TestSagaGrpcBarrierNormal(t *testing.T) {
@@ -34,9 +35,9 @@ func TestSagaGrpcBarrierRollback(t *testing.T) {
}
func genSagaGrpcBarrier(gid string, outFailed bool, inFailed bool) *dtmgrpc.SagaGrpc {
- saga := dtmgrpc.NewSagaGrpc(examples.DtmGrpcServer, gid)
- req := examples.GenBusiReq(30, outFailed, inFailed)
- saga.Add(examples.BusiGrpc+"/examples.Busi/TransOutBSaga", examples.BusiGrpc+"/examples.Busi/TransOutRevertBSaga", req)
- saga.Add(examples.BusiGrpc+"/examples.Busi/TransInBSaga", examples.BusiGrpc+"/examples.Busi/TransInRevertBSaga", req)
+ saga := dtmgrpc.NewSagaGrpc(dtmutil.DefaultGrpcServer, gid)
+ req := busi.GenBusiReq(30, outFailed, inFailed)
+ saga.Add(busi.BusiGrpc+"/busi.Busi/TransOutBSaga", busi.BusiGrpc+"/busi.Busi/TransOutRevertBSaga", req)
+ saga.Add(busi.BusiGrpc+"/busi.Busi/TransInBSaga", busi.BusiGrpc+"/busi.Busi/TransInRevertBSaga", req)
return saga
}
diff --git a/test/saga_grpc_test.go b/test/saga_grpc_test.go
index ca06f07..74fd827 100644
--- a/test/saga_grpc_test.go
+++ b/test/saga_grpc_test.go
@@ -9,11 +9,12 @@ package test
import (
"testing"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmgrpc"
+ "github.com/dtm-labs/dtm/dtmutil"
+ "github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmgrpc"
- "github.com/yedf/dtm/examples"
)
func TestSagaGrpcNormal(t *testing.T) {
@@ -26,11 +27,12 @@ func TestSagaGrpcNormal(t *testing.T) {
func TestSagaGrpcRollback(t *testing.T) {
saga := genSagaGrpc(dtmimp.GetFuncName(), false, true)
- examples.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing)
+ busi.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing)
saga.Submit()
waitTransProcessed(saga.Gid)
assert.Equal(t, StatusAborting, getTransStatus(saga.Gid))
- cronTransOnce()
+ g := cronTransOnce()
+ assert.Equal(t, saga.Gid, g)
assert.Equal(t, StatusFailed, getTransStatus(saga.Gid))
assert.Equal(t, []string{StatusSucceed, StatusSucceed, StatusSucceed, StatusFailed}, getBranchesStatus(saga.Gid))
}
@@ -56,29 +58,107 @@ func TestSagaGrpcCurrentOrder(t *testing.T) {
func TestSagaGrpcCommittedOngoing(t *testing.T) {
saga := genSagaGrpc(dtmimp.GetFuncName(), false, false)
- examples.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
+ busi.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
saga.Submit()
waitTransProcessed(saga.Gid)
assert.Equal(t, StatusSubmitted, getTransStatus(saga.Gid))
assert.Equal(t, []string{StatusPrepared, StatusPrepared, StatusPrepared, StatusPrepared}, getBranchesStatus(saga.Gid))
- cronTransOnce()
+ g := cronTransOnce()
+ assert.Equal(t, saga.Gid, g)
assert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))
assert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))
}
func TestSagaGrpcNormalWait(t *testing.T) {
saga := genSagaGrpc(dtmimp.GetFuncName(), false, false)
- saga.SetOptions(&dtmcli.TransOptions{WaitResult: true})
+ saga.WaitResult = true
saga.Submit()
assert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))
assert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))
waitTransProcessed(saga.Gid)
}
+func TestSagaGrpcEmptyUrl(t *testing.T) {
+ saga := dtmgrpc.NewSagaGrpc(dtmutil.DefaultGrpcServer, dtmimp.GetFuncName())
+ req := busi.GenBusiReq(30, false, false)
+ saga.Add(busi.BusiGrpc+"/busi.Busi/TransOut", busi.BusiGrpc+"/busi.Busi/TransOutRevert", req)
+ saga.Add("", busi.BusiGrpc+"/busi.Busi/TransInRevert", req)
+ saga.Submit()
+ waitTransProcessed(saga.Gid)
+ assert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))
+ assert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))
+}
+
func genSagaGrpc(gid string, outFailed bool, inFailed bool) *dtmgrpc.SagaGrpc {
- saga := dtmgrpc.NewSagaGrpc(examples.DtmGrpcServer, gid)
- req := examples.GenBusiReq(30, outFailed, inFailed)
- saga.Add(examples.BusiGrpc+"/examples.Busi/TransOut", examples.BusiGrpc+"/examples.Busi/TransOutRevert", req)
- saga.Add(examples.BusiGrpc+"/examples.Busi/TransIn", examples.BusiGrpc+"/examples.Busi/TransInRevert", req)
+ saga := dtmgrpc.NewSagaGrpc(dtmutil.DefaultGrpcServer, gid)
+ req := busi.GenBusiReq(30, outFailed, inFailed)
+ saga.Add(busi.BusiGrpc+"/busi.Busi/TransOut", busi.BusiGrpc+"/busi.Busi/TransOutRevert", req)
+ saga.Add(busi.BusiGrpc+"/busi.Busi/TransIn", busi.BusiGrpc+"/busi.Busi/TransInRevert", req)
return saga
}
+
+func TestSagaGrpcPassthroughHeadersYes(t *testing.T) {
+ gidYes := dtmimp.GetFuncName()
+ sagaYes := dtmgrpc.NewSagaGrpc(dtmutil.DefaultGrpcServer, gidYes)
+ sagaYes.WaitResult = true
+ sagaYes.PassthroughHeaders = []string{"test_header"}
+ sagaYes.Add(busi.BusiGrpc+"/busi.Busi/TransOutHeaderYes", "", nil)
+ err := sagaYes.Submit()
+ assert.Nil(t, err)
+ waitTransProcessed(gidYes)
+}
+
+func TestSagaGrpcCronPassthroughHeadersYes(t *testing.T) {
+ gidYes := dtmimp.GetFuncName()
+ sagaYes := dtmgrpc.NewSagaGrpc(dtmutil.DefaultGrpcServer, gidYes)
+ sagaYes.PassthroughHeaders = []string{"test_header"}
+ sagaYes.Add(busi.BusiGrpc+"/busi.Busi/TransOutHeaderYes", "", nil)
+ busi.MainSwitch.TransOutResult.SetOnce("ONGOING")
+ err := sagaYes.Submit()
+ assert.Nil(t, err)
+ waitTransProcessed(gidYes)
+ assert.Equal(t, StatusSubmitted, getTransStatus(gidYes))
+ g := cronTransOnce()
+ assert.Equal(t, gidYes, g)
+ assert.Equal(t, StatusSucceed, getTransStatus(gidYes))
+}
+
+func TestSagaGrpcPassthroughHeadersNo(t *testing.T) {
+ gidNo := dtmimp.GetFuncName()
+ sagaNo := dtmgrpc.NewSagaGrpc(dtmutil.DefaultGrpcServer, gidNo)
+ sagaNo.WaitResult = true
+ sagaNo.Add(busi.BusiGrpc+"/busi.Busi/TransOutHeaderNo", "", nil)
+ err := sagaNo.Submit()
+ assert.Nil(t, err)
+ waitTransProcessed(gidNo)
+}
+
+func TestSagaGrpcHeaders(t *testing.T) {
+ gidYes := dtmimp.GetFuncName()
+ sagaYes := dtmgrpc.NewSagaGrpc(dtmutil.DefaultGrpcServer, gidYes).
+ Add(busi.BusiGrpc+"/busi.Busi/TransOutHeaderYes", "", nil)
+ sagaYes.BranchHeaders = map[string]string{
+ "test_header": "test",
+ }
+ sagaYes.WaitResult = true
+ err := sagaYes.Submit()
+ assert.Nil(t, err)
+ waitTransProcessed(gidYes)
+}
+
+func TestSagaGrpcCronHeaders(t *testing.T) {
+ gidYes := dtmimp.GetFuncName()
+ sagaYes := dtmgrpc.NewSagaGrpc(dtmutil.DefaultGrpcServer, gidYes)
+ sagaYes.BranchHeaders = map[string]string{
+ "test_header": "test",
+ }
+ sagaYes.Add(busi.BusiGrpc+"/busi.Busi/TransOutHeaderYes", "", nil)
+ busi.MainSwitch.TransOutResult.SetOnce("ONGOING")
+ err := sagaYes.Submit()
+ assert.Nil(t, err)
+ waitTransProcessed(gidYes)
+ assert.Equal(t, StatusSubmitted, getTransStatus(gidYes))
+ g := cronTransOnce()
+ assert.Equal(t, gidYes, g)
+ assert.Equal(t, StatusSucceed, getTransStatus(gidYes))
+}
diff --git a/test/saga_options_test.go b/test/saga_options_test.go
index 7a11fcf..783823c 100644
--- a/test/saga_options_test.go
+++ b/test/saga_options_test.go
@@ -9,20 +9,22 @@ package test
import (
"testing"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmutil"
+ "github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/examples"
)
func TestSagaOptionsRetryOngoing(t *testing.T) {
saga := genSaga1(dtmimp.GetFuncName(), false, false)
saga.RetryInterval = 150 // CronForwardDuration is larger than RetryInterval
- examples.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
+ busi.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
err := saga.Submit()
assert.Nil(t, err)
waitTransProcessed(saga.Gid)
- cronTransOnce()
+ g := cronTransOnce()
+ assert.Equal(t, saga.Gid, g)
assert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))
assert.Equal(t, []string{StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))
}
@@ -30,14 +32,16 @@ func TestSagaOptionsRetryOngoing(t *testing.T) {
func TestSagaOptionsRetryError(t *testing.T) {
saga := genSaga1(dtmimp.GetFuncName(), false, false)
saga.RetryInterval = 150 // CronForwardDuration is less than 2*RetryInterval
- examples.MainSwitch.TransOutResult.SetOnce("ERROR")
+ busi.MainSwitch.TransOutResult.SetOnce("ERROR")
err := saga.Submit()
assert.Nil(t, err)
waitTransProcessed(saga.Gid)
- cronTransOnce()
assert.Equal(t, StatusSubmitted, getTransStatus(saga.Gid))
assert.Equal(t, []string{StatusPrepared, StatusPrepared}, getBranchesStatus(saga.Gid))
- cronTransOnceForwardCron(360)
+ g := cronTransOnce()
+ assert.Equal(t, "", g)
+ g = cronTransOnceForwardCron(360)
+ assert.Equal(t, saga.Gid, g)
assert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))
assert.Equal(t, []string{StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))
}
@@ -45,7 +49,7 @@ func TestSagaOptionsRetryError(t *testing.T) {
func TestSagaOptionsTimeout(t *testing.T) {
saga := genSaga(dtmimp.GetFuncName(), false, false)
saga.TimeoutToFail = 1800
- examples.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
+ busi.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
saga.Submit()
waitTransProcessed(saga.Gid)
assert.Equal(t, StatusSubmitted, getTransStatus(saga.Gid))
@@ -55,7 +59,7 @@ func TestSagaOptionsTimeout(t *testing.T) {
func TestSagaOptionsNormalWait(t *testing.T) {
saga := genSaga(dtmimp.GetFuncName(), false, false)
- saga.SetOptions(&dtmcli.TransOptions{WaitResult: true})
+ saga.WaitResult = true
err := saga.Submit()
assert.Nil(t, err)
assert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))
@@ -65,24 +69,91 @@ func TestSagaOptionsNormalWait(t *testing.T) {
func TestSagaOptionsCommittedOngoingWait(t *testing.T) {
saga := genSaga(dtmimp.GetFuncName(), false, false)
- examples.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
- saga.SetOptions(&dtmcli.TransOptions{WaitResult: true})
+ busi.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
+ saga.WaitResult = true
err := saga.Submit()
assert.Error(t, err)
assert.Equal(t, []string{StatusPrepared, StatusPrepared, StatusPrepared, StatusPrepared}, getBranchesStatus(saga.Gid))
assert.Equal(t, StatusSubmitted, getTransStatus(saga.Gid))
waitTransProcessed(saga.Gid)
- cronTransOnce()
+ g := cronTransOnce()
+ assert.Equal(t, saga.Gid, g)
assert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))
assert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))
}
func TestSagaOptionsRollbackWait(t *testing.T) {
saga := genSaga(dtmimp.GetFuncName(), false, true)
- saga.SetOptions(&dtmcli.TransOptions{WaitResult: true})
+ saga.WaitResult = true
err := saga.Submit()
assert.Error(t, err)
waitTransProcessed(saga.Gid)
assert.Equal(t, StatusFailed, getTransStatus(saga.Gid))
assert.Equal(t, []string{StatusSucceed, StatusSucceed, StatusSucceed, StatusFailed}, getBranchesStatus(saga.Gid))
}
+
+func TestSagaPassthroughHeadersYes(t *testing.T) {
+ gidYes := dtmimp.GetFuncName()
+ sagaYes := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, gidYes)
+ sagaYes.WaitResult = true
+ sagaYes.PassthroughHeaders = []string{"test_header"}
+ sagaYes.Add(busi.Busi+"/TransOutHeaderYes", "", nil)
+ err := sagaYes.Submit()
+ assert.Nil(t, err)
+ waitTransProcessed(gidYes)
+}
+
+func TestSagaCronPassthroughHeadersYes(t *testing.T) {
+ gidYes := dtmimp.GetFuncName()
+ sagaYes := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, gidYes)
+ sagaYes.PassthroughHeaders = []string{"test_header"}
+ sagaYes.Add(busi.Busi+"/TransOutHeaderYes", "", nil)
+ busi.MainSwitch.TransOutResult.SetOnce("ONGOING")
+ err := sagaYes.Submit()
+ assert.Nil(t, err)
+ waitTransProcessed(gidYes)
+ assert.Equal(t, StatusSubmitted, getTransStatus(gidYes))
+ g := cronTransOnce()
+ assert.Equal(t, gidYes, g)
+ assert.Equal(t, StatusSucceed, getTransStatus(gidYes))
+}
+
+func TestSagaPassthroughHeadersNo(t *testing.T) {
+ gidNo := dtmimp.GetFuncName()
+ sagaNo := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, gidNo)
+ sagaNo.WaitResult = true
+ sagaNo.Add(busi.Busi+"/TransOutHeaderNo", "", nil)
+ err := sagaNo.Submit()
+ assert.Nil(t, err)
+ waitTransProcessed(gidNo)
+}
+
+func TestSagaHeaders(t *testing.T) {
+ gidYes := dtmimp.GetFuncName()
+ sagaYes := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, gidYes)
+ sagaYes.BranchHeaders = map[string]string{
+ "test_header": "test",
+ }
+ sagaYes.WaitResult = true
+ sagaYes.Add(busi.Busi+"/TransOutHeaderYes", "", nil)
+ err := sagaYes.Submit()
+ assert.Nil(t, err)
+ waitTransProcessed(gidYes)
+}
+
+func TestSagaHeadersYes1(t *testing.T) {
+ gidYes := dtmimp.GetFuncName()
+ sagaYes := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, gidYes)
+ sagaYes.BranchHeaders = map[string]string{
+ "test_header": "test",
+ }
+ sagaYes.Add(busi.Busi+"/TransOutHeaderYes", "", nil)
+ busi.MainSwitch.TransOutResult.SetOnce("ONGOING")
+ err := sagaYes.Submit()
+ assert.Nil(t, err)
+ waitTransProcessed(gidYes)
+ assert.Equal(t, StatusSubmitted, getTransStatus(gidYes))
+ g := cronTransOnce()
+ assert.Equal(t, gidYes, g)
+ assert.Equal(t, StatusSucceed, getTransStatus(gidYes))
+}
diff --git a/test/saga_test.go b/test/saga_test.go
index 0e42b05..b7afd3a 100644
--- a/test/saga_test.go
+++ b/test/saga_test.go
@@ -9,10 +9,11 @@ package test
import (
"testing"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmutil"
+ "github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/examples"
)
func TestSagaNormal(t *testing.T) {
@@ -23,34 +24,47 @@ func TestSagaNormal(t *testing.T) {
assert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))
}
+func TestSagaRollback(t *testing.T) {
+ saga := genSaga(dtmimp.GetFuncName(), false, true)
+ err := saga.Submit()
+ assert.Nil(t, err)
+ waitTransProcessed(saga.Gid)
+ assert.Equal(t, []string{StatusSucceed, StatusSucceed, StatusSucceed, StatusFailed}, getBranchesStatus(saga.Gid))
+ assert.Equal(t, StatusFailed, getTransStatus(saga.Gid))
+}
+
func TestSagaOngoingSucceed(t *testing.T) {
saga := genSaga(dtmimp.GetFuncName(), false, false)
- examples.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
+ busi.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
saga.Submit()
waitTransProcessed(saga.Gid)
assert.Equal(t, []string{StatusPrepared, StatusPrepared, StatusPrepared, StatusPrepared}, getBranchesStatus(saga.Gid))
assert.Equal(t, StatusSubmitted, getTransStatus(saga.Gid))
- cronTransOnce()
+ g := cronTransOnce()
+ assert.Equal(t, saga.Gid, g)
assert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))
assert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))
}
func TestSagaFailed(t *testing.T) {
saga := genSaga(dtmimp.GetFuncName(), false, true)
- examples.MainSwitch.TransOutRevertResult.SetOnce("ERROR")
+ busi.MainSwitch.TransOutRevertResult.SetOnce("ERROR")
err := saga.Submit()
assert.Nil(t, err)
waitTransProcessed(saga.Gid)
assert.Equal(t, StatusAborting, getTransStatus(saga.Gid))
- cronTransOnce()
+ g := cronTransOnce()
+ assert.Equal(t, saga.Gid, g)
assert.Equal(t, StatusFailed, getTransStatus(saga.Gid))
assert.Equal(t, []string{StatusSucceed, StatusSucceed, StatusSucceed, StatusFailed}, getBranchesStatus(saga.Gid))
}
func TestSagaAbnormal(t *testing.T) {
saga := genSaga(dtmimp.GetFuncName(), false, false)
+ busi.MainSwitch.TransOutResult.SetOnce("ONGOING")
err := saga.Submit()
assert.Nil(t, err)
+ waitTransProcessed(saga.Gid)
err = saga.Submit() // submit twice, ignored
assert.Nil(t, err)
waitTransProcessed(saga.Gid)
@@ -58,17 +72,28 @@ func TestSagaAbnormal(t *testing.T) {
assert.Error(t, err) // a succeed trans can't accept submit
}
+func TestSagaEmptyUrl(t *testing.T) {
+ saga := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, dtmimp.GetFuncName())
+ req := busi.GenTransReq(30, false, false)
+ saga.Add(busi.Busi+"/TransOut", "", &req)
+ saga.Add("", "", &req)
+ saga.Submit()
+ waitTransProcessed(saga.Gid)
+ assert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))
+ assert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))
+}
+
func genSaga(gid string, outFailed bool, inFailed bool) *dtmcli.Saga {
- saga := dtmcli.NewSaga(examples.DtmHttpServer, gid)
- req := examples.GenTransReq(30, outFailed, inFailed)
- saga.Add(examples.Busi+"/TransOut", examples.Busi+"/TransOutRevert", &req)
- saga.Add(examples.Busi+"/TransIn", examples.Busi+"/TransInRevert", &req)
+ saga := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, gid)
+ req := busi.GenTransReq(30, outFailed, inFailed)
+ saga.Add(busi.Busi+"/TransOut", busi.Busi+"/TransOutRevert", &req)
+ saga.Add(busi.Busi+"/TransIn", busi.Busi+"/TransInRevert", &req)
return saga
}
func genSaga1(gid string, outFailed bool, inFailed bool) *dtmcli.Saga {
- saga := dtmcli.NewSaga(examples.DtmHttpServer, gid)
- req := examples.GenTransReq(30, outFailed, inFailed)
- saga.Add(examples.Busi+"/TransOut", examples.Busi+"/TransOutRevert", &req)
+ saga := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, gid)
+ req := busi.GenTransReq(30, outFailed, inFailed)
+ saga.Add(busi.Busi+"/TransOut", busi.Busi+"/TransOutRevert", &req)
return saga
}
diff --git a/test/store_test.go b/test/store_test.go
index 49549c0..b711c76 100644
--- a/test/store_test.go
+++ b/test/store_test.go
@@ -6,9 +6,9 @@ import (
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmsvr/storage"
- "github.com/yedf/dtm/dtmsvr/storage/registry"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmsvr/storage"
+ "github.com/dtm-labs/dtm/dtmsvr/storage/registry"
)
func initTransGlobal(gid string) (*storage.TransGlobalStore, storage.Store) {
@@ -70,31 +70,27 @@ func TestStoreLockTrans(t *testing.T) {
gid := dtmimp.GetFuncName()
g, s := initTransGlobal(gid)
- g2 := s.LockOneGlobalTrans(2 * time.Duration(config.RetryInterval) * time.Second)
+ g2 := s.LockOneGlobalTrans(2 * time.Duration(conf.RetryInterval) * time.Second)
assert.NotNil(t, g2)
assert.Equal(t, gid, g2.Gid)
- s.TouchCronTime(g, 3*config.RetryInterval)
- g2 = s.LockOneGlobalTrans(2 * time.Duration(config.RetryInterval) * time.Second)
+ s.TouchCronTime(g, 3*conf.RetryInterval)
+ g2 = s.LockOneGlobalTrans(2 * time.Duration(conf.RetryInterval) * time.Second)
assert.Nil(t, g2)
- s.TouchCronTime(g, 1*config.RetryInterval)
- g2 = s.LockOneGlobalTrans(2 * time.Duration(config.RetryInterval) * time.Second)
+ s.TouchCronTime(g, 1*conf.RetryInterval)
+ g2 = s.LockOneGlobalTrans(2 * time.Duration(conf.RetryInterval) * time.Second)
assert.NotNil(t, g2)
assert.Equal(t, gid, g2.Gid)
s.ChangeGlobalStatus(g, "succeed", []string{}, true)
- g2 = s.LockOneGlobalTrans(2 * time.Duration(config.RetryInterval) * time.Second)
+ g2 = s.LockOneGlobalTrans(2 * time.Duration(conf.RetryInterval) * time.Second)
assert.Nil(t, g2)
}
-func TestStoreWait(t *testing.T) {
- registry.WaitStoreUp()
-}
-
-func TestUpdateBranchSql(t *testing.T) {
- if !config.Store.IsDB() {
- r := registry.GetStore().UpdateBranchesSql(nil, nil)
- assert.Nil(t, r)
+func TestUpdateBranches(t *testing.T) {
+ if !conf.Store.IsDB() {
+ _, err := registry.GetStore().UpdateBranches(nil, nil)
+ assert.Nil(t, err)
}
}
diff --git a/test/tcc_barrier_test.go b/test/tcc_barrier_test.go
index a1b3c88..8a1ae57 100644
--- a/test/tcc_barrier_test.go
+++ b/test/tcc_barrier_test.go
@@ -12,18 +12,18 @@ import (
"fmt"
"strings"
"testing"
- "time"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/test/busi"
"github.com/gin-gonic/gin"
"github.com/go-resty/resty/v2"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/examples"
)
func TestTccBarrierNormal(t *testing.T) {
- req := examples.GenTransReq(30, false, false)
+ req := busi.GenTransReq(30, false, false)
gid := dtmimp.GetFuncName()
err := dtmcli.TccGlobalTransaction(DtmServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
_, err := tcc.CallBranch(req, Busi+"/TccBTransOutTry", Busi+"/TccBTransOutConfirm", Busi+"/TccBTransOutCancel")
@@ -37,7 +37,7 @@ func TestTccBarrierNormal(t *testing.T) {
}
func TestTccBarrierRollback(t *testing.T) {
- req := examples.GenTransReq(30, false, true)
+ req := busi.GenTransReq(30, false, true)
gid := dtmimp.GetFuncName()
err := dtmcli.TccGlobalTransaction(DtmServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
_, err := tcc.CallBranch(req, Busi+"/TccBTransOutTry", Busi+"/TccBTransOutConfirm", Busi+"/TccBTransOutCancel")
@@ -50,30 +50,37 @@ func TestTccBarrierRollback(t *testing.T) {
assert.Equal(t, []string{StatusSucceed, StatusPrepared, StatusSucceed, StatusPrepared}, getBranchesStatus(gid))
}
-var disorderHandler func(c *gin.Context) (interface{}, error) = nil
+func TestTccBarrierDisorderMysql(t *testing.T) {
+ runTestTccBarrierDisorder(t, "mysql")
+}
-func TestTccBarrierDisorder(t *testing.T) {
- timeoutChan := make(chan string, 2)
- finishedChan := make(chan string, 2)
- gid := dtmimp.GetFuncName()
+func TestTccBarrierDisorderRedis(t *testing.T) {
+ busi.SetRedisBothAccount(200, 200)
+ runTestTccBarrierDisorder(t, "redis")
+}
+
+func runTestTccBarrierDisorder(t *testing.T, store string) {
+ before := getBeforeBalances(store)
+ cancelFinishedChan := make(chan string, 2)
+ cancelCanReturnChan := make(chan string, 2)
+ gid := dtmimp.GetFuncName() + store
+ cronFinished := make(chan string, 2)
err := dtmcli.TccGlobalTransaction(DtmServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
- body := &examples.TransReq{Amount: 30}
+ body := &busi.TransReq{Amount: 30, Store: store}
tryURL := Busi + "/TccBTransOutTry"
confirmURL := Busi + "/TccBTransOutConfirm"
cancelURL := Busi + "/TccBSleepCancel"
// 请参见子事务屏障里的时序图,这里为了模拟该时序图,手动拆解了callbranch
branchID := tcc.NewSubBranchID()
- sleeped := false
- disorderHandler = func(c *gin.Context) (interface{}, error) {
- res, err := examples.TccBarrierTransOutCancel(c)
- if !sleeped {
- sleeped = true
- dtmimp.Logf("sleep before cancel return")
- <-timeoutChan
- finishedChan <- "1"
- }
- return res, err
- }
+ busi.SetSleepCancelHandler(func(c *gin.Context) interface{} {
+ res := busi.TccBarrierTransOutCancel(c)
+ logger.Debugf("disorderHandler before cancel finish write")
+ cancelFinishedChan <- "1"
+ logger.Debugf("disorderHandler before cancel return read")
+ <-cancelCanReturnChan
+ logger.Debugf("disorderHandler after cancel return read")
+ return res
+ })
// 注册子事务
resp, err := dtmimp.RestyClient.R().
SetBody(map[string]interface{}{
@@ -88,37 +95,43 @@ func TestTccBarrierDisorder(t *testing.T) {
assert.Nil(t, err)
assert.Contains(t, resp.String(), dtmcli.ResultSuccess)
- go func() {
- dtmimp.Logf("sleeping to wait for tcc try timeout")
- <-timeoutChan
- r, _ := dtmimp.RestyClient.R().
- SetBody(body).
- SetQueryParams(map[string]string{
- "dtm": tcc.Dtm,
- "gid": tcc.Gid,
- "branch_id": branchID,
- "trans_type": "tcc",
- "op": dtmcli.BranchTry,
- }).
- Post(tryURL)
- assert.True(t, strings.Contains(r.String(), dtmcli.ResultSuccess)) // 这个是悬挂操作,为了简单起见,依旧让他返回成功
- finishedChan <- "1"
- }()
- dtmimp.Logf("cron to timeout and then call cancel")
- go cronTransOnceForwardNow(300)
- time.Sleep(100 * time.Millisecond)
- dtmimp.Logf("cron to timeout and then call cancelled twice")
- cronTransOnceForwardNow(300)
- timeoutChan <- "wake"
- timeoutChan <- "wake"
- <-finishedChan
- <-finishedChan
- time.Sleep(100 * time.Millisecond)
+ logger.Debugf("cron to timeout and then call cancelled twice")
+ cron := func() {
+ cronTransOnceForwardNow(300)
+ logger.Debugf("cronFinished write")
+ cronFinished <- "1"
+ logger.Debugf("cronFinished after write")
+ }
+ go cron()
+ <-cancelFinishedChan
+ go cron()
+ <-cancelFinishedChan
+ cancelCanReturnChan <- "1"
+ cancelCanReturnChan <- "1"
+ logger.Debugf("after cancelCanRetrun 2 write")
+ // after cancel then run try
+ r, _ := dtmimp.RestyClient.R().
+ SetBody(body).
+ SetQueryParams(map[string]string{
+ "dtm": tcc.Dtm,
+ "gid": tcc.Gid,
+ "branch_id": branchID,
+ "trans_type": "tcc",
+ "op": dtmcli.BranchTry,
+ }).
+ Post(tryURL)
+ assert.True(t, strings.Contains(r.String(), dtmcli.ResultSuccess)) // 这个是悬挂操作,为了简单起见,依旧让他返回成功
+ logger.Debugf("cronFinished read")
+ <-cronFinished
+ <-cronFinished
+ logger.Debugf("cronFinished after read")
+
return nil, fmt.Errorf("a cancelled tcc")
})
assert.Error(t, err, fmt.Errorf("a cancelled tcc"))
assert.Equal(t, []string{StatusSucceed, StatusPrepared}, getBranchesStatus(gid))
assert.Equal(t, StatusFailed, getTransStatus(gid))
+ assertSameBalance(t, before, store)
}
func TestTccBarrierPanic(t *testing.T) {
diff --git a/test/tcc_cover_test.go b/test/tcc_cover_test.go
index 12e5a32..00ba711 100644
--- a/test/tcc_cover_test.go
+++ b/test/tcc_cover_test.go
@@ -3,11 +3,12 @@ package test
import (
"testing"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmutil"
+ "github.com/dtm-labs/dtm/test/busi"
"github.com/go-resty/resty/v2"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/examples"
)
func TestTccCoverNotConnected(t *testing.T) {
@@ -21,10 +22,25 @@ func TestTccCoverNotConnected(t *testing.T) {
func TestTccCoverPanic(t *testing.T) {
gid := dtmimp.GetFuncName()
err := dtmimp.CatchP(func() {
- _ = dtmcli.TccGlobalTransaction(examples.DtmHttpServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
+ _ = dtmcli.TccGlobalTransaction(dtmutil.DefaultHTTPServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
panic("user panic")
})
assert.FailNow(t, "not executed")
})
assert.Contains(t, err.Error(), "user panic")
+ waitTransProcessed(gid)
+}
+
+func TestTccNested(t *testing.T) {
+ req := busi.GenTransReq(30, false, false)
+ gid := dtmimp.GetFuncName()
+ err := dtmcli.TccGlobalTransaction(dtmutil.DefaultHTTPServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
+ _, err := tcc.CallBranch(req, Busi+"/TransOut", Busi+"/TransOutConfirm", Busi+"/TransOutRevert")
+ assert.Nil(t, err)
+ return tcc.CallBranch(req, Busi+"/TransInTccNested", Busi+"/TransInConfirm", Busi+"/TransInRevert")
+ })
+ assert.Nil(t, err)
+ waitTransProcessed(gid)
+ assert.Equal(t, StatusSucceed, getTransStatus(gid))
+ assert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(gid))
}
diff --git a/test/tcc_grpc_cover_test.go b/test/tcc_grpc_cover_test.go
index f4c3fef..192d491 100644
--- a/test/tcc_grpc_cover_test.go
+++ b/test/tcc_grpc_cover_test.go
@@ -3,10 +3,11 @@ package test
import (
"testing"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmgrpc"
+ "github.com/dtm-labs/dtm/dtmutil"
+ "github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmgrpc"
- "github.com/yedf/dtm/examples"
"google.golang.org/protobuf/types/known/emptypb"
)
@@ -21,28 +22,31 @@ func TestTccGrpcCoverNotConnected(t *testing.T) {
func TestTccGrpcCoverPanic(t *testing.T) {
gid := dtmimp.GetFuncName()
err := dtmimp.CatchP(func() {
- _ = dtmgrpc.TccGlobalTransaction(examples.DtmGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {
+ _ = dtmgrpc.TccGlobalTransaction(dtmutil.DefaultGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {
panic("user panic")
})
assert.FailNow(t, "not executed")
})
assert.Contains(t, err.Error(), "user panic")
+ waitTransProcessed(gid)
}
func TestTccGrpcCoverCallBranch(t *testing.T) {
- req := examples.GenBusiReq(30, false, false)
+ req := busi.GenBusiReq(30, false, false)
gid := dtmimp.GetFuncName()
- err := dtmgrpc.TccGlobalTransaction(examples.DtmGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {
+ err := dtmgrpc.TccGlobalTransaction(dtmutil.DefaultGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {
r := &emptypb.Empty{}
- err := tcc.CallBranch(req, "not_exists://abc", examples.BusiGrpc+"/examples.Busi/TransOutConfirm", examples.BusiGrpc+"/examples.Busi/TransOutRevert", r)
+ err := tcc.CallBranch(req, "not_exists://abc", busi.BusiGrpc+"/busi.Busi/TransOutConfirm", busi.BusiGrpc+"/busi.Busi/TransOutRevert", r)
assert.Error(t, err)
tcc.Dtm = "localhost:01"
- err = tcc.CallBranch(req, examples.BusiGrpc+"/examples.Busi/TransOut", examples.BusiGrpc+"/examples.Busi/TransOutConfirm", examples.BusiGrpc+"/examples.Busi/TransOutRevert", r)
+ err = tcc.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransOut", busi.BusiGrpc+"/busi.Busi/TransOutConfirm", busi.BusiGrpc+"/busi.Busi/TransOutRevert", r)
assert.Error(t, err)
return err
})
assert.Error(t, err)
+ g := cronTransOnceForwardNow(300)
+ assert.Equal(t, gid, g)
}
diff --git a/test/tcc_grpc_test.go b/test/tcc_grpc_test.go
index ff4b2df..b3a8169 100644
--- a/test/tcc_grpc_test.go
+++ b/test/tcc_grpc_test.go
@@ -10,22 +10,24 @@ import (
"context"
"testing"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/dtmgrpc"
+ "github.com/dtm-labs/dtm/dtmutil"
+ "github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmgrpc"
- "github.com/yedf/dtm/examples"
"google.golang.org/protobuf/types/known/emptypb"
)
func TestTccGrpcNormal(t *testing.T) {
- req := examples.GenBusiReq(30, false, false)
+ req := busi.GenBusiReq(30, false, false)
gid := dtmimp.GetFuncName()
- err := dtmgrpc.TccGlobalTransaction(examples.DtmGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {
+ err := dtmgrpc.TccGlobalTransaction(dtmutil.DefaultGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {
r := &emptypb.Empty{}
- err := tcc.CallBranch(req, examples.BusiGrpc+"/examples.Busi/TransOut", examples.BusiGrpc+"/examples.Busi/TransOutConfirm", examples.BusiGrpc+"/examples.Busi/TransOutRevert", r)
+ err := tcc.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransOut", busi.BusiGrpc+"/busi.Busi/TransOutConfirm", busi.BusiGrpc+"/busi.Busi/TransOutRevert", r)
assert.Nil(t, err)
- return tcc.CallBranch(req, examples.BusiGrpc+"/examples.Busi/TransIn", examples.BusiGrpc+"/examples.Busi/TransInConfirm", examples.BusiGrpc+"/examples.Busi/TransInRevert", r)
+ return tcc.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransIn", busi.BusiGrpc+"/busi.Busi/TransInConfirm", busi.BusiGrpc+"/busi.Busi/TransInRevert", r)
})
assert.Nil(t, err)
waitTransProcessed(gid)
@@ -36,30 +38,31 @@ func TestTccGrpcNormal(t *testing.T) {
func TestTccGrpcRollback(t *testing.T) {
gid := dtmimp.GetFuncName()
- req := examples.GenBusiReq(30, false, true)
- err := dtmgrpc.TccGlobalTransaction(examples.DtmGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {
+ req := busi.GenBusiReq(30, false, true)
+ err := dtmgrpc.TccGlobalTransaction(dtmutil.DefaultGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {
r := &emptypb.Empty{}
- err := tcc.CallBranch(req, examples.BusiGrpc+"/examples.Busi/TransOutTcc", examples.BusiGrpc+"/examples.Busi/TransOutConfirm", examples.BusiGrpc+"/examples.Busi/TransOutRevert", r)
+ err := tcc.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransOutTcc", busi.BusiGrpc+"/busi.Busi/TransOutConfirm", busi.BusiGrpc+"/busi.Busi/TransOutRevert", r)
assert.Nil(t, err)
- examples.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing)
- return tcc.CallBranch(req, examples.BusiGrpc+"/examples.Busi/TransInTcc", examples.BusiGrpc+"/examples.Busi/TransInConfirm", examples.BusiGrpc+"/examples.Busi/TransInRevert", r)
+ busi.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing)
+ return tcc.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransInTcc", busi.BusiGrpc+"/busi.Busi/TransInConfirm", busi.BusiGrpc+"/busi.Busi/TransInRevert", r)
})
assert.Error(t, err)
waitTransProcessed(gid)
assert.Equal(t, StatusAborting, getTransStatus(gid))
- cronTransOnce()
+ g2 := cronTransOnce()
+ assert.Equal(t, gid, g2)
assert.Equal(t, StatusFailed, getTransStatus(gid))
assert.Equal(t, []string{StatusSucceed, StatusPrepared, StatusSucceed, StatusPrepared}, getBranchesStatus(gid))
}
func TestTccGrpcNested(t *testing.T) {
- req := examples.GenBusiReq(30, false, false)
+ req := busi.GenBusiReq(30, false, false)
gid := dtmimp.GetFuncName()
- err := dtmgrpc.TccGlobalTransaction(examples.DtmGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {
+ err := dtmgrpc.TccGlobalTransaction(dtmutil.DefaultGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {
r := &emptypb.Empty{}
- err := tcc.CallBranch(req, examples.BusiGrpc+"/examples.Busi/TransOutTcc", examples.BusiGrpc+"/examples.Busi/TransOutConfirm", examples.BusiGrpc+"/examples.Busi/TransOutRevert", r)
+ err := tcc.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransOutTcc", busi.BusiGrpc+"/busi.Busi/TransOutConfirm", busi.BusiGrpc+"/busi.Busi/TransOutRevert", r)
assert.Nil(t, err)
- return tcc.CallBranch(req, examples.BusiGrpc+"/examples.Busi/TransInTccNested", examples.BusiGrpc+"/examples.Busi/TransInConfirm", examples.BusiGrpc+"/examples.Busi/TransInRevert", r)
+ return tcc.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransInTccNested", busi.BusiGrpc+"/busi.Busi/TransInConfirm", busi.BusiGrpc+"/busi.Busi/TransInRevert", r)
})
assert.Nil(t, err)
waitTransProcessed(gid)
@@ -70,7 +73,26 @@ func TestTccGrpcNested(t *testing.T) {
func TestTccGrpcType(t *testing.T) {
_, err := dtmgrpc.TccFromGrpc(context.Background())
assert.Error(t, err)
- dtmimp.Logf("expecting dtmgrpcserver error")
+ logger.Debugf("expecting dtmutil.DefaultGrpcServer error")
err = dtmgrpc.TccGlobalTransaction("-", "", func(tcc *dtmgrpc.TccGrpc) error { return nil })
assert.Error(t, err)
}
+
+func TestTccGrpcHeaders(t *testing.T) {
+ gid := dtmimp.GetFuncName()
+ err := dtmgrpc.TccGlobalTransaction2(dtmutil.DefaultGrpcServer, gid, func(tg *dtmgrpc.TccGrpc) {
+ tg.BranchHeaders = map[string]string{
+ "test_header": "test",
+ }
+ tg.WaitResult = true
+ }, func(tcc *dtmgrpc.TccGrpc) error {
+ data := &busi.BusiReq{Amount: 30}
+ r := &emptypb.Empty{}
+ return tcc.CallBranch(data, busi.BusiGrpc+"/busi.Busi/TransOutHeaderYes", "", "", r)
+ })
+ assert.Nil(t, err)
+ waitTransProcessed(gid)
+ assert.Equal(t, StatusSucceed, getTransStatus(gid))
+ assert.Equal(t, []string{StatusPrepared, StatusSucceed}, getBranchesStatus(gid))
+
+}
diff --git a/test/tcc_old_test.go b/test/tcc_old_test.go
new file mode 100644
index 0000000..9a55611
--- /dev/null
+++ b/test/tcc_old_test.go
@@ -0,0 +1,66 @@
+package test
+
+import (
+ "testing"
+
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmutil"
+ "github.com/dtm-labs/dtm/test/busi"
+ "github.com/go-resty/resty/v2"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestTccOldNormal(t *testing.T) {
+ req := busi.GenTransReq(30, false, false)
+ gid := dtmimp.GetFuncName()
+ err := dtmcli.TccGlobalTransaction(dtmutil.DefaultHTTPServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
+ _, err := tcc.CallBranch(req, Busi+"/TransOutOld", Busi+"/TransOutConfirmOld", Busi+"/TransOutRevertOld")
+ assert.Nil(t, err)
+ return tcc.CallBranch(req, Busi+"/TransInOld", Busi+"/TransInConfirmOld", Busi+"/TransInRevertOld")
+ })
+ assert.Nil(t, err)
+ waitTransProcessed(gid)
+ assert.Equal(t, StatusSucceed, getTransStatus(gid))
+ assert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(gid))
+}
+
+func TestTccOldRollback(t *testing.T) {
+ gid := dtmimp.GetFuncName()
+ req := busi.GenTransReq(30, false, true)
+ err := dtmcli.TccGlobalTransaction(dtmutil.DefaultHTTPServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
+ _, rerr := tcc.CallBranch(req, Busi+"/TransOutOld", Busi+"/TransOutConfirmOld", Busi+"/TransOutRevertOld")
+ assert.Nil(t, rerr)
+ busi.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing)
+ return tcc.CallBranch(req, Busi+"/TransInOld", Busi+"/TransInConfirmOld", Busi+"/TransInRevertOld")
+ })
+ assert.Error(t, err)
+ waitTransProcessed(gid)
+ assert.Equal(t, StatusAborting, getTransStatus(gid))
+ g := cronTransOnce()
+ assert.Equal(t, gid, g)
+ assert.Equal(t, StatusFailed, getTransStatus(gid))
+ assert.Equal(t, []string{StatusSucceed, StatusPrepared, StatusSucceed, StatusPrepared}, getBranchesStatus(gid))
+}
+
+func TestTccOldTimeout(t *testing.T) {
+ req := busi.GenTransReq(30, false, false)
+ gid := dtmimp.GetFuncName()
+ timeoutChan := make(chan int, 1)
+
+ err := dtmcli.TccGlobalTransaction(dtmutil.DefaultHTTPServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
+ _, err := tcc.CallBranch(req, Busi+"/TransOutOld", Busi+"/TransOutConfirmOld", Busi+"/TransOutRevertOld")
+ assert.Nil(t, err)
+ go func() {
+ cronTransOnceForwardNow(300)
+ timeoutChan <- 0
+ }()
+ <-timeoutChan
+ _, err = tcc.CallBranch(req, Busi+"/TransInOld", Busi+"/TransInConfirmOld", Busi+"/TransInRevertOld")
+ assert.Error(t, err)
+ return nil, err
+ })
+ assert.Error(t, err)
+ assert.Equal(t, StatusFailed, getTransStatus(gid))
+ assert.Equal(t, []string{StatusSucceed, StatusPrepared}, getBranchesStatus(gid))
+}
diff --git a/test/tcc_test.go b/test/tcc_test.go
index 0697f4e..8264d43 100644
--- a/test/tcc_test.go
+++ b/test/tcc_test.go
@@ -9,17 +9,18 @@ package test
import (
"testing"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmutil"
+ "github.com/dtm-labs/dtm/test/busi"
"github.com/go-resty/resty/v2"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/examples"
)
func TestTccNormal(t *testing.T) {
- req := examples.GenTransReq(30, false, false)
+ req := busi.GenTransReq(30, false, false)
gid := dtmimp.GetFuncName()
- err := dtmcli.TccGlobalTransaction(examples.DtmHttpServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
+ err := dtmcli.TccGlobalTransaction(dtmutil.DefaultHTTPServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
_, err := tcc.CallBranch(req, Busi+"/TransOut", Busi+"/TransOutConfirm", Busi+"/TransOutRevert")
assert.Nil(t, err)
return tcc.CallBranch(req, Busi+"/TransIn", Busi+"/TransInConfirm", Busi+"/TransInRevert")
@@ -32,27 +33,28 @@ func TestTccNormal(t *testing.T) {
func TestTccRollback(t *testing.T) {
gid := dtmimp.GetFuncName()
- req := examples.GenTransReq(30, false, true)
- err := dtmcli.TccGlobalTransaction(examples.DtmHttpServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
+ req := busi.GenTransReq(30, false, true)
+ err := dtmcli.TccGlobalTransaction(dtmutil.DefaultHTTPServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
_, rerr := tcc.CallBranch(req, Busi+"/TransOut", Busi+"/TransOutConfirm", Busi+"/TransOutRevert")
assert.Nil(t, rerr)
- examples.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing)
+ busi.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing)
return tcc.CallBranch(req, Busi+"/TransIn", Busi+"/TransInConfirm", Busi+"/TransInRevert")
})
assert.Error(t, err)
waitTransProcessed(gid)
assert.Equal(t, StatusAborting, getTransStatus(gid))
- cronTransOnce()
+ g := cronTransOnce()
+ assert.Equal(t, gid, g)
assert.Equal(t, StatusFailed, getTransStatus(gid))
assert.Equal(t, []string{StatusSucceed, StatusPrepared, StatusSucceed, StatusPrepared}, getBranchesStatus(gid))
}
func TestTccTimeout(t *testing.T) {
- req := examples.GenTransReq(30, false, false)
+ req := busi.GenTransReq(30, false, false)
gid := dtmimp.GetFuncName()
timeoutChan := make(chan int, 1)
- err := dtmcli.TccGlobalTransaction(examples.DtmHttpServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
+ err := dtmcli.TccGlobalTransaction(dtmutil.DefaultHTTPServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
_, err := tcc.CallBranch(req, Busi+"/TransOut", Busi+"/TransOutConfirm", Busi+"/TransOutRevert")
assert.Nil(t, err)
go func() {
@@ -70,9 +72,9 @@ func TestTccTimeout(t *testing.T) {
}
func TestTccCompatible(t *testing.T) {
- req := examples.GenTransReq(30, false, false)
+ req := busi.GenTransReq(30, false, false)
gid := dtmimp.GetFuncName()
- err := dtmcli.TccGlobalTransaction(examples.DtmHttpServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
+ err := dtmcli.TccGlobalTransaction(dtmutil.DefaultHTTPServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
_, err := tcc.CallBranch(req, Busi+"/TransOut", Busi+"/TransOutConfirm", Busi+"/TransOutRevert")
assert.Nil(t, err)
return tcc.CallBranch(req, Busi+"/TransIn", Busi+"/TransInConfirm", Busi+"/TransInRevert")
@@ -83,3 +85,19 @@ func TestTccCompatible(t *testing.T) {
assert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(gid))
}
+
+func TestTccHeaders(t *testing.T) {
+ req := busi.GenTransReq(30, false, false)
+ gid := dtmimp.GetFuncName()
+ err := dtmcli.TccGlobalTransaction2(dtmutil.DefaultHTTPServer, gid, func(t *dtmcli.Tcc) {
+ t.BranchHeaders = map[string]string{
+ "test_header": "test",
+ }
+ }, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
+ return tcc.CallBranch(req, Busi+"/TransOutHeaderYes", "", "")
+ })
+ assert.Nil(t, err)
+ waitTransProcessed(gid)
+ assert.Equal(t, StatusSucceed, getTransStatus(gid))
+ assert.Equal(t, []string{StatusPrepared, StatusSucceed}, getBranchesStatus(gid))
+}
diff --git a/test/types.go b/test/types.go
index 7c54b86..adff1a6 100644
--- a/test/types.go
+++ b/test/types.go
@@ -7,40 +7,43 @@
package test
import (
+ "testing"
"time"
- "github.com/yedf/dtm/common"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmsvr"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmcli/logger"
+ "github.com/dtm-labs/dtm/dtmsvr"
+ "github.com/dtm-labs/dtm/dtmsvr/config"
+ "github.com/dtm-labs/dtm/dtmutil"
+ "github.com/dtm-labs/dtm/test/busi"
+ "github.com/stretchr/testify/assert"
)
-var config = &common.Config
+var conf = &config.Config
-func dbGet() *common.DB {
- return common.DbGet(config.ExamplesDB)
+func dbGet() *dtmutil.DB {
+ return dtmutil.DbGet(busi.BusiConf)
}
// waitTransProcessed only for test usage. wait for transaction processed once
func waitTransProcessed(gid string) {
- dtmimp.Logf("waiting for gid %s", gid)
+ logger.Debugf("waiting for gid %s", gid)
select {
case id := <-dtmsvr.TransProcessedTestChan:
- for id != gid {
- dtmimp.LogRedf("-------id %s not match gid %s", id, gid)
- id = <-dtmsvr.TransProcessedTestChan
- }
- dtmimp.Logf("finish for gid %s", gid)
- case <-time.After(time.Duration(time.Second * 3)):
- dtmimp.LogFatalf("Wait Trans timeout")
+ logger.FatalfIf(id != gid, "------- expecting: %s but %s found", gid, id)
+ logger.Debugf("finish for gid %s", gid)
+ case <-time.After(time.Duration(time.Second * 4)):
+ logger.FatalfIf(true, "Wait Trans timeout")
}
}
-func cronTransOnce() {
+func cronTransOnce() string {
gid := dtmsvr.CronTransOnce()
if dtmsvr.TransProcessedTestChan != nil && gid != "" {
waitTransProcessed(gid)
}
+ return gid
}
var e2p = dtmimp.E2P
@@ -51,18 +54,20 @@ type TransGlobal = dtmsvr.TransGlobal
// TransBranch alias
type TransBranch = dtmsvr.TransBranch
-func cronTransOnceForwardNow(seconds int) {
+func cronTransOnceForwardNow(seconds int) string {
old := dtmsvr.NowForwardDuration
dtmsvr.NowForwardDuration = time.Duration(seconds) * time.Second
- cronTransOnce()
+ gid := cronTransOnce()
dtmsvr.NowForwardDuration = old
+ return gid
}
-func cronTransOnceForwardCron(seconds int) {
+func cronTransOnceForwardCron(seconds int) string {
old := dtmsvr.CronForwardDuration
dtmsvr.CronForwardDuration = time.Duration(seconds) * time.Second
- cronTransOnce()
+ gid := cronTransOnce()
dtmsvr.CronForwardDuration = old
+ return gid
}
const (
@@ -77,3 +82,23 @@ const (
// StatusAborting status for global trans status.
StatusAborting = dtmcli.StatusAborting
)
+
+func getBeforeBalances(store string) []int {
+ b1 := busi.GetBalanceByUid(busi.TransOutUID, store)
+ b2 := busi.GetBalanceByUid(busi.TransInUID, store)
+ return []int{b1, b2}
+}
+
+func assertSameBalance(t *testing.T, before []int, store string) {
+ b1 := busi.GetBalanceByUid(busi.TransOutUID, store)
+ b2 := busi.GetBalanceByUid(busi.TransInUID, store)
+ assert.Equal(t, before[0], b1)
+ assert.Equal(t, before[1], b2)
+}
+
+func assertNotSameBalance(t *testing.T, before []int, store string) {
+ b1 := busi.GetBalanceByUid(busi.TransOutUID, store)
+ b2 := busi.GetBalanceByUid(busi.TransInUID, store)
+ assert.NotEqual(t, before[0], b1)
+ assert.Equal(t, before[0]+before[1], b1+b2)
+}
diff --git a/test/xa_cover_test.go b/test/xa_cover_test.go
index bd6d8be..0103539 100644
--- a/test/xa_cover_test.go
+++ b/test/xa_cover_test.go
@@ -3,22 +3,22 @@ package test
import (
"testing"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/test/busi"
"github.com/go-resty/resty/v2"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/examples"
)
func TestXaCoverDBError(t *testing.T) {
oldDriver := getXc().Conf.Driver
gid := dtmimp.GetFuncName()
err := getXc().XaGlobalTransaction(gid, func(xa *dtmcli.Xa) (*resty.Response, error) {
- req := examples.GenTransReq(30, false, false)
- _, err := xa.CallBranch(req, examples.Busi+"/TransOutXa")
+ req := busi.GenTransReq(30, false, false)
+ _, err := xa.CallBranch(req, busi.Busi+"/TransOutXa")
assert.Nil(t, err)
getXc().Conf.Driver = "no-driver"
- _, err = xa.CallBranch(req, examples.Busi+"/TransInXa")
+ _, err = xa.CallBranch(req, busi.Busi+"/TransInXa")
assert.Error(t, err)
getXc().Conf.Driver = oldDriver // make abort succeed
return nil, err
@@ -43,12 +43,13 @@ func TestXaCoverDTMError(t *testing.T) {
}
func TestXaCoverGidError(t *testing.T) {
- gid := "errgid-' '"
+ gid := dtmimp.GetFuncName() + "-' '"
err := getXc().XaGlobalTransaction(gid, func(xa *dtmcli.Xa) (*resty.Response, error) {
- req := examples.GenTransReq(30, false, false)
- _, err := xa.CallBranch(req, examples.Busi+"/TransOutXa")
+ req := busi.GenTransReq(30, false, false)
+ _, err := xa.CallBranch(req, busi.Busi+"/TransOutXa")
assert.Error(t, err)
return nil, err
})
assert.Error(t, err)
+ waitTransProcessed(gid)
}
diff --git a/test/xa_grpc_test.go b/test/xa_grpc_test.go
index 76e1111..890d129 100644
--- a/test/xa_grpc_test.go
+++ b/test/xa_grpc_test.go
@@ -11,26 +11,26 @@ import (
"fmt"
"testing"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/dtmgrpc"
+ "github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/dtmgrpc"
- "github.com/yedf/dtm/examples"
"google.golang.org/protobuf/types/known/emptypb"
)
func getXcg() *dtmgrpc.XaGrpcClient {
- return examples.XaGrpcClient
+ return busi.XaGrpcClient
}
func TestXaGrpcNormal(t *testing.T) {
gid := dtmimp.GetFuncName()
err := getXcg().XaGlobalTransaction(gid, func(xa *dtmgrpc.XaGrpc) error {
- req := examples.GenBusiReq(30, false, false)
+ req := busi.GenBusiReq(30, false, false)
r := &emptypb.Empty{}
- err := xa.CallBranch(req, examples.BusiGrpc+"/examples.Busi/TransOutXa", r)
+ err := xa.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransOutXa", r)
if err != nil {
return err
}
- return xa.CallBranch(req, examples.BusiGrpc+"/examples.Busi/TransInXa", r)
+ return xa.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransInXa", r)
})
assert.Equal(t, nil, err)
waitTransProcessed(gid)
@@ -41,13 +41,13 @@ func TestXaGrpcNormal(t *testing.T) {
func TestXaGrpcRollback(t *testing.T) {
gid := dtmimp.GetFuncName()
err := getXcg().XaGlobalTransaction(gid, func(xa *dtmgrpc.XaGrpc) error {
- req := examples.GenBusiReq(30, false, true)
+ req := busi.GenBusiReq(30, false, true)
r := &emptypb.Empty{}
- err := xa.CallBranch(req, examples.BusiGrpc+"/examples.Busi/TransOutXa", r)
+ err := xa.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransOutXa", r)
if err != nil {
return err
}
- return xa.CallBranch(req, examples.BusiGrpc+"/examples.Busi/TransInXa", r)
+ return xa.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransInXa", r)
})
assert.Error(t, err)
waitTransProcessed(gid)
@@ -56,22 +56,26 @@ func TestXaGrpcRollback(t *testing.T) {
}
func TestXaGrpcType(t *testing.T) {
+ gid := dtmimp.GetFuncName()
_, err := dtmgrpc.XaGrpcFromRequest(context.Background())
assert.Error(t, err)
- err = examples.XaGrpcClient.XaLocalTransaction(context.Background(), nil, nil)
+ err = busi.XaGrpcClient.XaLocalTransaction(context.Background(), nil, nil)
assert.Error(t, err)
err = dtmimp.CatchP(func() {
- examples.XaGrpcClient.XaGlobalTransaction("id1", func(xa *dtmgrpc.XaGrpc) error { panic(fmt.Errorf("hello")) })
+ busi.XaGrpcClient.XaGlobalTransaction(gid, func(xa *dtmgrpc.XaGrpc) error { panic(fmt.Errorf("hello")) })
})
assert.Error(t, err)
+ waitTransProcessed(gid)
}
func TestXaGrpcLocalError(t *testing.T) {
- xc := examples.XaGrpcClient
- err := xc.XaGlobalTransaction(dtmimp.GetFuncName(), func(xa *dtmgrpc.XaGrpc) error {
+ gid := dtmimp.GetFuncName()
+ xc := busi.XaGrpcClient
+ err := xc.XaGlobalTransaction(gid, func(xa *dtmgrpc.XaGrpc) error {
return fmt.Errorf("an error")
})
assert.Error(t, err, fmt.Errorf("an error"))
+ waitTransProcessed(gid)
}
diff --git a/test/xa_test.go b/test/xa_test.go
index 7122910..fcb460c 100644
--- a/test/xa_test.go
+++ b/test/xa_test.go
@@ -10,26 +10,26 @@ import (
"fmt"
"testing"
+ "github.com/dtm-labs/dtm/dtmcli"
+ "github.com/dtm-labs/dtm/dtmcli/dtmimp"
+ "github.com/dtm-labs/dtm/test/busi"
"github.com/go-resty/resty/v2"
"github.com/stretchr/testify/assert"
- "github.com/yedf/dtm/dtmcli"
- "github.com/yedf/dtm/dtmcli/dtmimp"
- "github.com/yedf/dtm/examples"
)
func getXc() *dtmcli.XaClient {
- return examples.XaClient
+ return busi.XaClient
}
func TestXaNormal(t *testing.T) {
gid := dtmimp.GetFuncName()
err := getXc().XaGlobalTransaction(gid, func(xa *dtmcli.Xa) (*resty.Response, error) {
- req := examples.GenTransReq(30, false, false)
- resp, err := xa.CallBranch(req, examples.Busi+"/TransOutXa")
+ req := busi.GenTransReq(30, false, false)
+ resp, err := xa.CallBranch(req, busi.Busi+"/TransOutXa")
if err != nil {
return resp, err
}
- return xa.CallBranch(req, examples.Busi+"/TransInXa")
+ return xa.CallBranch(req, busi.Busi+"/TransInXa")
})
assert.Equal(t, nil, err)
waitTransProcessed(gid)
@@ -40,10 +40,10 @@ func TestXaNormal(t *testing.T) {
func TestXaDuplicate(t *testing.T) {
gid := dtmimp.GetFuncName()
err := getXc().XaGlobalTransaction(gid, func(xa *dtmcli.Xa) (*resty.Response, error) {
- req := examples.GenTransReq(30, false, false)
- _, err := xa.CallBranch(req, examples.Busi+"/TransOutXa")
+ req := busi.GenTransReq(30, false, false)
+ _, err := xa.CallBranch(req, busi.Busi+"/TransOutXa")
assert.Nil(t, err)
- sdb, err := dtmimp.StandaloneDB(config.ExamplesDB)
+ sdb, err := dtmimp.StandaloneDB(busi.BusiConf)
assert.Nil(t, err)
if dtmcli.GetCurrentDBType() == dtmcli.DBTypeMysql {
_, err = dtmimp.DBExec(sdb, "xa recover")
@@ -51,7 +51,7 @@ func TestXaDuplicate(t *testing.T) {
}
_, err = dtmimp.DBExec(sdb, dtmimp.GetDBSpecial().GetXaSQL("commit", gid+"-01")) // 先把某一个事务提交,模拟重复请求
assert.Nil(t, err)
- return xa.CallBranch(req, examples.Busi+"/TransInXa")
+ return xa.CallBranch(req, busi.Busi+"/TransInXa")
})
assert.Nil(t, err)
waitTransProcessed(gid)
@@ -62,12 +62,12 @@ func TestXaDuplicate(t *testing.T) {
func TestXaRollback(t *testing.T) {
gid := dtmimp.GetFuncName()
err := getXc().XaGlobalTransaction(gid, func(xa *dtmcli.Xa) (*resty.Response, error) {
- req := examples.GenTransReq(30, false, true)
- resp, err := xa.CallBranch(req, examples.Busi+"/TransOutXa")
+ req := busi.GenTransReq(30, false, true)
+ resp, err := xa.CallBranch(req, busi.Busi+"/TransOutXa")
if err != nil {
return resp, err
}
- return xa.CallBranch(req, examples.Busi+"/TransInXa")
+ return xa.CallBranch(req, busi.Busi+"/TransInXa")
})
assert.Error(t, err)
waitTransProcessed(gid)
@@ -92,7 +92,7 @@ func TestXaTimeout(t *testing.T) {
cronTransOnceForwardNow(300)
timeoutChan <- 0
}()
- _ = <-timeoutChan
+ <-timeoutChan
return nil, nil
})
assert.Error(t, err)
@@ -109,16 +109,17 @@ func TestXaNotTimeout(t *testing.T) {
timeoutChan <- 0
}()
_ = <-timeoutChan
- req := examples.GenTransReq(30, false, false)
- _, err := xa.CallBranch(req, examples.Busi+"/TransOutXa")
+ req := busi.GenTransReq(30, false, false)
+ _, err := xa.CallBranch(req, busi.Busi+"/TransOutXa")
assert.Nil(t, err)
- examples.MainSwitch.NextResult.SetOnce(dtmcli.ResultOngoing) // make commit temp error
+ busi.MainSwitch.NextResult.SetOnce(dtmcli.ResultOngoing) // make commit temp error
return nil, nil
})
assert.Nil(t, err)
waitTransProcessed(gid)
assert.Equal(t, StatusSubmitted, getTransStatus(gid))
- cronTransOnce()
+ g := cronTransOnce()
+ assert.Equal(t, gid, g)
assert.Equal(t, StatusSucceed, getTransStatus(gid))
assert.Equal(t, []string{StatusPrepared, StatusSucceed}, getBranchesStatus(gid))
}