Browse Source

Merge branch 'dtm-labs:main' into main

pull/151/head
wondarchan 4 years ago
committed by GitHub
parent
commit
45b7d79100
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      .gitignore
  2. 6
      .golangci.yml
  3. 33
      .vscode/launch.json.sample
  4. 3
      .vscode/settings.json.sample
  5. 85
      README-cn.md
  6. 84
      README.md
  7. 82
      app/main.go
  8. 14
      bench/main.go
  9. 34
      bench/svr/http.go
  10. 128
      common/config.go
  11. 49
      common/config_test.go
  12. 67
      common/types_test.go
  13. 30
      conf.sample.yml
  14. 2
      dtmcli/barrier.go
  15. 23
      dtmcli/dtmimp/trans_base.go
  16. 2
      dtmcli/dtmimp/trans_xa_base.go
  17. 2
      dtmcli/dtmimp/utils.go
  18. 8
      dtmcli/dtmimp/vars.go
  19. 6
      dtmcli/tcc.go
  20. 23
      dtmcli/types.go
  21. 1
      dtmcli/types_test.go
  22. 12
      dtmcli/xa.go
  23. 7
      dtmgrpc/dtmgimp/grpc_clients.go
  24. 7
      dtmgrpc/dtmgimp/types.go
  25. 45
      dtmgrpc/dtmgimp/utils.go
  26. 197
      dtmgrpc/dtmgpb/dtmgimp.pb.go
  27. 2
      dtmgrpc/dtmgpb/dtmgimp.proto
  28. 23
      dtmgrpc/tcc.go
  29. 17
      dtmgrpc/type.go
  30. 5
      dtmgrpc/type_test.go
  31. 10
      dtmgrpc/xa.go
  32. 6
      dtmsvr/api_grpc.go
  33. 20
      dtmsvr/api_http.go
  34. 85
      dtmsvr/config/config.go
  35. 84
      dtmsvr/config/config_test.go
  36. 34
      dtmsvr/config/config_utils.go
  37. 2
      dtmsvr/cron.go
  38. 13
      dtmsvr/storage/boltdb/boltdb.go
  39. 39
      dtmsvr/storage/redis/redis.go
  40. 6
      dtmsvr/storage/registry/registry.go
  41. 32
      dtmsvr/storage/sql/sql.go
  42. 18
      dtmsvr/storage/trans.go
  43. 19
      dtmsvr/svr.go
  44. 7
      dtmsvr/svr_imports.go
  45. 31
      dtmsvr/trans_class.go
  46. 11
      dtmsvr/trans_process.go
  47. 16
      dtmsvr/trans_status.go
  48. 12
      dtmsvr/trans_type_saga.go
  49. 5
      dtmsvr/utils.go
  50. 4
      dtmsvr/utils_test.go
  51. 14
      dtmutil/consts.go
  52. 16
      dtmutil/db.go
  53. 2
      dtmutil/utils.go
  54. 4
      dtmutil/utils_test.go
  55. 14
      examples/base.go
  56. 330
      examples/busi.pb.go
  57. 55
      examples/data.go
  58. 25
      examples/grpc_msg.go
  59. 36
      examples/grpc_saga.go
  60. 70
      examples/grpc_saga_barrier.go
  61. 32
      examples/grpc_tcc.go
  62. 37
      examples/grpc_xa.go
  63. 29
      examples/http_gorm_xa.go
  64. 28
      examples/http_msg.go
  65. 56
      examples/http_saga.go
  66. 79
      examples/http_saga_barrier.go
  67. 43
      examples/http_saga_gorm_barrier.go
  68. 51
      examples/http_tcc.go
  69. 111
      examples/http_tcc_barrier.go
  70. 42
      examples/http_xa.go
  71. 2
      go.mod
  72. 7
      go.sum
  73. 4
      helper/.goreleaser.yml
  74. 6
      helper/Dockerfile-release
  75. 2
      helper/sync-dtmcli.sh
  76. 2
      helper/test-cover.sh
  77. 71
      main.go
  78. 18
      qs/main.go
  79. 0
      sqls/busi.mysql.sql
  80. 0
      sqls/busi.postgres.sql
  81. 4
      sqls/dtmsvr.storage.mysql.sql
  82. 4
      sqls/dtmsvr.storage.postgres.sql
  83. 46
      sqls/dtmsvr.storage.tdsql.sql
  84. 14
      test/api_test.go
  85. 12
      test/base_test.go
  86. 124
      test/busi/barrier.go
  87. 62
      test/busi/base_grpc.go
  88. 138
      test/busi/base_http.go
  89. 84
      test/busi/base_types.go
  90. 85
      test/busi/busi.go
  91. 337
      test/busi/busi.pb.go
  92. 6
      test/busi/busi.proto
  93. 146
      test/busi/busi_grpc.pb.go
  94. 40
      test/busi/quick_start.go
  95. 23
      test/busi/startup.go
  96. 85
      test/busi/utils.go
  97. 48
      test/common_test.go
  98. 19
      test/dtmsvr_test.go
  99. 20
      test/examples_test.go
  100. 53
      test/main_test.go

7
.gitignore

@ -5,11 +5,14 @@ conf.yml
main
dist
.idea/**
.vscode/*.json
.vscode
default.etcd
*/**/*.bolt
# Output file of unit test coverage
coverage.*
profile.*
test.sh
test.sh
dtm
dtm-*
dtm.*

6
.golangci.yml

@ -1,8 +1,8 @@
run:
deadline: 5m
skip-dirs:
- test
- examples
# skip-dirs:
# - test
# - examples
linter-settings:
goconst:

33
.vscode/launch.json.sample

@ -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": []
}
]
}

3
.vscode/settings.json.sample

@ -1,3 +0,0 @@
{
"go.formatTool": "gofmt"
}

85
README-cn.md

@ -12,9 +12,7 @@ DTM是一款golang开发的分布式事务管理器,解决了跨数据库、
他优雅的解决了幂等、空补偿、悬挂等分布式事务难题,提供了简单易用、高性能、易水平扩展的解决方案。
作者受邀参加中国数据库大会分享[多语言环境下分布式事务实践](http://dtcc.it168.com/yicheng.html#b9)
## 谁在使用dtm
## 谁在使用DTM(仅列出部分)
[Tencent 腾讯](https://dtm.pub/other/using.html#tencent)
[Ivydad 常青藤爸爸](https://dtm.pub/other/using.html#ivydad)
@ -23,26 +21,22 @@ DTM是一款golang开发的分布式事务管理器,解决了跨数据库、
[极欧科技](https://dtm.pub/other/using.html)
[金数智联](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 |备注|
|:-----:|:----:|:----:|:----:|
@ -69,31 +63,27 @@ DTM是一款golang开发的分布式事务管理器,解决了跨数据库、
## 微服务框架支持
- [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/protocol/intro.html)
具体微服务接入使用,参见[微服务支持](https://dtm.pub/protocol/intro.html)
## 快速开始
### 获取代码
`git clone https://github.com/dtm-labs/dtm && cd dtm`
如果您不是Go语言,可以跳转[各语言客户端及示例](https://dtm.pub/summary/code.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"
@ -117,23 +107,34 @@ DTM是一款golang开发的分布式事务管理器,解决了跨数据库、
<img src="https://pic3.zhimg.com/80/v2-b7d98659093c399e182a0173a8e549ca_1440w.jpg" height=428 />
### 完整示例
参考[examples/quick_start.go](./examples/quick_start.go)
### 失败情况
在实际的业务中,子事务可能出现失败,例如转入的子账号被冻结导致转账失败。我们对业务代码进行修改,让TransIn的正向操作失败,然后看看结果
## 公众号
您可以关注公众号:分布式事务,及时跟踪dtm的最新内容
## 交流群
``` go
app.POST(qsBusiAPI+"/TransIn", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
return M{"dtm_result": "FAILURE"}, nil
}))
```
再运行这个例子,整个事务最终失败,时序图如下:
![saga_rollback](https://pic3.zhimg.com/80/v2-8d8f1476be8a1e2e09ce97a89b4116c2_1440w.jpg)
在转入操作失败的情况下,TransIn和TransOut的补偿操作被执行,保证了最终的余额和转账前是一样的。
### 更多示例
参考[dtm-labs/dtm-examples](https://github.com/dtm-labs/dtm-examples)
## 联系我们
### 公众号
dtm官方公众号:分布式事务,大量干货分享,以及dtm的最新消息
### 交流群
请加 yedf2008 好友或者扫码加好友,验证回复 dtm 按照指引进群
![yedf2008](http://service.ivydad.com/cover/dubbingb6b5e2c0-2d2a-cd59-f7c5-c6b90aceb6f1.jpeg)
### github
作者github: [https://github.com/yedf2](https://github.com/yedf2)
欢迎使用[dtm](https://github.com/dtm-labs/dtm),或者通过dtm学习实践分布式事务相关知识,欢迎star支持我们
## 谁在使用
<div style='vertical-align: middle'>
<img alt='腾讯' height='80' src='https://dtm.pub/assets/tencent.4b87bfd8.jpeg' /img>
<img alt='常青藤爸爸' height='80' src='https://dtm.pub/assets/ivydad.d0f58a94.png' /img>
<img alt='镜小二' height='80' src='https://img.epeijing.cn/official-website/assets/logo.png' /img>
<img alt='极欧科技' height='80' src='https://dtm.pub/assets/jiou.5bed10c2.png' /img>
<img alt='金数智联' height='80' src='https://dtm.pub/assets/gdci.214d305a.png' /img>
</div>

84
README.md

@ -12,9 +12,7 @@ DTM是一款golang开发的分布式事务管理器,解决了跨数据库、
他优雅的解决了幂等、空补偿、悬挂等分布式事务难题,提供了简单易用、高性能、易水平扩展的解决方案。
作者受邀参加中国数据库大会分享[多语言环境下分布式事务实践](http://dtcc.it168.com/yicheng.html#b9)
## 谁在使用dtm
## 谁在使用DTM(仅列出部分)
[Tencent 腾讯](https://dtm.pub/other/using.html#tencent)
[Ivydad 常青藤爸爸](https://dtm.pub/other/using.html#ivydad)
@ -23,26 +21,22 @@ DTM是一款golang开发的分布式事务管理器,解决了跨数据库、
[极欧科技](https://dtm.pub/other/using.html)
[金数智联](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 |备注|
|:-----:|:----:|:----:|:----:|
@ -69,31 +63,27 @@ DTM是一款golang开发的分布式事务管理器,解决了跨数据库、
## 微服务框架支持
- [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/protocol/intro.html)
具体微服务接入使用,参见[微服务支持](https://dtm.pub/protocol/intro.html)
## 快速开始
### 获取代码
`git clone https://github.com/dtm-labs/dtm && cd dtm`
如果您不是Go语言,可以跳转[各语言客户端及示例](https://dtm.pub/summary/code.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"
@ -117,23 +107,33 @@ DTM是一款golang开发的分布式事务管理器,解决了跨数据库、
<img src="https://pic3.zhimg.com/80/v2-b7d98659093c399e182a0173a8e549ca_1440w.jpg" height=428 />
### 完整示例
参考[examples/quick_start.go](./examples/quick_start.go)
### 失败情况
在实际的业务中,子事务可能出现失败,例如转入的子账号被冻结导致转账失败。我们对业务代码进行修改,让TransIn的正向操作失败,然后看看结果
## 公众号
您可以关注公众号:分布式事务,及时跟踪dtm的最新内容
## 交流群
``` go
app.POST(qsBusiAPI+"/TransIn", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
return M{"dtm_result": "FAILURE"}, nil
}))
```
再运行这个例子,整个事务最终失败,时序图如下:
![saga_rollback](https://pic3.zhimg.com/80/v2-8d8f1476be8a1e2e09ce97a89b4116c2_1440w.jpg)
在转入操作失败的情况下,TransIn和TransOut的补偿操作被执行,保证了最终的余额和转账前是一样的。
### 更多示例
参考[dtm-labs/dtm-examples](https://github.com/dtm-labs/dtm-examples)
## 联系我们
### 公众号
dtm官方公众号:分布式事务,大量干货分享,以及dtm的最新消息
### 交流群
请加 yedf2008 好友或者扫码加好友,验证回复 dtm 按照指引进群
![yedf2008](http://service.ivydad.com/cover/dubbingb6b5e2c0-2d2a-cd59-f7c5-c6b90aceb6f1.jpeg)
作者github: [https://github.com/yedf2](https://github.com/yedf2)
欢迎使用[dtm](https://github.com/dtm-labs/dtm),或者通过dtm学习实践分布式事务相关知识,欢迎star支持我们
## 谁在使用
<div style='vertical-align: middle'>
<img alt='腾讯' height='80' src='https://dtm.pub/assets/tencent.4b87bfd8.jpeg' /img>
<img alt='常青藤爸爸' height='80' src='https://dtm.pub/assets/ivydad.d0f58a94.png' /img>
<img alt='镜小二' height='80' src='https://img.epeijing.cn/official-website/assets/logo.png' /img>
<img alt='极欧科技' height='80' src='https://dtm.pub/assets/jiou.5bed10c2.png' /img>
<img alt='金数智联' height='80' src='https://dtm.pub/assets/gdci.214d305a.png' /img>
</div>

82
app/main.go

@ -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/dtm-labs/dtm/common"
"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/storage/registry"
"github.com/dtm-labs/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
}
logger.Infof("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": // do nothing, not fallthrough
default:
// 下面是各类的例子
examples.GrpcStartup()
examples.BaseAppStartup()
sample := examples.Samples[os.Args[1]]
logger.FatalfIf(sample == nil, "no sample name for %s", os.Args[1])
sample.Action()
}
select {}
}

14
bench/main.go

@ -5,12 +5,12 @@ import (
"os"
"github.com/dtm-labs/dtm/bench/svr"
"github.com/dtm-labs/dtm/common"
"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/examples"
"github.com/dtm-labs/dtm/test/busi"
)
var usage = `bench is a bench test server for dtmf
@ -25,23 +25,23 @@ func hintAndExit() {
os.Exit(0)
}
var conf = &common.Config
var conf = &config.Config
func main() {
if len(os.Args) <= 1 {
hintAndExit()
}
logger.Infof("starting bench server")
common.MustLoadConfig()
config.MustLoadConfig("")
logger.InitLog(conf.LogLevel)
if conf.ExamplesDB.Driver != "" {
dtmcli.SetCurrentDBType(conf.ExamplesDB.Driver)
if busi.BusiConf.Driver != "" {
dtmcli.SetCurrentDBType(busi.BusiConf.Driver)
svr.PrepareBenchDB()
}
registry.WaitStoreUp()
dtmsvr.PopulateDB(false)
if os.Args[1] == "db" {
examples.PopulateDB(false)
busi.PopulateDB(false)
} else if os.Args[1] == "redis" || os.Args[1] == "boltdb" {
} else {

34
bench/svr/http.go

@ -14,11 +14,12 @@ import (
"sync/atomic"
"time"
"github.com/dtm-labs/dtm/common"
"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/lithammer/shortuuid"
)
@ -32,14 +33,14 @@ const total = 200000
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.ExamplesDB)
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()
logger.FatalIfError(err)
return tx
@ -48,7 +49,7 @@ func txGet() *sql.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))
@ -69,7 +70,7 @@ var mode string = ""
var sqls int = 1
func PrepareBenchDB() {
db := sdbGet()
db := pdbGet()
_, err := dtmimp.DBExec(db, "drop table if exists dtm_busi.user_account_log")
logger.FatalIfError(err)
_, err = dtmimp.DBExec(db, `create table if not exists dtm_busi.user_account_log (
@ -91,7 +92,7 @@ func PrepareBenchDB() {
// StartSvr 1
func StartSvr() {
app := common.GetGinApp()
app := dtmutil.GetGinApp()
benchAddRoute(app)
logger.Debugf("bench listening at %d", benchPort)
go app.Run(fmt.Sprintf(":%s", benchPort))
@ -127,20 +128,19 @@ func qsAdjustBalance(uid int, amount int, c *gin.Context) (interface{}, error) {
}
func benchAddRoute(app *gin.Engine) {
dtmHttpServer := fmt.Sprintf("http://localhost:%d/api/dtmsvr", common.Config.HttpPort)
app.POST(benchAPI+"/TransIn", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
app.POST(benchAPI+"/TransIn", dtmutil.WrapHandler(func(c *gin.Context) (interface{}, error) {
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.WrapHandler(func(c *gin.Context) (interface{}, error) {
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.WrapHandler(func(c *gin.Context) (interface{}, error) {
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.WrapHandler(func(c *gin.Context) (interface{}, error) {
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.WrapHandler(func(c *gin.Context) (interface{}, error) {
reloadData()
mode = c.Query("m")
s := c.Query("sqls")
@ -149,7 +149,7 @@ func benchAddRoute(app *gin.Engine) {
}
return nil, nil
}))
app.Any(benchAPI+"/bench", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
app.Any(benchAPI+"/bench", dtmutil.WrapHandler(func(c *gin.Context) (interface{}, error) {
uid := (atomic.AddInt32(&uidCounter, 1)-1)%total + 1
suid := fmt.Sprintf("%d", uid)
suid2 := fmt.Sprintf("%d", total+1-uid)
@ -158,7 +158,7 @@ func benchAddRoute(app *gin.Engine) {
params2 := fmt.Sprintf("?uid=%s", suid2)
logger.Debugf("mode: %s contains dtm: %t", mode, strings.Contains(mode, "dtm"))
if strings.Contains(mode, "dtm") {
saga := dtmcli.NewSaga(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
@ -172,10 +172,10 @@ func benchAddRoute(app *gin.Engine) {
}
return nil, nil
}))
app.Any(benchAPI+"/benchEmptyUrl", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
app.Any(benchAPI+"/benchEmptyUrl", dtmutil.WrapHandler(func(c *gin.Context) (interface{}, error) {
gid := shortuuid.New()
req := gin.H{}
saga := dtmcli.NewSaga(dtmHttpServer, gid).
saga := dtmcli.NewSaga(dtmutil.DefaultHttpServer, gid).
Add("", "", req).
Add("", "", req)
saga.WaitResult = true

128
common/config.go

@ -1,128 +0,0 @@
package common
import (
"encoding/json"
"errors"
"io/ioutil"
"path/filepath"
"github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/logger"
"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,
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"`
HttpPort int64 `yaml:"HttpPort" default:"36789"`
GrpcPort int64 `yaml:"GrpcPort" default:"36790"`
MicroService MicroService `yaml:"MicroService"`
UpdateBranchSync int64 `yaml:"UpdateBranchSync"`
LogLevel string `yaml:"LogLevel" default:"info"`
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)
logger.FatalIfError(err)
}
scont, err := json.MarshalIndent(&Config, "", " ")
logger.FatalIfError(err)
logger.Debugf("config is: \n%s", scont)
err = checkConfig()
logger.FatalfIf(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")
}
switch Config.Store.Driver {
case BoltDb:
return nil
case 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 ")
}
case Redis:
if Config.Store.Host == "" {
return errors.New("Redis host not valid ")
}
if Config.Store.Port == 0 {
return errors.New("Redis port not valid ")
}
}
return nil
}

49
common/config_test.go

@ -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)
}

67
common/types_test.go

@ -1,67 +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/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/stretchr/testify/assert"
)
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
}

30
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:
### Store.MaxOpenConns can also specified from env: STORE_MAX_OPEN_CONNS
#####################################################################
# 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'
# TransBranchOp: 'dtm.trans_branch_op'
### flollowing config is only for some Driver
# DataExpire: 604800 # Trans data will expire in 7 days. only for redis/boltdb.
@ -41,10 +50,3 @@ Store: # specify which engine to store trans status
# LogLevel: 'info' # default: info. can be debug|info|warn|error
### dtm can run examples, and examples will use following config to connect db
ExamplesDB:
Driver: 'mysql'
Host: 'localhost'
User: 'root'
Password: ''
Port: 3306

2
dtmcli/barrier.go

@ -54,7 +54,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)
}

23
dtmcli/dtmimp/trans_base.go

@ -40,9 +40,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 +64,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},
}
}
@ -118,6 +116,7 @@ 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)
}

2
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

2
dtmcli/dtmimp/utils.go

@ -168,7 +168,7 @@ func PooledDB(conf DBConf) (*sql.DB, error) {
// StandaloneDB get a standalone db instance
func StandaloneDB(conf DBConf) (*sql.DB, error) {
dsn := GetDsn(conf)
logger.Errorf("opening standalone %s: %s", conf.Driver, strings.Replace(dsn, conf.Password, "****", 1))
logger.Infof("opening standalone %s: %s", conf.Driver, strings.Replace(dsn, conf.Password, "****", 1))
return sql.Open(conf.Driver, dsn)
}

8
dtmcli/dtmimp/vars.go

@ -31,13 +31,19 @@ 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)
logger.Debugf("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 {

6
dtmcli/tcc.go

@ -27,7 +27,13 @@ type TccGlobalFunc func(tcc *Tcc) (*resty.Response, error)
// 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

23
dtmcli/types.go

@ -10,6 +10,7 @@ import (
"fmt"
"github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/go-resty/resty/v2"
)
// MustGenGid generate a new gid
@ -49,3 +50,25 @@ func SetXaSqlTimeoutMs(ms int) {
func GetXaSqlTimeoutMs() int {
return dtmimp.XaSqlTimeoutMs
}
func SetBarrierTableName(tablename string) {
dtmimp.BarrierTableName = tablename
}
// OnBeforeRequest add before request middleware
func OnBeforeRequest(middleware func(c *resty.Client, r *resty.Request) error) {
dtmimp.RestyClient.OnBeforeRequest(middleware)
}
// OnAfterResponse add after request middleware
func OnAfterResponse(middleware func(c *resty.Client, resp *resty.Response) error) {
dtmimp.RestyClient.OnAfterResponse(middleware)
}
// 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
}

1
dtmcli/types_test.go

@ -28,4 +28,5 @@ func TestTypes(t *testing.T) {
func TestXaSqlTimeout(t *testing.T) {
old := GetXaSqlTimeoutMs()
SetXaSqlTimeoutMs(old)
SetBarrierTableName(dtmimp.BarrierTableName) // just cover this func
}

12
dtmcli/xa.go

@ -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)
}
// XaGlobalTransaction 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
})
}

7
dtmgrpc/dtmgimp/grpc_clients.go

@ -13,6 +13,7 @@ import (
"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"
)
@ -35,6 +36,8 @@ func (cb rawCodec) Name() string { return "dtm_raw" }
var normalClients, rawClients sync.Map
var ClientInterceptors = []grpc.UnaryClientInterceptor{}
// MustGetDtmClient 1
func MustGetDtmClient(grpcServer string) dtmgpb.DtmClient {
return dtmgpb.NewDtmClient(MustGetGrpcConn(grpcServer, false))
@ -59,7 +62,9 @@ func GetGrpcConn(grpcServer string, isRaw bool) (conn *grpc.ClientConn, rerr err
opts = grpc.WithDefaultCallOptions(grpc.ForceCodec(rawCodec{}))
}
logger.Debugf("grpc client connecting %s", grpcServer)
conn, rerr := grpc.Dial(grpcServer, grpc.WithInsecure(), grpc.WithUnaryInterceptor(GrpcClientLog), opts)
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

7
dtmgrpc/dtmgimp/types.go

@ -35,12 +35,13 @@ func GrpcServerLog(ctx context.Context, req interface{}, info *grpc.UnaryServerI
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 {
logger.Debugf("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 {
logger.Errorf("%s", res)
} else {

45
dtmgrpc/dtmgimp/utils.go

@ -31,9 +31,11 @@ func DtmGrpcCall(s *dtmimp.TransBase, operation string) error {
Gid: s.Gid,
TransType: s.TransType,
TransOptions: &dtmgpb.DtmTransOptions{
WaitResult: s.WaitResult,
TimeoutToFail: s.TimeoutToFail,
RetryInterval: s.RetryInterval,
WaitResult: s.WaitResult,
TimeoutToFail: s.TimeoutToFail,
RetryInterval: s.RetryInterval,
PassthroughHeaders: s.PassthroughHeaders,
BranchHeaders: s.BranchHeaders,
},
QueryPrepared: s.QueryPrepared,
CustomedData: s.CustomData,
@ -42,20 +44,29 @@ 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)
@ -64,8 +75,12 @@ func LogDtmCtx(ctx context.Context) {
}
}
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 ""
}
@ -75,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)
}

197
dtmgrpc/dtmgpb/dtmgimp.pb.go

@ -26,9 +26,11 @@ type DtmTransOptions struct {
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"`
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() {
@ -84,6 +86,20 @@ func (x *DtmTransOptions) GetRetryInterval() int64 {
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
@ -321,69 +337,82 @@ var file_dtmgrpc_dtmgpb_dtmgimp_proto_rawDesc = []byte{
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,
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, 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,
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, 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,
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 (
@ -398,33 +427,35 @@ func file_dtmgrpc_dtmgpb_dtmgimp_proto_rawDescGZIP() []byte {
return file_dtmgrpc_dtmgpb_dtmgimp_proto_rawDescData
}
var file_dtmgrpc_dtmgpb_dtmgimp_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
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.DtmBranchRequest.DataEntry
(*emptypb.Empty)(nil), // 5: google.protobuf.Empty
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{
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
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() }
@ -488,7 +519,7 @@ func file_dtmgrpc_dtmgpb_dtmgimp_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_dtmgrpc_dtmgpb_dtmgimp_proto_rawDesc,
NumEnums: 0,
NumMessages: 5,
NumMessages: 6,
NumExtensions: 0,
NumServices: 1,
},

2
dtmgrpc/dtmgpb/dtmgimp.proto

@ -18,6 +18,8 @@ message DtmTransOptions {
bool WaitResult = 1;
int64 TimeoutToFail = 2;
int64 RetryInterval = 3;
repeated string PassthroughHeaders = 4;
map<string, string> BranchHeaders = 5;
}
// DtmRequest request sent to dtm server

23
dtmgrpc/tcc.go

@ -14,6 +14,7 @@ import (
"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"
)
@ -30,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 := &dtmgpb.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
}
@ -44,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
}
@ -87,6 +89,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)
}

17
dtmgrpc/type.go

@ -9,10 +9,10 @@ package dtmgrpc
import (
context "context"
"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"
emptypb "google.golang.org/protobuf/types/known/emptypb"
)
@ -24,16 +24,11 @@ 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)
}
func AddUnaryInterceptor(interceptor grpc.UnaryClientInterceptor) {
dtmgimp.ClientInterceptors = append(dtmgimp.ClientInterceptors, interceptor)
}

5
dtmgrpc/type_test.go

@ -10,7 +10,6 @@ import (
"context"
"testing"
"github.com/dtm-labs/dtm/dtmcli"
"github.com/stretchr/testify/assert"
)
@ -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)
}

10
dtmgrpc/xa.go

@ -92,7 +92,13 @@ 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 := &dtmgpb.DtmRequest{
Gid: gid,
@ -107,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)
})
}

6
dtmsvr/api_grpc.go

@ -25,17 +25,17 @@ func (s *dtmServer) NewGid(ctx context.Context, in *emptypb.Empty) (*pb.DtmGidRe
}
func (s *dtmServer) Submit(ctx context.Context, in *pb.DtmRequest) (*emptypb.Empty, error) {
r, err := svcSubmit(TransFromDtmRequest(in))
r, err := svcSubmit(TransFromDtmRequest(ctx, in))
return &emptypb.Empty{}, dtmgimp.Result2Error(r, err)
}
func (s *dtmServer) Prepare(ctx context.Context, in *pb.DtmRequest) (*emptypb.Empty, error) {
r, err := svcPrepare(TransFromDtmRequest(in))
r, err := svcPrepare(TransFromDtmRequest(ctx, in))
return &emptypb.Empty{}, dtmgimp.Result2Error(r, err)
}
func (s *dtmServer) Abort(ctx context.Context, in *pb.DtmRequest) (*emptypb.Empty, error) {
r, err := svcAbort(TransFromDtmRequest(in))
r, err := svcAbort(TransFromDtmRequest(ctx, in))
return &emptypb.Empty{}, dtmgimp.Result2Error(r, err)
}

20
dtmsvr/api_http.go

@ -9,23 +9,23 @@ package dtmsvr
import (
"errors"
"github.com/dtm-labs/dtm/common"
"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"
)
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.WrapHandler(newGid))
engine.POST("/api/dtmsvr/prepare", dtmutil.WrapHandler(prepare))
engine.POST("/api/dtmsvr/submit", dtmutil.WrapHandler(submit))
engine.POST("/api/dtmsvr/abort", dtmutil.WrapHandler(abort))
engine.POST("/api/dtmsvr/registerBranch", dtmutil.WrapHandler(registerBranch))
engine.POST("/api/dtmsvr/registerXaBranch", dtmutil.WrapHandler(registerBranch)) // compatible for old sdk
engine.POST("/api/dtmsvr/registerTccBranch", dtmutil.WrapHandler(registerBranch)) // compatible for old sdk
engine.GET("/api/dtmsvr/query", dtmutil.WrapHandler(query))
engine.GET("/api/dtmsvr/all", dtmutil.WrapHandler(all))
// add prometheus exporter
h := promhttp.Handler()

85
dtmsvr/config/config.go

@ -0,0 +1,85 @@
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 = 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
TransGlobalTable string `yaml:"TransGlobalTable" default:"dtm.trans_global"`
TransBranchOpTable string `yaml:"BranchTransOpTable" default:"dtm.trans_branch_op"`
}
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,
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"`
HttpPort int64 `yaml:"HttpPort" default:"36789"`
GrpcPort int64 `yaml:"GrpcPort" default:"36790"`
MicroService MicroService `yaml:"MicroService"`
UpdateBranchSync int64 `yaml:"UpdateBranchSync"`
LogLevel string `yaml:"LogLevel" default:"info"`
}
// 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)
}

84
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
}

34
common/config_utils.go → dtmsvr/config/config_utils.go

@ -1,6 +1,7 @@
package common
package config
import (
"errors"
"fmt"
"os"
"reflect"
@ -53,3 +54,34 @@ func toUnderscoreUpper(key string) string {
// logger.Debugf("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:
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
}

2
dtmsvr/cron.go

@ -65,7 +65,7 @@ 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)
logger.Debugf("sleeping for %v milli", interval/time.Microsecond)
time.Sleep(interval)

13
dtmsvr/storage/boltdb/boltdb.go

@ -14,13 +14,14 @@ import (
bolt "go.etcd.io/bbolt"
"github.com/dtm-labs/dtm/common"
"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"
)
var config = &common.Config
var conf = &config.Config
type BoltdbStore struct {
}
@ -41,7 +42,7 @@ func boltGet() *bolt.DB {
// 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,
time.Duration(conf.Store.DataExpire)*time.Second,
db,
)
dtmimp.E2P(err)
@ -347,8 +348,8 @@ func (s *BoltdbStore) ChangeGlobalStatus(global *storage.TransGlobalStore, newSt
func (s *BoltdbStore) 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 {
g := tGetGlobal(t, global.Gid)
@ -366,7 +367,7 @@ func (s *BoltdbStore) TouchCronTime(global *storage.TransGlobalStore, nextCronIn
func (s *BoltdbStore) LockOneGlobalTrans(expireIn time.Duration) *storage.TransGlobalStore {
var trans *storage.TransGlobalStore = nil
min := fmt.Sprintf("%d", time.Now().Add(expireIn).Unix())
next := time.Now().Add(time.Duration(config.RetryInterval) * time.Second)
next := time.Now().Add(time.Duration(conf.RetryInterval) * time.Second)
err := boltGet().Update(func(t *bolt.Tx) error {
cursor := t.Bucket(bucketIndex).Cursor()
for trans == nil {

39
dtmsvr/storage/redis/redis.go

@ -8,14 +8,15 @@ import (
"github.com/go-redis/redis/v8"
"github.com/dtm-labs/dtm/common"
"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 common.Config
var config = &common.Config
// 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()
@ -39,7 +40,7 @@ func (s *RedisStore) PopulateData(skipDrop bool) {
func (s *RedisStore) FindTransGlobalStore(gid string) *storage.TransGlobalStore {
logger.Debugf("calling FindTransGlobalStore: %s", gid)
r, err := redisGet().Get(ctx, config.Store.RedisPrefix+"_g_"+gid).Result()
r, err := redisGet().Get(ctx, conf.Store.RedisPrefix+"_g_"+gid).Result()
if err == redis.Nil {
return nil
}
@ -55,7 +56,7 @@ func (s *RedisStore) ScanTransGlobalStores(position *string, limit int64) []stor
if *position != "" {
lid = uint64(dtmimp.MustAtoi(*position))
}
keys, cursor, err := redisGet().Scan(ctx, lid, config.Store.RedisPrefix+"_g_*", limit).Result()
keys, cursor, err := redisGet().Scan(ctx, lid, conf.Store.RedisPrefix+"_g_*", limit).Result()
dtmimp.E2P(err)
globals := []storage.TransGlobalStore{}
if len(keys) > 0 {
@ -77,7 +78,7 @@ func (s *RedisStore) ScanTransGlobalStores(position *string, limit int64) []stor
func (s *RedisStore) FindBranches(gid string) []storage.TransBranchStore {
logger.Debugf("calling FindBranches: %s", gid)
sa, err := redisGet().LRange(ctx, config.Store.RedisPrefix+"_b_"+gid, 0, -1).Result()
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 {
@ -97,14 +98,14 @@ type argList struct {
func newArgList() *argList {
a := &argList{}
return a.AppendRaw(config.Store.RedisPrefix).AppendObject(config.Store.DataExpire)
return a.AppendRaw(conf.Store.RedisPrefix).AppendObject(conf.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")
a.Keys = append(a.Keys, config.Store.RedisPrefix+"_s_"+gid)
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
}
@ -220,9 +221,9 @@ end
func (s *RedisStore) LockOneGlobalTrans(expireIn time.Duration) *storage.TransGlobalStore {
expired := time.Now().Add(expireIn).Unix()
next := time.Now().Add(time.Duration(config.RetryInterval) * time.Second).Unix()
next := time.Now().Add(time.Duration(conf.RetryInterval) * time.Second).Unix()
args := newArgList().AppendGid("").AppendRaw(expired).AppendRaw(next)
lua := `-- LocakOneGlobalTrans
lua := `-- LockOneGlobalTrans
local r = redis.call('ZRANGE', KEYS[3], 0, 0, 'WITHSCORES')
local gid = r[1]
if gid == nil then
@ -249,8 +250,8 @@ return gid
}
func (s *RedisStore) TouchCronTime(global *storage.TransGlobalStore, nextCronInterval int64) {
global.NextCronTime = common.GetNextTime(nextCronInterval)
global.UpdateTime = common.GetNextTime(0)
global.NextCronTime = dtmutil.GetNextTime(nextCronInterval)
global.UpdateTime = dtmutil.GetNextTime(0)
global.NextCronInterval = nextCronInterval
args := newArgList().
AppendGid(global.Gid).
@ -276,11 +277,11 @@ var (
func redisGet() *redis.Client {
once.Do(func() {
logger.Debugf("connecting to redis: %v", config.Store)
logger.Debugf("connecting to redis: %v", conf.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,
Addr: fmt.Sprintf("%s:%d", conf.Store.Host, conf.Store.Port),
Username: conf.Store.User,
Password: conf.Store.Password,
})
})
return rdb

6
dtmsvr/storage/registry/registry.go

@ -3,14 +3,14 @@ package registry
import (
"time"
"github.com/dtm-labs/dtm/common"
"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": &redis.RedisStore{},
@ -20,7 +20,7 @@ var stores map[string]storage.Store = map[string]storage.Store{
}
func GetStore() storage.Store {
return stores[config.Store.Driver]
return stores[conf.Store.Driver]
}
// WaitStoreUp wait for db to go up

32
dtmsvr/storage/sql/sql.go

@ -9,26 +9,27 @@ import (
"gorm.io/gorm"
"gorm.io/gorm/clause"
"github.com/dtm-labs/dtm/common"
"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 config = &common.Config
var conf = &config.Config
type SqlStore struct {
}
func (s *SqlStore) Ping() error {
db, err := dtmimp.StandaloneDB(config.Store.GetDBConf())
db, err := dtmimp.StandaloneDB(conf.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)
file := fmt.Sprintf("%s/dtmsvr.storage.%s.sql", dtmutil.GetSqlDir(), conf.Store.Driver)
dtmutil.RunSQLScript(conf.Store.GetDBConf(), file, skipDrop)
}
func (s *SqlStore) FindTransGlobalStore(gid string) *storage.TransGlobalStore {
@ -84,7 +85,7 @@ func (s *SqlStore) LockGlobalSaveBranches(gid string, status string, branches []
func (s *SqlStore) MaySaveNewTrans(global *storage.TransGlobalStore, branches []storage.TransBranchStore) error {
return dbGet().Transaction(func(db1 *gorm.DB) error {
db := &common.DB{DB: db1}
db := &dtmutil.DB{DB: db1}
dbr := db.Must().Clauses(clause.OnConflict{
DoNothing: true,
}).Create(global)
@ -110,8 +111,8 @@ func (s *SqlStore) ChangeGlobalStatus(global *storage.TransGlobalStore, newStatu
}
func (s *SqlStore) TouchCronTime(global *storage.TransGlobalStore, nextCronInterval int64) {
global.NextCronTime = common.GetNextTime(nextCronInterval)
global.UpdateTime = common.GetNextTime(0)
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)
@ -123,7 +124,7 @@ func (s *SqlStore) LockOneGlobalTrans(expireIn time.Duration) *storage.TransGlob
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]
}[conf.Store.Driver]
}
expire := int(expireIn / time.Second)
whereTime := fmt.Sprintf("next_cron_time < %s", getTime(expire))
@ -135,7 +136,7 @@ func (s *SqlStore) LockOneGlobalTrans(expireIn time.Duration) *storage.TransGlob
Select([]string{"owner", "next_cron_time"}).
Updates(&storage.TransGlobalStore{
Owner: owner,
NextCronTime: common.GetNextTime(common.Config.RetryInterval),
NextCronTime: dtmutil.GetNextTime(conf.RetryInterval),
})
if dbr.RowsAffected == 0 {
return nil
@ -144,8 +145,15 @@ func (s *SqlStore) LockOneGlobalTrans(expireIn time.Duration) *storage.TransGlob
return global
}
func dbGet() *common.DB {
return common.DbGet(config.Store.GetDBConf())
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 {

18
dtmsvr/storage/trans.go

@ -3,13 +3,18 @@ package storage
import (
"time"
"github.com/dtm-labs/dtm/common"
"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"
)
type TransGlobalExt struct {
Headers map[string]string `json:"headers,omitempty" gorm:"-"`
}
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:"-"`
@ -18,7 +23,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"`
@ -26,12 +30,14 @@ 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 (g *TransGlobalStore) TableName() string {
return "dtm.trans_global"
return config.Config.Store.TransGlobalTable
}
func (g *TransGlobalStore) String() string {
@ -40,7 +46,7 @@ func (g *TransGlobalStore) String() string {
// TransBranchStore branch transaction
type TransBranchStore struct {
common.ModelBase
dtmutil.ModelBase
Gid string `json:"gid,omitempty"`
URL string `json:"url,omitempty"`
BinData []byte
@ -53,7 +59,7 @@ type TransBranchStore struct {
// TableName TableName
func (b *TransBranchStore) TableName() string {
return "dtm.trans_branch_op"
return config.Config.Store.TransBranchOpTable
}
func (b *TransBranchStore) String() string {

19
dtmsvr/svr.go

@ -11,10 +11,10 @@ import (
"net"
"time"
"github.com/dtm-labs/dtm/common"
"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/dtmutil"
"github.com/dtm-labs/dtmdriver"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
"google.golang.org/grpc"
@ -23,13 +23,13 @@ import (
// StartSvr StartSvr
func StartSvr() {
logger.Infof("start dtmsvr")
app := common.GetGinApp()
app := dtmutil.GetGinApp()
app = httpMetrics(app)
addRoute(app)
logger.Infof("dtmsvr listen at: %d", config.HttpPort)
go app.Run(fmt.Sprintf(":%d", config.HttpPort))
logger.Infof("dtmsvr listen at: %d", conf.HttpPort)
go app.Run(fmt.Sprintf(":%d", conf.HttpPort))
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", config.GrpcPort))
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", conf.GrpcPort))
logger.FatalIfError(err)
s := grpc.NewServer(
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
@ -44,9 +44,9 @@ func StartSvr() {
go updateBranchAsync()
time.Sleep(100 * time.Millisecond)
err = dtmdriver.Use(config.MicroService.Driver)
err = dtmdriver.Use(conf.MicroService.Driver)
logger.FatalIfError(err)
err = dtmdriver.GetDriver().RegisterGrpcService(config.MicroService.Target, config.MicroService.EndPoint)
err = dtmdriver.GetDriver().RegisterGrpcService(conf.MicroService.Target, conf.MicroService.EndPoint)
logger.FatalIfError(err)
}
@ -61,7 +61,7 @@ var updateBranchAsyncChan chan branchStatus = make(chan branchStatus, 1000)
func updateBranchAsync() {
for { // flush branches every second
defer common.RecoverPanic(nil)
defer dtmutil.RecoverPanic(nil)
updates := []TransBranch{}
started := time.Now()
checkInterval := 20 * time.Millisecond
@ -69,7 +69,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,
})

7
dtmsvr/svr_imports.go

@ -1,7 +0,0 @@
package dtmsvr
import (
_ "github.com/dtm-labs/dtmdriver-gozero"
_ "github.com/dtm-labs/dtmdriver-polaris"
_ "github.com/dtm-labs/dtmdriver-protocol1"
)

31
dtmsvr/trans_class.go

@ -7,11 +7,13 @@
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"
@ -69,11 +71,21 @@ 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 *dtmgpb.DtmRequest) *TransGlobal {
func TransFromDtmRequest(ctx context.Context, c *dtmgpb.DtmRequest) *TransGlobal {
o := &dtmgpb.DtmTransOptions{}
if c.TransOptions != nil {
o = c.TransOptions
@ -85,13 +97,24 @@ func TransFromDtmRequest(c *dtmgpb.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
}

11
dtmsvr/trans_process.go

@ -9,10 +9,10 @@ package dtmsvr
import (
"time"
"github.com/dtm-labs/dtm/common"
"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
@ -26,6 +26,9 @@ func (t *TransGlobal) process(branches []TransBranch) map[string]interface{} {
if t.Options != "" {
dtmimp.MustUnmarshalString(t.Options, &t.TransOptions)
}
if t.ExtData != "" {
dtmimp.MustUnmarshalString(t.ExtData, &t.Ext)
}
if !t.WaitResult {
go t.processInner(branches)
@ -62,7 +65,11 @@ func (t *TransGlobal) processInner(branches []TransBranch) (rerr error) {
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 = ""

16
dtmsvr/trans_status.go

@ -17,6 +17,7 @@ import (
"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"
)
@ -47,19 +48,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
@ -83,6 +84,9 @@ func (t *TransGlobal) getURLResult(url string, branchID, op string, branchPayloa
}
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
@ -106,6 +110,8 @@ 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
@ -136,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)
@ -154,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
}
}

12
dtmsvr/trans_type_saga.go

@ -20,7 +20,8 @@ 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 {
@ -114,7 +115,8 @@ func (t *transSagaProcessor) ProcessOnce(branches []TransBranch) error {
toRun := []int{}
for current := 0; current < n; current++ {
br := &branchResults[current]
if br.op == dtmcli.BranchAction && !br.started && isPreconditionsSucceed(current) && br.status == dtmcli.StatusPrepared {
if br.op == dtmcli.BranchAction && !br.started && isPreconditionsSucceed(current) &&
br.status == dtmcli.StatusPrepared {
toRun = append(toRun, current)
}
}
@ -132,11 +134,13 @@ func (t *transSagaProcessor) ProcessOnce(branches []TransBranch) error {
}
pickAndRunCompensates := func(toRunActions []int) {
for _, b := range toRunActions {
// these branches may have run. so flag them to status succeed, then run the corresponding compensate
// 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 {
if b.op == dtmcli.BranchCompensate && b.status != dtmcli.StatusSucceed &&
branchResults[i+1].status != dtmcli.StatusPrepared {
rsCToStart++
go asyncExecBranch(i)
}

5
dtmsvr/utils.go

@ -10,8 +10,8 @@ import (
"fmt"
"time"
"github.com/dtm-labs/dtm/common"
"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"
@ -19,6 +19,7 @@ import (
type branchStatus struct {
id uint64
gid string
status string
finishTime *time.Time
}
@ -26,7 +27,7 @@ type branchStatus struct {
var p2e = dtmimp.P2E
var e2p = dtmimp.E2P
var config = &common.Config
var conf = &config.Config
func GetStore() storage.Store {
return registry.GetStore()

4
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))
}

14
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"
)

16
common/db.go → dtmutil/db.go

@ -1,4 +1,4 @@
package common
package dtmutil
import (
"database/sql"
@ -96,16 +96,8 @@ 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 {
@ -116,7 +108,9 @@ func DbGet(conf dtmcli.DBConf) *DB {
dtmimp.E2P(err)
db1.Use(&tracePlugin{})
db = &DB{DB: db1}
SetDBConn(db.(*DB))
for _, op := range ops {
op(db1)
}
dbs.Store(dsn, db)
}
return db.(*DB)

2
common/utils.go → dtmutil/utils.go

@ -4,7 +4,7 @@
* license that can be found in the LICENSE file.
*/
package common
package dtmutil
import (
"bytes"

4
common/utils_test.go → dtmutil/utils_test.go

@ -4,7 +4,7 @@
* license that can be found in the LICENSE file.
*/
package common
package dtmutil
import (
"errors"
@ -16,7 +16,7 @@ import (
"testing"
"github.com/gin-gonic/gin"
"github.com/go-playground/assert/v2"
"github.com/stretchr/testify/assert"
)
func TestGin(t *testing.T) {

14
examples/base.go

@ -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)
}

330
examples/busi.pb.go

@ -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
}

55
examples/data.go

@ -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/dtm-labs/dtm/common"
"github.com/dtm-labs/dtm/dtmcli/logger"
)
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) {
logger.FatalfIf(Samples[name] != nil, "%s already exists", name)
Samples[name] = &sampleInfo{Arg: name, Action: fn}
}

25
examples/grpc_msg.go

@ -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/dtm-labs/dtm/dtmcli/logger"
dtmgrpc "github.com/dtm-labs/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()
logger.FatalIfError(err)
return msg.Gid
})
}

36
examples/grpc_saga.go

@ -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/dtm-labs/dtm/dtmcli/logger"
dtmgrpc "github.com/dtm-labs/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/TransInRevert", req)
err := saga.Submit()
logger.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/TransInRevert", req)
saga.WaitResult = true
err := saga.Submit()
logger.FatalIfError(err)
return saga.Gid
})
}

70
examples/grpc_saga_barrier.go

@ -1,70 +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/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/dtmcli/logger"
"github.com/dtm-labs/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()
logger.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, "")
})
}

32
examples/grpc_tcc.go

@ -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/dtm-labs/dtm/dtmcli/logger"
dtmgrpc "github.com/dtm-labs/dtm/dtmgrpc"
emptypb "google.golang.org/protobuf/types/known/emptypb"
)
func init() {
addSample("grpc_tcc", func() string {
logger.Debugf("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
})
logger.FatalIfError(err)
return gid
})
}

37
examples/grpc_xa.go

@ -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/dtm-labs/dtm/dtmcli/logger"
"github.com/dtm-labs/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
})
logger.FatalIfError(err)
return gid
})
}
func (s *busiServer) XaNotify(ctx context.Context, in *emptypb.Empty) (*emptypb.Empty, error) {
return XaGrpcClient.HandleCallback(ctx)
}

29
examples/http_gorm_xa.go

@ -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/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/logger"
"github.com/go-resty/resty/v2"
)
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")
})
logger.FatalIfError(err)
return gid
})
}

28
examples/http_msg.go

@ -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/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/logger"
)
func init() {
addSample("msg", func() string {
logger.Debugf("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")
logger.FatalIfError(err)
logger.Debugf("busi trans submit")
err = msg.Submit()
logger.FatalIfError(err)
return msg.Gid
})
}

56
examples/http_saga.go

@ -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/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/logger"
)
func init() {
addSample("saga", func() string {
logger.Debugf("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)
logger.Debugf("saga busi trans submit")
err := saga.Submit()
logger.Debugf("result gid is: %s", saga.Gid)
logger.FatalIfError(err)
return saga.Gid
})
addSample("saga_wait", func() string {
logger.Debugf("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()
logger.Debugf("result gid is: %s", saga.Gid)
logger.FatalIfError(err)
return saga.Gid
})
addSample("concurrent_saga", func() string {
logger.Debugf("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})
logger.Debugf("concurrent saga busi trans submit")
err := csaga.Submit()
logger.Debugf("result gid is: %s", csaga.Gid)
logger.FatalIfError(err)
return csaga.Gid
})
}

79
examples/http_saga_barrier.go

@ -1,79 +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/dtm-labs/dtm/common"
"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"
)
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 {
logger.Debugf("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)
logger.Debugf("busi trans submit")
err := saga.Submit()
logger.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)
})
}

43
examples/http_saga_gorm_barrier.go

@ -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/dtm-labs/dtm/common"
"github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/logger"
"github.com/gin-gonic/gin"
)
func init() {
setupFuncs["SagaGormBarrierSetup"] = func(app *gin.Engine) {
app.POST(BusiAPI+"/SagaBTransOutGorm", common.WrapHandler(sagaGormBarrierTransOut))
}
addSample("saga_gorm_barrier", func() string {
logger.Debugf("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)
logger.Debugf("busi trans submit")
err := saga.Submit()
logger.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
})
}

51
examples/http_tcc.go

@ -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/dtm-labs/dtm/common"
"github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/logger"
"github.com/gin-gonic/gin"
"github.com/go-resty/resty/v2"
)
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())
logger.FatalIfError(err)
logger.Debugf("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")
})
logger.FatalIfError(err)
return gid
})
addSample("tcc", func() string {
logger.Debugf("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")
})
logger.FatalIfError(err)
return gid
})
}

111
examples/http_tcc_barrier.go

@ -1,111 +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/dtm-labs/dtm/common"
"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"
"github.com/go-resty/resty/v2"
)
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 {
logger.Debugf("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")
})
logger.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)
})
}

42
examples/http_xa.go

@ -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/dtm-labs/dtm/common"
"github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/logger"
"github.com/gin-gonic/gin"
"github.com/go-resty/resty/v2"
)
// 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"))
}))
})
logger.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")
})
logger.FatalIfError(err)
return gid
})
}

2
go.mod

@ -8,11 +8,9 @@ require (
github.com/dtm-labs/dtmdriver-polaris v0.0.2
github.com/dtm-labs/dtmdriver-protocol1 v0.0.1
github.com/gin-gonic/gin v1.6.3
github.com/go-playground/assert/v2 v2.0.1
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

7
go.sum

@ -509,6 +509,12 @@ 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/go.mod h1:0BdQvxXlGOlF6YVlsDoVvu8jyxdTlJZ9Kyh5t9lRA94=
github.com/yedf/dtm v1.7.2 h1:qGjio5O+Zm5CmMfga9Eb0rsUu9nkNHxGuJwc7PEkMfc=
github.com/yedf/dtm v1.7.2/go.mod h1:R1Q55spqLh7yHMVJhGI8RpS1iG7OvRX99pWZRlsAtME=
github.com/yedf/dtmdriver v0.0.0-20211203060147-29426c663b6e/go.mod h1:aeo6ZWiVI0x8P8O18r6uB1cG2uw9BCQyYZaH15MlRDI=
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/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=
@ -877,6 +883,7 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.20.12 h1:LfRpmRkJLwPP8eaYehsVVmIIfg1yCBIIUHaSsdqCgHA=
k8s.io/api v0.20.12/go.mod h1:A2brwyEkVLM3wQGNnzoAa5JsQRzHK0uoOQ+bsnv7V68=

4
helper/.goreleaser.yml

@ -10,6 +10,6 @@ builds:
- 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}}

6
helper/Dockerfile-release

@ -6,10 +6,10 @@ 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/
COPY --from=builder /app/dtm/dtm /app/dtm/
ENV IS_DOCKER=1
WORKDIR /app/dtm
CMD ["/app/dtm/main", "dtmsvr"]
ENTRYPOINT ["/app/dtm/dtm"]

2
helper/sync-dtmcli.sh

@ -30,8 +30,6 @@ 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

2
helper/test-cover.sh

@ -2,7 +2,7 @@ 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/common,github.com/dtm-labs/dtm/dtmcli,github.com/dtm-labs/dtm/dtmcli/dtmimp,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/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/dtmsvr/storage/boltdb,github.com/dtm-labs/dtm/dtmsvr/storage/registry -gcflags=-l $d
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
if [ -f profile.out ]; then
cat profile.out >> coverage.txt
echo > profile.out

71
main.go

@ -0,0 +1,71 @@
/*
* 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"
// load the microserver driver
_ "github.com/dtm-labs/dtmdriver-gozero"
_ "github.com/dtm-labs/dtmdriver-polaris"
_ "github.com/dtm-labs/dtmdriver-protocol1"
)
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)
if *isDebug {
config.Config.LogLevel = "debug"
}
if *isReset {
dtmsvr.PopulateDB(false)
}
maxprocs.Set(maxprocs.Logger(logger.Infof))
registry.WaitStoreUp()
dtmsvr.StartSvr() // 启动dtmsvr的api服务
go dtmsvr.CronExpiredTrans(-1) // 启动dtmsvr的定时过期查询
select {}
}

18
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)
}

0
sqls/examples.mysql.sql → sqls/busi.mysql.sql

0
sqls/examples.postgres.sql → sqls/busi.postgres.sql

4
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`),

4
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)
);

46
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;

14
test/api_test.go

@ -11,7 +11,7 @@ import (
"testing"
"github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/examples"
"github.com/dtm-labs/dtm/dtmutil"
"github.com/stretchr/testify/assert"
)
@ -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,7 +67,7 @@ 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)

12
test/base_test.go

@ -11,17 +11,17 @@ import (
"fmt"
"testing"
"github.com/dtm-labs/dtm/common"
"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/examples"
"github.com/dtm-labs/dtm/dtmutil"
"github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
)
// BarrierModel barrier model for gorm
type BarrierModel struct {
common.ModelBase
dtmutil.ModelBase
dtmcli.BranchBarrier
}
@ -30,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",
@ -60,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")
}

124
test/busi/barrier.go

@ -0,0 +1,124 @@
/*
* 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["TccBarrierSetup"] = func(app *gin.Engine) {
app.POST(BusiAPI+"/SagaBTransIn", dtmutil.WrapHandler(func(c *gin.Context) (interface{}, error) {
barrier := MustBarrierFromGin(c)
return dtmcli.MapSuccess, barrier.Call(txGet(), func(tx *sql.Tx) error {
return sagaAdjustBalance(tx, transInUID, reqFrom(c).Amount, reqFrom(c).TransInResult)
})
}))
app.POST(BusiAPI+"/SagaBTransInCompensate", dtmutil.WrapHandler(func(c *gin.Context) (interface{}, error) {
barrier := MustBarrierFromGin(c)
return dtmcli.MapSuccess, barrier.Call(txGet(), func(tx *sql.Tx) error {
return sagaAdjustBalance(tx, transInUID, -reqFrom(c).Amount, "")
})
}))
app.POST(BusiAPI+"/SagaBTransOut", dtmutil.WrapHandler(func(c *gin.Context) (interface{}, error) {
barrier := MustBarrierFromGin(c)
return dtmcli.MapSuccess, barrier.Call(txGet(), func(tx *sql.Tx) error {
return sagaAdjustBalance(tx, transOutUID, -reqFrom(c).Amount, reqFrom(c).TransOutResult)
})
}))
app.POST(BusiAPI+"/SagaBTransOutCompensate", dtmutil.WrapHandler(func(c *gin.Context) (interface{}, error) {
barrier := MustBarrierFromGin(c)
return dtmcli.MapSuccess, barrier.Call(txGet(), func(tx *sql.Tx) error {
return sagaAdjustBalance(tx, transOutUID, reqFrom(c).Amount, "")
})
}))
app.POST(BusiAPI+"/SagaBTransOutGorm", dtmutil.WrapHandler(func(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, transOutUID).Error
})
}))
app.POST(BusiAPI+"/TccBTransInTry", dtmutil.WrapHandler(func(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 tccAdjustTrading(tx, transInUID, req.Amount)
})
}))
app.POST(BusiAPI+"/TccBTransInConfirm", dtmutil.WrapHandler(func(c *gin.Context) (interface{}, error) {
return dtmcli.MapSuccess, MustBarrierFromGin(c).Call(txGet(), func(tx *sql.Tx) error {
return tccAdjustBalance(tx, transInUID, reqFrom(c).Amount)
})
}))
app.POST(BusiAPI+"/TccBTransInCancel", dtmutil.WrapHandler(func(c *gin.Context) (interface{}, error) {
return dtmcli.MapSuccess, MustBarrierFromGin(c).Call(txGet(), func(tx *sql.Tx) error {
return tccAdjustTrading(tx, transInUID, -reqFrom(c).Amount)
})
}))
app.POST(BusiAPI+"/TccBTransOutTry", dtmutil.WrapHandler(func(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 tccAdjustTrading(tx, transOutUID, -req.Amount)
})
}))
app.POST(BusiAPI+"/TccBTransOutConfirm", dtmutil.WrapHandler(func(c *gin.Context) (interface{}, error) {
return dtmcli.MapSuccess, MustBarrierFromGin(c).Call(txGet(), func(tx *sql.Tx) error {
return tccAdjustBalance(tx, transOutUID, -reqFrom(c).Amount)
})
}))
app.POST(BusiAPI+"/TccBTransOutCancel", dtmutil.WrapHandler(TccBarrierTransOutCancel))
}
}
// 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 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, "")
})
}

62
examples/base_grpc.go → test/busi/base_grpc.go

@ -4,26 +4,24 @@
* 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/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"
)
@ -38,13 +36,13 @@ 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))
conn, err := grpc.Dial(dtmutil.DefaultGrpcServer, grpc.WithInsecure(), grpc.WithUnaryInterceptor(dtmgimp.GrpcClientLog))
logger.FatalIfError(err)
DtmClient = dtmgpb.NewDtmClient(conn)
logger.Debugf("dtm client inited")
@ -58,23 +56,9 @@ func GrpcStartup() {
err := s.Serve(lis)
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)
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.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
}
@ -118,21 +102,13 @@ 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)
})
}
@ -140,7 +116,27 @@ func (s *busiServer) TransInTccNested(ctx context.Context, in *BusiReq) (*emptyp
tcc, err := dtmgrpc.TccFromGrpc(ctx)
logger.FatalIfError(err)
r := &emptypb.Empty{}
err = tcc.CallBranch(in, BusiGrpc+"/examples.Busi/TransIn", BusiGrpc+"/examples.Busi/TransInConfirm", BusiGrpc+"/examples.Busi/TransInRevert", r)
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
}

138
examples/base_http.go → test/busi/base_http.go

@ -4,19 +4,17 @@
* license that can be found in the LICENSE file.
*/
package examples
package busi
import (
"database/sql"
"errors"
"fmt"
"strings"
"time"
"github.com/dtm-labs/dtm/common"
"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"
@ -39,10 +37,20 @@ 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{}, error)
var sleepCancelHandler SleepCancelHandler = nil
func SetSleepCancelHandler(handler SleepCancelHandler) {
sleepCancelHandler = handler
}
// BaseAppStartup base app startup
func BaseAppStartup() *gin.Engine {
logger.Debugf("examples starting")
app := common.GetGinApp()
logger.Infof("examples starting")
app := dtmutil.GetGinApp()
app.Use(func(c *gin.Context) {
v := MainSwitch.NextResult.Fetch()
if v != "" {
@ -52,6 +60,13 @@ func BaseAppStartup() *gin.Engine {
}
c.Next()
})
var err error
XaClient, err = dtmcli.NewXaClient(dtmutil.DefaultHttpServer, BusiConf, Busi+"/xa", func(path string, xa *dtmcli.XaClient) {
app.POST(path, dtmutil.WrapHandler(func(c *gin.Context) (interface{}, error) {
return xa.HandleCallback(c.Query("gid"), c.Query("branch_id"), c.Query("op"))
}))
})
logger.FatalIfError(err)
BaseAddRoute(app)
for k, v := range setupFuncs {
@ -61,107 +76,53 @@ func BaseAppStartup() *gin.Engine {
logger.Debugf("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)
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 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) {
app.POST(BusiAPI+"/TransIn", dtmutil.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) {
app.POST(BusiAPI+"/TransOut", dtmutil.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) {
app.POST(BusiAPI+"/TransInConfirm", dtmutil.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) {
app.POST(BusiAPI+"/TransOutConfirm", dtmutil.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) {
app.POST(BusiAPI+"/TransInRevert", dtmutil.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) {
app.POST(BusiAPI+"/TransOutRevert", dtmutil.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) {
app.GET(BusiAPI+"/CanSubmit", dtmutil.WrapHandler(func(c *gin.Context) (interface{}, error) {
logger.Debugf("%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) {
app.POST(BusiAPI+"/TransInXa", dtmutil.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 sagaAdjustBalance(db, transInUID, reqFrom(c).Amount, reqFrom(c).TransInResult)
})
return error2Resp(err)
}))
app.POST(BusiAPI+"/TransOutXa", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
app.POST(BusiAPI+"/TransOutXa", dtmutil.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 sagaAdjustBalance(db, transOutUID, reqFrom(c).Amount, reqFrom(c).TransOutResult)
})
return error2Resp(err)
}))
app.POST(BusiAPI+"/TransOutXaGorm", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
app.POST(BusiAPI+"/TransInTccParent", dtmutil.WrapHandler(func(c *gin.Context) (interface{}, error) {
tcc, err := dtmcli.TccFromQuery(c.Request.URL.Query())
logger.FatalIfError(err)
logger.Debugf("TransInTccParent ")
return tcc.CallBranch(&TransReq{Amount: reqFrom(c).Amount}, Busi+"/TransIn", Busi+"/TransInConfirm", Busi+"/TransInRevert")
}))
app.POST(BusiAPI+"/TransOutXaGorm", dtmutil.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
@ -176,13 +137,13 @@ func BaseAddRoute(app *gin.Engine) {
if err != nil {
return err
}
dbr := gdb.Exec("update dtm_busi.user_account set balance=balance-? where user_id=?", reqFrom(c).Amount, 1)
dbr := gdb.Exec("update dtm_busi.user_account set balance=balance-? where user_id=?", reqFrom(c).Amount, transOutUID)
return dbr.Error
})
return error2Resp(err)
}))
app.POST(BusiAPI+"/TestPanic", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
app.POST(BusiAPI+"/TestPanic", dtmutil.WrapHandler(func(c *gin.Context) (interface{}, error) {
if c.Query("panic_error") != "" {
panic(errors.New("panic_error"))
} else if c.Query("panic_string") != "" {
@ -190,4 +151,21 @@ func BaseAddRoute(app *gin.Engine) {
}
return "SUCCESS", nil
}))
app.POST(BusiAPI+"/TccBSleepCancel", dtmutil.WrapHandler(func(c *gin.Context) (interface{}, error) {
return sleepCancelHandler(c)
}))
app.POST(BusiAPI+"/TransOutHeaderYes", dtmutil.WrapHandler(func(c *gin.Context) (interface{}, error) {
h := c.GetHeader("test_header")
if h == "" {
return nil, errors.New("no test_header found in TransOutHeaderYes")
}
return handleGeneralBusiness(c, MainSwitch.TransOutResult.Fetch(), reqFrom(c).TransOutResult, "TransOut")
}))
app.POST(BusiAPI+"/TransOutHeaderNo", dtmutil.WrapHandler(func(c *gin.Context) (interface{}, error) {
h := c.GetHeader("test_header")
if h != "" {
return nil, errors.New("test_header found in TransOutHeaderNo")
}
return dtmcli.MapSuccess, nil
}))
}

84
examples/base_types.go → test/busi/base_types.go

@ -4,32 +4,50 @@
* license that can be found in the LICENSE file.
*/
package examples
package busi
import (
"context"
"database/sql"
"fmt"
"github.com/dtm-labs/dtm/common"
"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/gin-gonic/gin"
)
// DtmHttpServer dtm service address
var DtmHttpServer = fmt.Sprintf("http://localhost:%d/api/dtmsvr", 36789)
var BusiConf = dtmcli.DBConf{
Driver: "mysql",
Host: "localhost",
Port: 3306,
User: "root",
}
// DtmGrpcServer dtm grpc service address
var DtmGrpcServer = fmt.Sprintf("localhost:%d", 36790)
type UserAccount struct {
UserId int
Balance string
TradingBalance string
}
func (*UserAccount) TableName() string {
return "dtm_busi.user_account"
}
func GetUserAccountByUid(uid int) *UserAccount {
ua := UserAccount{}
dbr := dbGet().Must().Model(&ua).Where("user_id=?", uid).First(&ua)
dtmimp.E2P(dbr.Error)
return &ua
}
func IsEqual(ua1, ua2 *UserAccount) bool {
return ua1.UserId == ua2.UserId && ua1.Balance == ua2.Balance && ua1.TradingBalance == ua2.TradingBalance
}
// TransReq transaction request payload
type TransReq struct {
Amount int `json:"amount"`
TransInResult string `json:"transInResult"`
TransOutResult string `json:"transOutResult"`
TransInResult string `json:"trans_in_result"`
TransOutResult string `json:"trans_out_Result"`
}
func (t *TransReq) String() string {
@ -76,33 +94,33 @@ func infoFromContext(c *gin.Context) *dtmcli.BranchBarrier {
return &info
}
func dbGet() *common.DB {
return common.DbGet(config.ExamplesDB)
// AutoEmptyString auto reset to empty when used once
type AutoEmptyString struct {
value string
}
func sdbGet() *sql.DB {
db, err := dtmimp.PooledDB(config.ExamplesDB)
logger.FatalIfError(err)
return db
// SetOnce set a value once
func (s *AutoEmptyString) SetOnce(v string) {
s.value = v
}
func txGet() *sql.Tx {
db := sdbGet()
tx, err := db.Begin()
logger.FatalIfError(err)
return tx
// Fetch fetch the stored value, then reset the value to empty
func (s *AutoEmptyString) Fetch() string {
v := s.value
s.value = ""
return v
}
// MustBarrierFromGin 1
func MustBarrierFromGin(c *gin.Context) *dtmcli.BranchBarrier {
ti, err := dtmcli.BarrierFromQuery(c.Request.URL.Query())
logger.FatalIfError(err)
return ti
type mainSwitchType struct {
TransInResult AutoEmptyString
TransOutResult AutoEmptyString
TransInConfirmResult AutoEmptyString
TransOutConfirmResult AutoEmptyString
TransInRevertResult AutoEmptyString
TransOutRevertResult AutoEmptyString
CanSubmitResult AutoEmptyString
NextResult AutoEmptyString
}
// MustBarrierFromGrpc 1
func MustBarrierFromGrpc(ctx context.Context) *dtmcli.BranchBarrier {
ti, err := dtmgrpc.BarrierFromGrpc(ctx)
logger.FatalIfError(err)
return ti
}
// MainSwitch controls busi success or fail
var MainSwitch mainSwitchType

85
test/busi/busi.go

@ -0,0 +1,85 @@
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.Aborted, 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{}, 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 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
}
return gin.H{"dtm_result": dtmcli.ResultSuccess}, 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
}

337
test/busi/busi.pb.go

@ -0,0 +1,337 @@
// 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, 0xce, 0x08, 0x0a, 0x04, 0x42, 0x75, 0x73, 0x69, 0x12, 0x2d, 0x0a, 0x09, 0x43, 0x61,
0x6e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 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, 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, 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.CanSubmit:input_type -> busi.BusiReq
0, // 1: busi.Busi.TransIn:input_type -> busi.BusiReq
0, // 2: busi.Busi.TransOut:input_type -> busi.BusiReq
0, // 3: busi.Busi.TransInRevert:input_type -> busi.BusiReq
0, // 4: busi.Busi.TransOutRevert:input_type -> busi.BusiReq
0, // 5: busi.Busi.TransInConfirm:input_type -> busi.BusiReq
0, // 6: busi.Busi.TransOutConfirm:input_type -> busi.BusiReq
2, // 7: busi.Busi.XaNotify:input_type -> google.protobuf.Empty
0, // 8: busi.Busi.TransInXa:input_type -> busi.BusiReq
0, // 9: busi.Busi.TransOutXa:input_type -> busi.BusiReq
0, // 10: busi.Busi.TransInTcc:input_type -> busi.BusiReq
0, // 11: busi.Busi.TransOutTcc:input_type -> busi.BusiReq
0, // 12: busi.Busi.TransInTccNested:input_type -> busi.BusiReq
0, // 13: busi.Busi.TransInBSaga:input_type -> busi.BusiReq
0, // 14: busi.Busi.TransOutBSaga:input_type -> busi.BusiReq
0, // 15: busi.Busi.TransInRevertBSaga:input_type -> busi.BusiReq
0, // 16: busi.Busi.TransOutRevertBSaga:input_type -> busi.BusiReq
0, // 17: busi.Busi.TransOutHeaderYes:input_type -> busi.BusiReq
0, // 18: busi.Busi.TransOutHeaderNo:input_type -> busi.BusiReq
1, // 19: busi.Busi.CanSubmit:output_type -> busi.BusiReply
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
19, // [19:38] is the sub-list for method output_type
0, // [0:19] 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
}

6
examples/busi.proto → 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 {
@ -36,5 +36,7 @@ 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) {}
}

146
examples/busi_grpc.pb.go → 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"
@ -36,6 +36,8 @@ 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)
}
type busiClient struct {
@ -48,7 +50,7 @@ func NewBusiClient(cc grpc.ClientConnInterface) BusiClient {
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...)
err := c.cc.Invoke(ctx, "/busi.Busi/CanSubmit", in, out, opts...)
if err != nil {
return nil, err
}
@ -57,7 +59,7 @@ func (c *busiClient) CanSubmit(ctx context.Context, in *BusiReq, opts ...grpc.Ca
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 +68,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 +77,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 +86,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 +95,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 +104,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 +113,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 +122,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 +131,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 +140,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 +149,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 +158,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 +167,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 +176,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 +185,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 +194,25 @@ 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
}
@ -220,6 +240,8 @@ 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)
mustEmbedUnimplementedBusiServer()
}
@ -278,6 +300,12 @@ 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) mustEmbedUnimplementedBusiServer() {}
// UnsafeBusiServer may be embedded to opt out of forward compatibility for this service.
@ -301,7 +329,7 @@ func _Busi_CanSubmit_Handler(srv interface{}, ctx context.Context, dec func(inte
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/examples.Busi/CanSubmit",
FullMethod: "/busi.Busi/CanSubmit",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BusiServer).CanSubmit(ctx, req.(*BusiReq))
@ -319,7 +347,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 +365,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 +383,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 +401,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 +419,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 +437,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 +455,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 +473,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 +491,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 +509,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 +527,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 +545,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 +563,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 +581,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 +599,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 +617,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,11 +625,47 @@ 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)
}
// 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{
{
@ -672,7 +736,15 @@ var Busi_ServiceDesc = grpc.ServiceDesc{
MethodName: "TransOutRevertBSaga",
Handler: _Busi_TransOutRevertBSaga_Handler,
},
{
MethodName: "TransOutHeaderYes",
Handler: _Busi_TransOutHeaderYes_Handler,
},
{
MethodName: "TransOutHeaderNo",
Handler: _Busi_TransOutHeaderNo_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "examples/busi.proto",
Metadata: "test/busi/busi.proto",
}

40
examples/quick_start.go → test/busi/quick_start.go

@ -4,16 +4,15 @@
* license that can be found in the LICENSE file.
*/
package examples
package busi
import (
"fmt"
"time"
"github.com/dtm-labs/dtm/common"
"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"
)
@ -25,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)
logger.Debugf("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()
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.WrapHandler(func(c *gin.Context) (interface{}, error) {
logger.Infof("TransIn")
return dtmcli.MapSuccess, nil
}))
app.POST(qsBusiAPI+"/TransInCompensate", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
return qsAdjustBalance(2, -30)
app.POST(qsBusiAPI+"/TransInCompensate", dtmutil.WrapHandler(func(c *gin.Context) (interface{}, error) {
logger.Infof("TransInCompensate")
return dtmcli.MapSuccess, nil
}))
app.POST(qsBusiAPI+"/TransOut", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
return qsAdjustBalance(1, -30)
app.POST(qsBusiAPI+"/TransOut", dtmutil.WrapHandler(func(c *gin.Context) (interface{}, error) {
logger.Infof("TransOut")
return dtmcli.MapSuccess, nil
}))
app.POST(qsBusiAPI+"/TransOutCompensate", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
return qsAdjustBalance(1, 30)
app.POST(qsBusiAPI+"/TransOutCompensate", dtmutil.WrapHandler(func(c *gin.Context) (interface{}, error) {
logger.Infof("TransOutCompensate")
return dtmcli.MapSuccess, nil
}))
}

23
test/busi/startup.go

@ -0,0 +1,23 @@
package busi
import (
"fmt"
"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)
}

85
test/busi/utils.go

@ -0,0 +1,85 @@
package busi
import (
"context"
"database/sql"
"fmt"
"strings"
"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-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
}

48
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)
}

19
test/dtmsvr_test.go

@ -10,18 +10,15 @@ import (
"testing"
"time"
"github.com/dtm-labs/dtm/common"
"github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/dtmsvr"
"github.com/dtm-labs/dtm/examples"
"github.com/gin-gonic/gin"
"github.com/dtm-labs/dtm/dtmutil"
"github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
)
var DtmServer = examples.DtmHttpServer
var Busi = examples.Busi
var app *gin.Engine
var DtmServer = dtmutil.DefaultHttpServer
var Busi = busi.Busi
func getTransStatus(gid string) string {
return dtmsvr.GetTransGlobal(gid).Status
@ -42,17 +39,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
}

20
test/examples_test.go

@ -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/dtm-labs/dtm/examples"
)
func TestExamples(t *testing.T) {
examples.QsStartSvr()
for _, s := range examples.Samples {
assertSucceed(t, s.Action())
}
}

53
test/main_test.go

@ -11,12 +11,13 @@ import (
"testing"
"time"
"github.com/dtm-labs/dtm/common"
"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/examples"
"github.com/gin-gonic/gin"
"github.com/dtm-labs/dtm/dtmsvr/config"
"github.com/dtm-labs/dtm/test/busi"
"github.com/go-resty/resty/v2"
)
func exitIf(code int) {
@ -26,39 +27,39 @@ func exitIf(code int) {
}
func TestMain(m *testing.M) {
common.MustLoadConfig()
logger.InitLog(config.LogLevel)
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.OnBeforeRequest(busi.SetHttpHeaderForHeadersYes)
dtmcli.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
}
dtmsvr.PopulateDB(false)
examples.PopulateDB(false)
go dtmsvr.StartSvr()
busi.PopulateDB(false)
_ = busi.Startup()
exitIf(m.Run())
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save