Browse Source

Merge pull request #139 from dtm-labs/alpha

default to boltdb. no config needed
pull/141/head
yedf2 4 years ago
committed by GitHub
parent
commit
342c8155e5
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      .gitignore
  2. 6
      .golangci.yml
  3. 33
      .vscode/launch.json.sample
  4. 3
      .vscode/settings.json.sample
  5. 41
      README-cn.md
  6. 82
      app/main.go
  7. 14
      bench/main.go
  8. 26
      bench/svr/http.go
  9. 128
      common/config.go
  10. 67
      common/types_test.go
  11. 19
      conf.sample.yml
  12. 2
      dtmcli/dtmimp/utils.go
  13. 7
      dtmgrpc/dtmgimp/types.go
  14. 20
      dtmsvr/api_http.go
  15. 83
      dtmsvr/config/config.go
  16. 30
      dtmsvr/config/config_test.go
  17. 34
      dtmsvr/config/config_utils.go
  18. 2
      dtmsvr/cron.go
  19. 13
      dtmsvr/storage/boltdb/boltdb.go
  20. 39
      dtmsvr/storage/redis/redis.go
  21. 6
      dtmsvr/storage/registry/registry.go
  22. 32
      dtmsvr/storage/sql/sql.go
  23. 6
      dtmsvr/storage/trans.go
  24. 18
      dtmsvr/svr.go
  25. 7
      dtmsvr/svr_imports.go
  26. 4
      dtmsvr/trans_process.go
  27. 8
      dtmsvr/trans_status.go
  28. 4
      dtmsvr/utils.go
  29. 4
      dtmsvr/utils_test.go
  30. 14
      dtmutil/consts.go
  31. 16
      dtmutil/db.go
  32. 2
      dtmutil/utils.go
  33. 4
      dtmutil/utils_test.go
  34. 14
      examples/base.go
  35. 330
      examples/busi.pb.go
  36. 55
      examples/data.go
  37. 25
      examples/grpc_msg.go
  38. 36
      examples/grpc_saga.go
  39. 70
      examples/grpc_saga_barrier.go
  40. 32
      examples/grpc_tcc.go
  41. 37
      examples/grpc_xa.go
  42. 29
      examples/http_gorm_xa.go
  43. 28
      examples/http_msg.go
  44. 56
      examples/http_saga.go
  45. 79
      examples/http_saga_barrier.go
  46. 43
      examples/http_saga_gorm_barrier.go
  47. 51
      examples/http_tcc.go
  48. 111
      examples/http_tcc_barrier.go
  49. 42
      examples/http_xa.go
  50. 1
      go.mod
  51. 1
      go.sum
  52. 2
      helper/test-cover.sh
  53. 72
      main.go
  54. 19
      qs/main.go
  55. 0
      sqls/busi.mysql.sql
  56. 0
      sqls/busi.postgres.sql
  57. 14
      test/api_test.go
  58. 12
      test/base_test.go
  59. 124
      test/busi/barrier.go
  60. 45
      test/busi/base_grpc.go
  61. 124
      test/busi/base_http.go
  62. 84
      test/busi/base_types.go
  63. 85
      test/busi/busi.go
  64. 325
      test/busi/busi.pb.go
  65. 4
      test/busi/busi.proto
  66. 74
      test/busi/busi_grpc.pb.go
  67. 38
      test/busi/quick_start.go
  68. 23
      test/busi/startup.go
  69. 61
      test/busi/utils.go
  70. 48
      test/common_test.go
  71. 16
      test/dtmsvr_test.go
  72. 20
      test/examples_test.go
  73. 47
      test/main_test.go
  74. 21
      test/msg_grpc_test.go
  75. 4
      test/msg_options_test.go
  76. 21
      test/msg_test.go
  77. 4
      test/saga_barrier_test.go
  78. 7
      test/saga_compatible_test.go
  79. 6
      test/saga_concurrent_test.go
  80. 11
      test/saga_grpc_barrier_test.go
  81. 23
      test/saga_grpc_test.go
  82. 10
      test/saga_options_test.go
  83. 27
      test/saga_test.go
  84. 14
      test/store_test.go
  85. 93
      test/tcc_barrier_test.go
  86. 4
      test/tcc_cover_test.go
  87. 13
      test/tcc_grpc_cover_test.go
  88. 31
      test/tcc_grpc_test.go
  89. 21
      test/tcc_test.go
  90. 12
      test/types.go
  91. 12
      test/xa_cover_test.go
  92. 22
      test/xa_grpc_test.go
  93. 32
      test/xa_test.go

6
.gitignore

@ -5,11 +5,13 @@ 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.*

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

41
README-cn.md

@ -12,9 +12,9 @@ DTM是一款golang开发的分布式事务管理器,解决了跨数据库、
他优雅的解决了幂等、空补偿、悬挂等分布式事务难题,提供了简单易用、高性能、易水平扩展的解决方案。
作者受邀参加中国数据库大会分享[多语言环境下分布式事务实践](http://dtcc.it168.com/yicheng.html#b9)
通俗一点说,DTM提供跨服务事务能力,一组服务要么全部成功,要么全部回滚,避免只更新了一部分数据产生的业务问题。
## 谁在使用dtm
## 谁在使用DTM(仅列出部分)
[Tencent 腾讯](https://dtm.pub/other/using.html#tencent)
[Ivydad 常青藤爸爸](https://dtm.pub/other/using.html#ivydad)
@ -74,26 +74,23 @@ DTM是一款golang开发的分布式事务管理器,解决了跨数据库、
具体微服务接入使用,参见[微服务支持](https://dtm.pub/protocol/intro.html)
## 快速开始
### 获取代码
如果您不是Go语言,可以跳转[各语言客户端及示例](https://dtm.pub/summary/code.html#go),里面有相关的快速开始示例
`git clone https://github.com/dtm-labs/dtm && cd dtm`
### 运行dtm
### dtm依赖于mysql
安装[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示例
`go run qs/main.go`
### 启动并运行saga示例
`go run app/main.go qs`
这是一个类似跨行转账的示例,包括两个事务分支:资金转出(TransOut)、资金转入(TransIn)。DTM保证TransIn和TransOut要么全部成功,要么全部回滚,保证最终金额的正确性。
## 开始使用
## 接入详解
### 使用
### 接入代码
``` GO
// 具体业务微服务地址
const qsBusi = "http://localhost:8081/api/busi_saga"
@ -117,8 +114,8 @@ DTM是一款golang开发的分布式事务管理器,解决了跨数据库、
<img src="https://pic3.zhimg.com/80/v2-b7d98659093c399e182a0173a8e549ca_1440w.jpg" height=428 />
### 完整示例
参考[examples/quick_start.go](./examples/quick_start.go)
### 更多示例
参考[dtm-labs/dtm-examples](https://github.com/dtm-labs/dtm-examples)
## 公众号
您可以关注公众号:分布式事务,及时跟踪dtm的最新内容
@ -129,11 +126,3 @@ DTM是一款golang开发的分布式事务管理器,解决了跨数据库、
欢迎使用[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 {

26
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"
)
@ -33,7 +34,7 @@ var benchPort = dtmimp.If(os.Getenv("BENCH_PORT") == "", "8083", os.Getenv("BENC
var benchBusi = fmt.Sprintf("http://localhost:%s%s", benchPort, benchAPI)
func sdbGet() *sql.DB {
db, err := dtmimp.PooledDB(common.Config.ExamplesDB)
db, err := dtmimp.PooledDB(busi.BusiConf)
logger.FatalIfError(err)
return db
}
@ -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
}

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
}

19
conf.sample.yml

@ -1,4 +1,4 @@
Store: # specify which engine to store trans status
# Store: # specify which engine to store trans status
# Driver: 'boltdb' # default store engine
# Driver: 'redis'
@ -7,11 +7,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'
@ -41,10 +41,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/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)
}

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 {

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()

83
dtmsvr/config/config.go

@ -0,0 +1,83 @@
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
}
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()
logger.FatalfIf(err != nil, `config error: '%v'.
please visit http://d.dtm.pub to see the config document.`, err)
}

30
common/config_test.go → dtmsvr/config/config_test.go

@ -1,11 +1,11 @@
package common
package config
import (
"errors"
"os"
"testing"
"github.com/go-playground/assert/v2"
"github.com/stretchr/testify/assert"
)
func TestLoadFromEnv(t *testing.T) {
@ -18,12 +18,15 @@ func TestLoadFromEnv(t *testing.T) {
}
func TestCheckConfig(t *testing.T) {
MustLoadConfig("../../conf.sample.yml")
config := &Config
config.RetryInterval = 1
retryIntervalErr := checkConfig()
retryIntervalExpect := errors.New("RetryInterval should not be less than 10")
assert.Equal(t, retryIntervalErr, retryIntervalExpect)
config.RetryInterval = 10
config.TimeoutToFail = 5
timeoutToFailErr := checkConfig()
timeoutToFailExpect := errors.New("TimeoutToFail should not be less than RetryInterval")
assert.Equal(t, timeoutToFailErr, timeoutToFailExpect)
@ -47,3 +50,26 @@ func TestCheckConfig(t *testing.T) {
userExpect := errors.New("Db user not valid ")
assert.Equal(t, userErr, userExpect)
}
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
}

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

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 {

6
dtmsvr/storage/trans.go

@ -3,13 +3,13 @@ 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/dtmutil"
)
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:"-"`
@ -40,7 +40,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

18
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,7 @@ func updateBranchAsync() {
select {
case updateBranch := <-updateBranchAsyncChan:
updates = append(updates, TransBranch{
ModelBase: common.ModelBase{ID: updateBranch.id},
ModelBase: dtmutil.ModelBase{ID: updateBranch.id},
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"
)

4
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
@ -62,7 +62,7 @@ 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.Options = dtmimp.MustMarshalString(t.TransOptions)
if t.Options == "{}" {
t.Options = ""

8
dtmsvr/trans_status.go

@ -47,7 +47,7 @@ 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())
@ -59,7 +59,7 @@ func (t *TransGlobal) changeBranchStatus(b *TransBranch, status string, branchPo
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
@ -136,7 +136,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 +154,6 @@ func (t *TransGlobal) getNextCronInterval(ctype cronType) int64 {
} else if t.RetryInterval != 0 {
return t.RetryInterval
} else {
return config.RetryInterval
return conf.RetryInterval
}
}

4
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"
@ -26,7 +26,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
})
}

1
go.mod

@ -32,6 +32,7 @@ require (
gorm.io/driver/mysql v1.0.3
gorm.io/driver/postgres v1.2.1
gorm.io/gorm v1.22.2
honnef.co/go/tools v0.0.1-2020.1.3
// gotest.tools v2.2.0+incompatible
)

1
go.sum

@ -877,6 +877,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=

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

72
main.go

@ -0,0 +1,72 @@
/*
* 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, Commit, Date string
func version() {
if Version == "" {
Version = "0.0.0-dev"
Commit = "NA"
Date = "NA"
}
if len(Commit) > 8 {
Commit = Commit[:8]
}
fmt.Printf("version: %s commit: %s built at: %s\n", Version, Commit, Date)
}
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 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"
}
maxprocs.Set(maxprocs.Logger(logger.Infof))
registry.WaitStoreUp()
dtmsvr.StartSvr() // 启动dtmsvr的api服务
go dtmsvr.CronExpiredTrans(-1) // 启动dtmsvr的定时过期查询
select {}
}

19
qs/main.go

@ -0,0 +1,19 @@
/*
* 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 (
"time"
"github.com/dtm-labs/dtm/test/busi"
)
func main() {
busi.QsStartSvr()
busi.QsFireRequest()
time.Sleep(1 * time.Second)
}

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

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

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, "")
})
}

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

@ -4,26 +4,23 @@
* license that can be found in the LICENSE file.
*/
package examples
package busi
import (
"context"
"database/sql"
"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 +35,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 +55,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 +101,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 +115,11 @@ 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)
}

124
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,7 @@ func BaseAddRoute(app *gin.Engine) {
}
return "SUCCESS", nil
}))
app.POST(BusiAPI+"/TccBSleepCancel", dtmutil.WrapHandler(func(c *gin.Context) (interface{}, error) {
return sleepCancelHandler(c)
}))
}

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
}

325
test/busi/busi.pb.go

@ -0,0 +1,325 @@
// 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, 0xd3, 0x07, 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, 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
1, // 17: busi.Busi.CanSubmit:output_type -> busi.BusiReply
2, // 18: busi.Busi.TransIn:output_type -> google.protobuf.Empty
2, // 19: busi.Busi.TransOut:output_type -> google.protobuf.Empty
2, // 20: busi.Busi.TransInRevert:output_type -> google.protobuf.Empty
2, // 21: busi.Busi.TransOutRevert:output_type -> google.protobuf.Empty
2, // 22: busi.Busi.TransInConfirm:output_type -> google.protobuf.Empty
2, // 23: busi.Busi.TransOutConfirm:output_type -> google.protobuf.Empty
2, // 24: busi.Busi.XaNotify:output_type -> google.protobuf.Empty
2, // 25: busi.Busi.TransInXa:output_type -> google.protobuf.Empty
2, // 26: busi.Busi.TransOutXa:output_type -> google.protobuf.Empty
2, // 27: busi.Busi.TransInTcc:output_type -> google.protobuf.Empty
2, // 28: busi.Busi.TransOutTcc:output_type -> google.protobuf.Empty
2, // 29: busi.Busi.TransInTccNested:output_type -> google.protobuf.Empty
2, // 30: busi.Busi.TransInBSaga:output_type -> google.protobuf.Empty
2, // 31: busi.Busi.TransOutBSaga:output_type -> google.protobuf.Empty
2, // 32: busi.Busi.TransInRevertBSaga:output_type -> google.protobuf.Empty
2, // 33: busi.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_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
}

4
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 {

74
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"
@ -48,7 +48,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 +57,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 +66,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 +75,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 +84,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 +93,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 +102,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 +111,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 +120,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 +129,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 +138,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 +147,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 +156,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 +165,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 +174,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 +183,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 +192,7 @@ 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
}
@ -301,7 +301,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 +319,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 +337,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 +355,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 +373,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 +391,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 +409,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 +427,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 +445,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 +463,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 +481,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 +499,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 +517,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 +535,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 +553,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 +571,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 +589,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))
@ -601,7 +601,7 @@ func _Busi_TransOutRevertBSaga_Handler(srv interface{}, ctx context.Context, dec
// 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{
{
@ -674,5 +674,5 @@ var Busi_ServiceDesc = grpc.ServiceDesc{
},
},
Streams: []grpc.StreamDesc{},
Metadata: "examples/busi.proto",
Metadata: "test/busi/busi.proto",
}

38
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,20 +24,18 @@ 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"
@ -49,22 +46,21 @@ func QsFireRequest() string {
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)
}

61
test/busi/utils.go

@ -0,0 +1,61 @@
package busi
import (
"context"
"database/sql"
"fmt"
"github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/dtmcli/logger"
"github.com/dtm-labs/dtm/dtmgrpc"
"github.com/dtm-labs/dtm/dtmutil"
"github.com/gin-gonic/gin"
)
func dbGet() *dtmutil.DB {
return dtmutil.DbGet(BusiConf)
}
func sdbGet() *sql.DB {
db, err := dtmimp.PooledDB(BusiConf)
logger.FatalIfError(err)
return db
}
func txGet() *sql.Tx {
db := sdbGet()
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
}

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

16
test/dtmsvr_test.go

@ -10,18 +10,16 @@ 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,10 +40,10 @@ 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})
err := saga.Submit()
@ -54,5 +52,5 @@ func TestUpdateBranchAsync(t *testing.T) {
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())
}
}

47
test/main_test.go

@ -11,12 +11,11 @@ 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/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"
)
func exitIf(code int) {
@ -26,39 +25,35 @@ 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
// 启动组件
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())
}

21
test/msg_grpc_test.go

@ -13,7 +13,8 @@ import (
"github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/dtmgrpc"
"github.com/dtm-labs/dtm/examples"
"github.com/dtm-labs/dtm/dtmutil"
"github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
)
@ -30,10 +31,10 @@ func TestMsgGrpcTimeoutSuccess(t *testing.T) {
msg := genGrpcMsg(dtmimp.GetFuncName())
err := msg.Prepare("")
assert.Nil(t, err)
examples.MainSwitch.CanSubmitResult.SetOnce(dtmcli.ResultOngoing)
busi.MainSwitch.CanSubmitResult.SetOnce(dtmcli.ResultOngoing)
cronTransOnceForwardNow(180)
assert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))
examples.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
busi.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
cronTransOnceForwardNow(180)
assert.Equal(t, StatusSubmitted, getTransStatus(msg.Gid))
assert.Equal(t, []string{StatusPrepared, StatusPrepared}, getBranchesStatus(msg.Gid))
@ -46,20 +47,20 @@ func TestMsgGrpcTimeoutFailed(t *testing.T) {
msg := genGrpcMsg(dtmimp.GetFuncName())
msg.Prepare("")
assert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))
examples.MainSwitch.CanSubmitResult.SetOnce(dtmcli.ResultOngoing)
busi.MainSwitch.CanSubmitResult.SetOnce(dtmcli.ResultOngoing)
cronTransOnceForwardNow(180)
assert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))
examples.MainSwitch.CanSubmitResult.SetOnce(dtmcli.ResultFailure)
busi.MainSwitch.CanSubmitResult.SetOnce(dtmcli.ResultFailure)
cronTransOnceForwardNow(180)
assert.Equal(t, StatusFailed, getTransStatus(msg.Gid))
assert.Equal(t, []string{StatusPrepared, StatusPrepared}, getBranchesStatus(msg.Gid))
}
func genGrpcMsg(gid string) *dtmgrpc.MsgGrpc {
req := &examples.BusiReq{Amount: 30}
msg := dtmgrpc.NewMsgGrpc(examples.DtmGrpcServer, gid).
Add(examples.BusiGrpc+"/examples.Busi/TransOut", req).
Add(examples.BusiGrpc+"/examples.Busi/TransIn", req)
msg.QueryPrepared = fmt.Sprintf("%s/examples.Busi/CanSubmit", examples.BusiGrpc)
req := &busi.BusiReq{Amount: 30}
msg := dtmgrpc.NewMsgGrpc(dtmutil.DefaultGrpcServer, gid).
Add(busi.BusiGrpc+"/busi.Busi/TransOut", req).
Add(busi.BusiGrpc+"/busi.Busi/TransIn", req)
msg.QueryPrepared = fmt.Sprintf("%s/busi.Busi/CanSubmit", busi.BusiGrpc)
return msg
}

4
test/msg_options_test.go

@ -11,7 +11,7 @@ import (
"github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/examples"
"github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
)
@ -44,7 +44,7 @@ func TestMsgOptionsTimeoutFailed(t *testing.T) {
assert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))
cronTransOnceForwardNow(60)
assert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))
examples.MainSwitch.CanSubmitResult.SetOnce(dtmcli.ResultFailure)
busi.MainSwitch.CanSubmitResult.SetOnce(dtmcli.ResultFailure)
cronTransOnceForwardNow(180)
assert.Equal(t, StatusFailed, getTransStatus(msg.Gid))
}

21
test/msg_test.go

@ -11,7 +11,8 @@ import (
"github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/examples"
"github.com/dtm-labs/dtm/dtmutil"
"github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
)
@ -28,10 +29,10 @@ func TestMsgTimeoutSuccess(t *testing.T) {
msg := genMsg(dtmimp.GetFuncName())
msg.Prepare("")
assert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))
examples.MainSwitch.CanSubmitResult.SetOnce(dtmcli.ResultOngoing)
busi.MainSwitch.CanSubmitResult.SetOnce(dtmcli.ResultOngoing)
cronTransOnceForwardNow(180)
assert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))
examples.MainSwitch.TransInResult.SetOnce(dtmcli.ResultOngoing)
busi.MainSwitch.TransInResult.SetOnce(dtmcli.ResultOngoing)
cronTransOnceForwardNow(180)
assert.Equal(t, StatusSubmitted, getTransStatus(msg.Gid))
cronTransOnce()
@ -43,10 +44,10 @@ func TestMsgTimeoutFailed(t *testing.T) {
msg := genMsg(dtmimp.GetFuncName())
msg.Prepare("")
assert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))
examples.MainSwitch.CanSubmitResult.SetOnce(dtmcli.ResultOngoing)
busi.MainSwitch.CanSubmitResult.SetOnce(dtmcli.ResultOngoing)
cronTransOnceForwardNow(180)
assert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))
examples.MainSwitch.CanSubmitResult.SetOnce(dtmcli.ResultFailure)
busi.MainSwitch.CanSubmitResult.SetOnce(dtmcli.ResultFailure)
cronTransOnceForwardNow(180)
assert.Equal(t, []string{StatusPrepared, StatusPrepared}, getBranchesStatus(msg.Gid))
assert.Equal(t, StatusFailed, getTransStatus(msg.Gid))
@ -65,10 +66,10 @@ func TestMsgAbnormal(t *testing.T) {
}
func genMsg(gid string) *dtmcli.Msg {
req := examples.GenTransReq(30, false, false)
msg := dtmcli.NewMsg(examples.DtmHttpServer, gid).
Add(examples.Busi+"/TransOut", &req).
Add(examples.Busi+"/TransIn", &req)
msg.QueryPrepared = examples.Busi + "/CanSubmit"
req := busi.GenTransReq(30, false, false)
msg := dtmcli.NewMsg(dtmutil.DefaultHttpServer, gid).
Add(busi.Busi+"/TransOut", &req).
Add(busi.Busi+"/TransIn", &req)
msg.QueryPrepared = busi.Busi + "/CanSubmit"
return msg
}

4
test/saga_barrier_test.go

@ -11,7 +11,7 @@ import (
"github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/examples"
"github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
)
@ -34,7 +34,7 @@ func TestSagaBarrierRollback(t *testing.T) {
}
func genSagaBarrier(gid string, outFailed, inFailed bool) *dtmcli.Saga {
req := examples.GenTransReq(30, outFailed, inFailed)
req := busi.GenTransReq(30, outFailed, inFailed)
return dtmcli.NewSaga(DtmServer, gid).
Add(Busi+"/SagaBTransOut", Busi+"/SagaBTransOutCompensate", req).
Add(Busi+"/SagaBTransIn", Busi+"/SagaBTransInCompensate", req)

7
test/saga_compatible_test.go

@ -11,15 +11,16 @@ import (
"testing"
"github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/examples"
"github.com/dtm-labs/dtm/dtmutil"
"github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
)
func TestSagaCompatibleNormal(t *testing.T) { // compatible with old http, which put payload in steps.data
gid := dtmimp.GetFuncName()
body := fmt.Sprintf(`{"gid":"%s","trans_type":"saga","steps":[{"action":"%s/TransOut","compensate":"%s/TransOutRevert","data":"{\"amount\":30,\"transInResult\":\"SUCCESS\",\"transOutResult\":\"SUCCESS\"}"},{"action":"%s/TransIn","compensate":"%s/TransInRevert","data":"{\"amount\":30,\"transInResult\":\"SUCCESS\",\"transOutResult\":\"SUCCESS\"}"}]}`,
gid, examples.Busi, examples.Busi, examples.Busi, examples.Busi)
dtmimp.RestyClient.R().SetBody(body).Post(fmt.Sprintf("%s/submit", examples.DtmHttpServer))
gid, busi.Busi, busi.Busi, busi.Busi, busi.Busi)
dtmimp.RestyClient.R().SetBody(body).Post(fmt.Sprintf("%s/submit", dtmutil.DefaultHttpServer))
waitTransProcessed(gid)
assert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(gid))
assert.Equal(t, StatusSucceed, getTransStatus(gid))

6
test/saga_concurrent_test.go

@ -11,7 +11,7 @@ import (
"github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/examples"
"github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
)
@ -29,7 +29,7 @@ func TestSagaConNormal(t *testing.T) {
func TestSagaConRollbackNormal(t *testing.T) {
sagaCon := genSagaCon(dtmimp.GetFuncName(), true, false)
examples.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing)
busi.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing)
err := sagaCon.Submit()
assert.Nil(t, err)
waitTransProcessed(sagaCon.Gid)
@ -52,7 +52,7 @@ func TestSagaConRollbackOrder(t *testing.T) {
func TestSagaConCommittedOngoing(t *testing.T) {
sagaCon := genSagaCon(dtmimp.GetFuncName(), false, false)
examples.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
busi.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
sagaCon.Submit()
waitTransProcessed(sagaCon.Gid)
assert.Equal(t, []string{StatusPrepared, StatusPrepared, StatusPrepared, StatusSucceed}, getBranchesStatus(sagaCon.Gid))

11
test/saga_grpc_barrier_test.go

@ -11,7 +11,8 @@ import (
"github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/dtmgrpc"
"github.com/dtm-labs/dtm/examples"
"github.com/dtm-labs/dtm/dtmutil"
"github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
)
@ -34,9 +35,9 @@ func TestSagaGrpcBarrierRollback(t *testing.T) {
}
func genSagaGrpcBarrier(gid string, outFailed bool, inFailed bool) *dtmgrpc.SagaGrpc {
saga := dtmgrpc.NewSagaGrpc(examples.DtmGrpcServer, gid)
req := examples.GenBusiReq(30, outFailed, inFailed)
saga.Add(examples.BusiGrpc+"/examples.Busi/TransOutBSaga", examples.BusiGrpc+"/examples.Busi/TransOutRevertBSaga", req)
saga.Add(examples.BusiGrpc+"/examples.Busi/TransInBSaga", examples.BusiGrpc+"/examples.Busi/TransInRevertBSaga", req)
saga := dtmgrpc.NewSagaGrpc(dtmutil.DefaultGrpcServer, gid)
req := busi.GenBusiReq(30, outFailed, inFailed)
saga.Add(busi.BusiGrpc+"/busi.Busi/TransOutBSaga", busi.BusiGrpc+"/busi.Busi/TransOutRevertBSaga", req)
saga.Add(busi.BusiGrpc+"/busi.Busi/TransInBSaga", busi.BusiGrpc+"/busi.Busi/TransInRevertBSaga", req)
return saga
}

23
test/saga_grpc_test.go

@ -12,7 +12,8 @@ import (
"github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/dtmgrpc"
"github.com/dtm-labs/dtm/examples"
"github.com/dtm-labs/dtm/dtmutil"
"github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
)
@ -26,7 +27,7 @@ func TestSagaGrpcNormal(t *testing.T) {
func TestSagaGrpcRollback(t *testing.T) {
saga := genSagaGrpc(dtmimp.GetFuncName(), false, true)
examples.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing)
busi.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing)
saga.Submit()
waitTransProcessed(saga.Gid)
assert.Equal(t, StatusAborting, getTransStatus(saga.Gid))
@ -56,7 +57,7 @@ func TestSagaGrpcCurrentOrder(t *testing.T) {
func TestSagaGrpcCommittedOngoing(t *testing.T) {
saga := genSagaGrpc(dtmimp.GetFuncName(), false, false)
examples.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
busi.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
saga.Submit()
waitTransProcessed(saga.Gid)
assert.Equal(t, StatusSubmitted, getTransStatus(saga.Gid))
@ -76,10 +77,10 @@ func TestSagaGrpcNormalWait(t *testing.T) {
}
func TestSagaGrpcEmptyUrl(t *testing.T) {
saga := dtmgrpc.NewSagaGrpc(examples.DtmGrpcServer, dtmimp.GetFuncName())
req := examples.GenBusiReq(30, false, false)
saga.Add(examples.BusiGrpc+"/examples.Busi/TransOut", examples.BusiGrpc+"/examples.Busi/TransOutRevert", req)
saga.Add("", examples.BusiGrpc+"/examples.Busi/TransInRevert", req)
saga := dtmgrpc.NewSagaGrpc(dtmutil.DefaultGrpcServer, dtmimp.GetFuncName())
req := busi.GenBusiReq(30, false, false)
saga.Add(busi.BusiGrpc+"/busi.Busi/TransOut", busi.BusiGrpc+"/busi.Busi/TransOutRevert", req)
saga.Add("", busi.BusiGrpc+"/busi.Busi/TransInRevert", req)
saga.Submit()
waitTransProcessed(saga.Gid)
assert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))
@ -87,9 +88,9 @@ func TestSagaGrpcEmptyUrl(t *testing.T) {
}
func genSagaGrpc(gid string, outFailed bool, inFailed bool) *dtmgrpc.SagaGrpc {
saga := dtmgrpc.NewSagaGrpc(examples.DtmGrpcServer, gid)
req := examples.GenBusiReq(30, outFailed, inFailed)
saga.Add(examples.BusiGrpc+"/examples.Busi/TransOut", examples.BusiGrpc+"/examples.Busi/TransOutRevert", req)
saga.Add(examples.BusiGrpc+"/examples.Busi/TransIn", examples.BusiGrpc+"/examples.Busi/TransInRevert", req)
saga := dtmgrpc.NewSagaGrpc(dtmutil.DefaultGrpcServer, gid)
req := busi.GenBusiReq(30, outFailed, inFailed)
saga.Add(busi.BusiGrpc+"/busi.Busi/TransOut", busi.BusiGrpc+"/busi.Busi/TransOutRevert", req)
saga.Add(busi.BusiGrpc+"/busi.Busi/TransIn", busi.BusiGrpc+"/busi.Busi/TransInRevert", req)
return saga
}

10
test/saga_options_test.go

@ -11,14 +11,14 @@ import (
"github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/examples"
"github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
)
func TestSagaOptionsRetryOngoing(t *testing.T) {
saga := genSaga1(dtmimp.GetFuncName(), false, false)
saga.RetryInterval = 150 // CronForwardDuration is larger than RetryInterval
examples.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
busi.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
err := saga.Submit()
assert.Nil(t, err)
waitTransProcessed(saga.Gid)
@ -30,7 +30,7 @@ func TestSagaOptionsRetryOngoing(t *testing.T) {
func TestSagaOptionsRetryError(t *testing.T) {
saga := genSaga1(dtmimp.GetFuncName(), false, false)
saga.RetryInterval = 150 // CronForwardDuration is less than 2*RetryInterval
examples.MainSwitch.TransOutResult.SetOnce("ERROR")
busi.MainSwitch.TransOutResult.SetOnce("ERROR")
err := saga.Submit()
assert.Nil(t, err)
waitTransProcessed(saga.Gid)
@ -45,7 +45,7 @@ func TestSagaOptionsRetryError(t *testing.T) {
func TestSagaOptionsTimeout(t *testing.T) {
saga := genSaga(dtmimp.GetFuncName(), false, false)
saga.TimeoutToFail = 1800
examples.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
busi.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
saga.Submit()
waitTransProcessed(saga.Gid)
assert.Equal(t, StatusSubmitted, getTransStatus(saga.Gid))
@ -65,7 +65,7 @@ func TestSagaOptionsNormalWait(t *testing.T) {
func TestSagaOptionsCommittedOngoingWait(t *testing.T) {
saga := genSaga(dtmimp.GetFuncName(), false, false)
examples.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
busi.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
saga.SetOptions(&dtmcli.TransOptions{WaitResult: true})
err := saga.Submit()
assert.Error(t, err)

27
test/saga_test.go

@ -11,7 +11,8 @@ import (
"github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/examples"
"github.com/dtm-labs/dtm/dtmutil"
"github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
)
@ -25,7 +26,7 @@ func TestSagaNormal(t *testing.T) {
func TestSagaOngoingSucceed(t *testing.T) {
saga := genSaga(dtmimp.GetFuncName(), false, false)
examples.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
busi.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)
saga.Submit()
waitTransProcessed(saga.Gid)
assert.Equal(t, []string{StatusPrepared, StatusPrepared, StatusPrepared, StatusPrepared}, getBranchesStatus(saga.Gid))
@ -37,7 +38,7 @@ func TestSagaOngoingSucceed(t *testing.T) {
func TestSagaFailed(t *testing.T) {
saga := genSaga(dtmimp.GetFuncName(), false, true)
examples.MainSwitch.TransOutRevertResult.SetOnce("ERROR")
busi.MainSwitch.TransOutRevertResult.SetOnce("ERROR")
err := saga.Submit()
assert.Nil(t, err)
waitTransProcessed(saga.Gid)
@ -59,9 +60,9 @@ func TestSagaAbnormal(t *testing.T) {
}
func TestSagaEmptyUrl(t *testing.T) {
saga := dtmcli.NewSaga(examples.DtmHttpServer, dtmimp.GetFuncName())
req := examples.GenTransReq(30, false, false)
saga.Add(examples.Busi+"/TransOut", "", &req)
saga := dtmcli.NewSaga(dtmutil.DefaultHttpServer, dtmimp.GetFuncName())
req := busi.GenTransReq(30, false, false)
saga.Add(busi.Busi+"/TransOut", "", &req)
saga.Add("", "", &req)
saga.Submit()
waitTransProcessed(saga.Gid)
@ -70,16 +71,16 @@ func TestSagaEmptyUrl(t *testing.T) {
}
func genSaga(gid string, outFailed bool, inFailed bool) *dtmcli.Saga {
saga := dtmcli.NewSaga(examples.DtmHttpServer, gid)
req := examples.GenTransReq(30, outFailed, inFailed)
saga.Add(examples.Busi+"/TransOut", examples.Busi+"/TransOutRevert", &req)
saga.Add(examples.Busi+"/TransIn", examples.Busi+"/TransInRevert", &req)
saga := dtmcli.NewSaga(dtmutil.DefaultHttpServer, gid)
req := busi.GenTransReq(30, outFailed, inFailed)
saga.Add(busi.Busi+"/TransOut", busi.Busi+"/TransOutRevert", &req)
saga.Add(busi.Busi+"/TransIn", busi.Busi+"/TransInRevert", &req)
return saga
}
func genSaga1(gid string, outFailed bool, inFailed bool) *dtmcli.Saga {
saga := dtmcli.NewSaga(examples.DtmHttpServer, gid)
req := examples.GenTransReq(30, outFailed, inFailed)
saga.Add(examples.Busi+"/TransOut", examples.Busi+"/TransOutRevert", &req)
saga := dtmcli.NewSaga(dtmutil.DefaultHttpServer, gid)
req := busi.GenTransReq(30, outFailed, inFailed)
saga.Add(busi.Busi+"/TransOut", busi.Busi+"/TransOutRevert", &req)
return saga
}

14
test/store_test.go

@ -70,21 +70,21 @@ func TestStoreLockTrans(t *testing.T) {
gid := dtmimp.GetFuncName()
g, s := initTransGlobal(gid)
g2 := s.LockOneGlobalTrans(2 * time.Duration(config.RetryInterval) * time.Second)
g2 := s.LockOneGlobalTrans(2 * time.Duration(conf.RetryInterval) * time.Second)
assert.NotNil(t, g2)
assert.Equal(t, gid, g2.Gid)
s.TouchCronTime(g, 3*config.RetryInterval)
g2 = s.LockOneGlobalTrans(2 * time.Duration(config.RetryInterval) * time.Second)
s.TouchCronTime(g, 3*conf.RetryInterval)
g2 = s.LockOneGlobalTrans(2 * time.Duration(conf.RetryInterval) * time.Second)
assert.Nil(t, g2)
s.TouchCronTime(g, 1*config.RetryInterval)
g2 = s.LockOneGlobalTrans(2 * time.Duration(config.RetryInterval) * time.Second)
s.TouchCronTime(g, 1*conf.RetryInterval)
g2 = s.LockOneGlobalTrans(2 * time.Duration(conf.RetryInterval) * time.Second)
assert.NotNil(t, g2)
assert.Equal(t, gid, g2.Gid)
s.ChangeGlobalStatus(g, "succeed", []string{}, true)
g2 = s.LockOneGlobalTrans(2 * time.Duration(config.RetryInterval) * time.Second)
g2 = s.LockOneGlobalTrans(2 * time.Duration(conf.RetryInterval) * time.Second)
assert.Nil(t, g2)
}
@ -93,7 +93,7 @@ func TestStoreWait(t *testing.T) {
}
func TestUpdateBranchSql(t *testing.T) {
if !config.Store.IsDB() {
if !conf.Store.IsDB() {
_, err := registry.GetStore().UpdateBranches(nil, nil)
assert.Nil(t, err)
}

93
test/tcc_barrier_test.go

@ -12,19 +12,18 @@ import (
"fmt"
"strings"
"testing"
"time"
"github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/dtmcli/logger"
"github.com/dtm-labs/dtm/examples"
"github.com/dtm-labs/dtm/test/busi"
"github.com/gin-gonic/gin"
"github.com/go-resty/resty/v2"
"github.com/stretchr/testify/assert"
)
func TestTccBarrierNormal(t *testing.T) {
req := examples.GenTransReq(30, false, false)
req := busi.GenTransReq(30, false, false)
gid := dtmimp.GetFuncName()
err := dtmcli.TccGlobalTransaction(DtmServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
_, err := tcc.CallBranch(req, Busi+"/TccBTransOutTry", Busi+"/TccBTransOutConfirm", Busi+"/TccBTransOutCancel")
@ -38,7 +37,7 @@ func TestTccBarrierNormal(t *testing.T) {
}
func TestTccBarrierRollback(t *testing.T) {
req := examples.GenTransReq(30, false, true)
req := busi.GenTransReq(30, false, true)
gid := dtmimp.GetFuncName()
err := dtmcli.TccGlobalTransaction(DtmServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
_, err := tcc.CallBranch(req, Busi+"/TccBTransOutTry", Busi+"/TccBTransOutConfirm", Busi+"/TccBTransOutCancel")
@ -51,30 +50,29 @@ func TestTccBarrierRollback(t *testing.T) {
assert.Equal(t, []string{StatusSucceed, StatusPrepared, StatusSucceed, StatusPrepared}, getBranchesStatus(gid))
}
var disorderHandler func(c *gin.Context) (interface{}, error) = nil
func TestTccBarrierDisorder(t *testing.T) {
timeoutChan := make(chan string, 2)
finishedChan := make(chan string, 2)
ua1 := busi.GetUserAccountByUid(1)
ua2 := busi.GetUserAccountByUid(2)
cancelFinishedChan := make(chan string, 2)
cancelCanReturnChan := make(chan string, 2)
gid := dtmimp.GetFuncName()
cronFinished := make(chan string, 2)
err := dtmcli.TccGlobalTransaction(DtmServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
body := &examples.TransReq{Amount: 30}
body := &busi.TransReq{Amount: 30}
tryURL := Busi + "/TccBTransOutTry"
confirmURL := Busi + "/TccBTransOutConfirm"
cancelURL := Busi + "/TccBSleepCancel"
// 请参见子事务屏障里的时序图,这里为了模拟该时序图,手动拆解了callbranch
branchID := tcc.NewSubBranchID()
sleeped := false
disorderHandler = func(c *gin.Context) (interface{}, error) {
res, err := examples.TccBarrierTransOutCancel(c)
if !sleeped {
sleeped = true
logger.Debugf("sleep before cancel return")
<-timeoutChan
finishedChan <- "1"
}
busi.SetSleepCancelHandler(func(c *gin.Context) (interface{}, error) {
res, err := busi.TccBarrierTransOutCancel(c)
logger.Debugf("disorderHandler before cancel finish write")
cancelFinishedChan <- "1"
logger.Debugf("disorderHandler before cancel return read")
<-cancelCanReturnChan
logger.Debugf("disorderHandler after cancel return read")
return res, err
}
})
// 注册子事务
resp, err := dtmimp.RestyClient.R().
SetBody(map[string]interface{}{
@ -89,37 +87,44 @@ func TestTccBarrierDisorder(t *testing.T) {
assert.Nil(t, err)
assert.Contains(t, resp.String(), dtmcli.ResultSuccess)
go func() {
logger.Debugf("sleeping to wait for tcc try timeout")
<-timeoutChan
r, _ := dtmimp.RestyClient.R().
SetBody(body).
SetQueryParams(map[string]string{
"dtm": tcc.Dtm,
"gid": tcc.Gid,
"branch_id": branchID,
"trans_type": "tcc",
"op": dtmcli.BranchTry,
}).
Post(tryURL)
assert.True(t, strings.Contains(r.String(), dtmcli.ResultSuccess)) // 这个是悬挂操作,为了简单起见,依旧让他返回成功
finishedChan <- "1"
}()
logger.Debugf("cron to timeout and then call cancel")
go cronTransOnceForwardNow(300)
time.Sleep(100 * time.Millisecond)
logger.Debugf("cron to timeout and then call cancelled twice")
cronTransOnceForwardNow(300)
timeoutChan <- "wake"
timeoutChan <- "wake"
<-finishedChan
<-finishedChan
time.Sleep(100 * time.Millisecond)
cron := func() {
cronTransOnceForwardNow(300)
logger.Debugf("cronFinished write")
cronFinished <- "1"
logger.Debugf("cronFinished after write")
}
go cron()
<-cancelFinishedChan
go cron()
<-cancelFinishedChan
cancelCanReturnChan <- "1"
cancelCanReturnChan <- "1"
logger.Debugf("after cancelCanRetrun 2 write")
// after cancel then run try
r, _ := dtmimp.RestyClient.R().
SetBody(body).
SetQueryParams(map[string]string{
"dtm": tcc.Dtm,
"gid": tcc.Gid,
"branch_id": branchID,
"trans_type": "tcc",
"op": dtmcli.BranchTry,
}).
Post(tryURL)
assert.True(t, strings.Contains(r.String(), dtmcli.ResultSuccess)) // 这个是悬挂操作,为了简单起见,依旧让他返回成功
logger.Debugf("cronFinished read")
<-cronFinished
<-cronFinished
logger.Debugf("cronFinished after read")
return nil, fmt.Errorf("a cancelled tcc")
})
assert.Error(t, err, fmt.Errorf("a cancelled tcc"))
assert.Equal(t, []string{StatusSucceed, StatusPrepared}, getBranchesStatus(gid))
assert.Equal(t, StatusFailed, getTransStatus(gid))
assert.True(t, busi.IsEqual(ua1, busi.GetUserAccountByUid(1)))
assert.True(t, busi.IsEqual(ua2, busi.GetUserAccountByUid(2)))
}
func TestTccBarrierPanic(t *testing.T) {

4
test/tcc_cover_test.go

@ -5,7 +5,7 @@ import (
"github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/examples"
"github.com/dtm-labs/dtm/dtmutil"
"github.com/go-resty/resty/v2"
"github.com/stretchr/testify/assert"
)
@ -21,7 +21,7 @@ func TestTccCoverNotConnected(t *testing.T) {
func TestTccCoverPanic(t *testing.T) {
gid := dtmimp.GetFuncName()
err := dtmimp.CatchP(func() {
_ = dtmcli.TccGlobalTransaction(examples.DtmHttpServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
_ = dtmcli.TccGlobalTransaction(dtmutil.DefaultHttpServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
panic("user panic")
})
assert.FailNow(t, "not executed")

13
test/tcc_grpc_cover_test.go

@ -5,7 +5,8 @@ import (
"github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/dtmgrpc"
"github.com/dtm-labs/dtm/examples"
"github.com/dtm-labs/dtm/dtmutil"
"github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
"google.golang.org/protobuf/types/known/emptypb"
)
@ -21,7 +22,7 @@ func TestTccGrpcCoverNotConnected(t *testing.T) {
func TestTccGrpcCoverPanic(t *testing.T) {
gid := dtmimp.GetFuncName()
err := dtmimp.CatchP(func() {
_ = dtmgrpc.TccGlobalTransaction(examples.DtmGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {
_ = dtmgrpc.TccGlobalTransaction(dtmutil.DefaultGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {
panic("user panic")
})
assert.FailNow(t, "not executed")
@ -30,16 +31,16 @@ func TestTccGrpcCoverPanic(t *testing.T) {
}
func TestTccGrpcCoverCallBranch(t *testing.T) {
req := examples.GenBusiReq(30, false, false)
req := busi.GenBusiReq(30, false, false)
gid := dtmimp.GetFuncName()
err := dtmgrpc.TccGlobalTransaction(examples.DtmGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {
err := dtmgrpc.TccGlobalTransaction(dtmutil.DefaultGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {
r := &emptypb.Empty{}
err := tcc.CallBranch(req, "not_exists://abc", examples.BusiGrpc+"/examples.Busi/TransOutConfirm", examples.BusiGrpc+"/examples.Busi/TransOutRevert", r)
err := tcc.CallBranch(req, "not_exists://abc", busi.BusiGrpc+"/busi.Busi/TransOutConfirm", busi.BusiGrpc+"/busi.Busi/TransOutRevert", r)
assert.Error(t, err)
tcc.Dtm = "localhost:01"
err = tcc.CallBranch(req, examples.BusiGrpc+"/examples.Busi/TransOut", examples.BusiGrpc+"/examples.Busi/TransOutConfirm", examples.BusiGrpc+"/examples.Busi/TransOutRevert", r)
err = tcc.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransOut", busi.BusiGrpc+"/busi.Busi/TransOutConfirm", busi.BusiGrpc+"/busi.Busi/TransOutRevert", r)
assert.Error(t, err)
return err

31
test/tcc_grpc_test.go

@ -14,19 +14,20 @@ import (
"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/examples"
"github.com/dtm-labs/dtm/dtmutil"
"github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
"google.golang.org/protobuf/types/known/emptypb"
)
func TestTccGrpcNormal(t *testing.T) {
req := examples.GenBusiReq(30, false, false)
req := busi.GenBusiReq(30, false, false)
gid := dtmimp.GetFuncName()
err := dtmgrpc.TccGlobalTransaction(examples.DtmGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {
err := dtmgrpc.TccGlobalTransaction(dtmutil.DefaultGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {
r := &emptypb.Empty{}
err := tcc.CallBranch(req, examples.BusiGrpc+"/examples.Busi/TransOut", examples.BusiGrpc+"/examples.Busi/TransOutConfirm", examples.BusiGrpc+"/examples.Busi/TransOutRevert", r)
err := tcc.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransOut", busi.BusiGrpc+"/busi.Busi/TransOutConfirm", busi.BusiGrpc+"/busi.Busi/TransOutRevert", r)
assert.Nil(t, err)
return tcc.CallBranch(req, examples.BusiGrpc+"/examples.Busi/TransIn", examples.BusiGrpc+"/examples.Busi/TransInConfirm", examples.BusiGrpc+"/examples.Busi/TransInRevert", r)
return tcc.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransIn", busi.BusiGrpc+"/busi.Busi/TransInConfirm", busi.BusiGrpc+"/busi.Busi/TransInRevert", r)
})
assert.Nil(t, err)
waitTransProcessed(gid)
@ -37,13 +38,13 @@ func TestTccGrpcNormal(t *testing.T) {
func TestTccGrpcRollback(t *testing.T) {
gid := dtmimp.GetFuncName()
req := examples.GenBusiReq(30, false, true)
err := dtmgrpc.TccGlobalTransaction(examples.DtmGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {
req := busi.GenBusiReq(30, false, true)
err := dtmgrpc.TccGlobalTransaction(dtmutil.DefaultGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {
r := &emptypb.Empty{}
err := tcc.CallBranch(req, examples.BusiGrpc+"/examples.Busi/TransOutTcc", examples.BusiGrpc+"/examples.Busi/TransOutConfirm", examples.BusiGrpc+"/examples.Busi/TransOutRevert", r)
err := tcc.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransOutTcc", busi.BusiGrpc+"/busi.Busi/TransOutConfirm", busi.BusiGrpc+"/busi.Busi/TransOutRevert", r)
assert.Nil(t, err)
examples.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing)
return tcc.CallBranch(req, examples.BusiGrpc+"/examples.Busi/TransInTcc", examples.BusiGrpc+"/examples.Busi/TransInConfirm", examples.BusiGrpc+"/examples.Busi/TransInRevert", r)
busi.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing)
return tcc.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransInTcc", busi.BusiGrpc+"/busi.Busi/TransInConfirm", busi.BusiGrpc+"/busi.Busi/TransInRevert", r)
})
assert.Error(t, err)
waitTransProcessed(gid)
@ -54,13 +55,13 @@ func TestTccGrpcRollback(t *testing.T) {
}
func TestTccGrpcNested(t *testing.T) {
req := examples.GenBusiReq(30, false, false)
req := busi.GenBusiReq(30, false, false)
gid := dtmimp.GetFuncName()
err := dtmgrpc.TccGlobalTransaction(examples.DtmGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {
err := dtmgrpc.TccGlobalTransaction(dtmutil.DefaultGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {
r := &emptypb.Empty{}
err := tcc.CallBranch(req, examples.BusiGrpc+"/examples.Busi/TransOutTcc", examples.BusiGrpc+"/examples.Busi/TransOutConfirm", examples.BusiGrpc+"/examples.Busi/TransOutRevert", r)
err := tcc.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransOutTcc", busi.BusiGrpc+"/busi.Busi/TransOutConfirm", busi.BusiGrpc+"/busi.Busi/TransOutRevert", r)
assert.Nil(t, err)
return tcc.CallBranch(req, examples.BusiGrpc+"/examples.Busi/TransInTccNested", examples.BusiGrpc+"/examples.Busi/TransInConfirm", examples.BusiGrpc+"/examples.Busi/TransInRevert", r)
return tcc.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransInTccNested", busi.BusiGrpc+"/busi.Busi/TransInConfirm", busi.BusiGrpc+"/busi.Busi/TransInRevert", r)
})
assert.Nil(t, err)
waitTransProcessed(gid)
@ -71,7 +72,7 @@ func TestTccGrpcNested(t *testing.T) {
func TestTccGrpcType(t *testing.T) {
_, err := dtmgrpc.TccFromGrpc(context.Background())
assert.Error(t, err)
logger.Debugf("expecting dtmgrpcserver error")
logger.Debugf("expecting dtmutil.DefaultGrpcServer error")
err = dtmgrpc.TccGlobalTransaction("-", "", func(tcc *dtmgrpc.TccGrpc) error { return nil })
assert.Error(t, err)
}

21
test/tcc_test.go

@ -11,15 +11,16 @@ import (
"github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/examples"
"github.com/dtm-labs/dtm/dtmutil"
"github.com/dtm-labs/dtm/test/busi"
"github.com/go-resty/resty/v2"
"github.com/stretchr/testify/assert"
)
func TestTccNormal(t *testing.T) {
req := examples.GenTransReq(30, false, false)
req := busi.GenTransReq(30, false, false)
gid := dtmimp.GetFuncName()
err := dtmcli.TccGlobalTransaction(examples.DtmHttpServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
err := dtmcli.TccGlobalTransaction(dtmutil.DefaultHttpServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
_, err := tcc.CallBranch(req, Busi+"/TransOut", Busi+"/TransOutConfirm", Busi+"/TransOutRevert")
assert.Nil(t, err)
return tcc.CallBranch(req, Busi+"/TransIn", Busi+"/TransInConfirm", Busi+"/TransInRevert")
@ -32,11 +33,11 @@ func TestTccNormal(t *testing.T) {
func TestTccRollback(t *testing.T) {
gid := dtmimp.GetFuncName()
req := examples.GenTransReq(30, false, true)
err := dtmcli.TccGlobalTransaction(examples.DtmHttpServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
req := busi.GenTransReq(30, false, true)
err := dtmcli.TccGlobalTransaction(dtmutil.DefaultHttpServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
_, rerr := tcc.CallBranch(req, Busi+"/TransOut", Busi+"/TransOutConfirm", Busi+"/TransOutRevert")
assert.Nil(t, rerr)
examples.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing)
busi.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing)
return tcc.CallBranch(req, Busi+"/TransIn", Busi+"/TransInConfirm", Busi+"/TransInRevert")
})
assert.Error(t, err)
@ -48,11 +49,11 @@ func TestTccRollback(t *testing.T) {
}
func TestTccTimeout(t *testing.T) {
req := examples.GenTransReq(30, false, false)
req := busi.GenTransReq(30, false, false)
gid := dtmimp.GetFuncName()
timeoutChan := make(chan int, 1)
err := dtmcli.TccGlobalTransaction(examples.DtmHttpServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
err := dtmcli.TccGlobalTransaction(dtmutil.DefaultHttpServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
_, err := tcc.CallBranch(req, Busi+"/TransOut", Busi+"/TransOutConfirm", Busi+"/TransOutRevert")
assert.Nil(t, err)
go func() {
@ -70,9 +71,9 @@ func TestTccTimeout(t *testing.T) {
}
func TestTccCompatible(t *testing.T) {
req := examples.GenTransReq(30, false, false)
req := busi.GenTransReq(30, false, false)
gid := dtmimp.GetFuncName()
err := dtmcli.TccGlobalTransaction(examples.DtmHttpServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
err := dtmcli.TccGlobalTransaction(dtmutil.DefaultHttpServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
_, err := tcc.CallBranch(req, Busi+"/TransOut", Busi+"/TransOutConfirm", Busi+"/TransOutRevert")
assert.Nil(t, err)
return tcc.CallBranch(req, Busi+"/TransIn", Busi+"/TransInConfirm", Busi+"/TransInRevert")

12
test/types.go

@ -9,17 +9,19 @@ package test
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/dtmsvr"
"github.com/dtm-labs/dtm/dtmsvr/config"
"github.com/dtm-labs/dtm/dtmutil"
"github.com/dtm-labs/dtm/test/busi"
)
var config = &common.Config
var conf = &config.Config
func dbGet() *common.DB {
return common.DbGet(config.ExamplesDB)
func dbGet() *dtmutil.DB {
return dtmutil.DbGet(busi.BusiConf)
}
// waitTransProcessed only for test usage. wait for transaction processed once
@ -28,7 +30,7 @@ func waitTransProcessed(gid string) {
select {
case id := <-dtmsvr.TransProcessedTestChan:
for id != gid {
logger.Errorf("-------id %s not match gid %s", id, gid)
logger.Warnf("------- expecting: %s but %s found", gid, id)
id = <-dtmsvr.TransProcessedTestChan
}
logger.Debugf("finish for gid %s", gid)

12
test/xa_cover_test.go

@ -5,7 +5,7 @@ import (
"github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/examples"
"github.com/dtm-labs/dtm/test/busi"
"github.com/go-resty/resty/v2"
"github.com/stretchr/testify/assert"
)
@ -14,11 +14,11 @@ func TestXaCoverDBError(t *testing.T) {
oldDriver := getXc().Conf.Driver
gid := dtmimp.GetFuncName()
err := getXc().XaGlobalTransaction(gid, func(xa *dtmcli.Xa) (*resty.Response, error) {
req := examples.GenTransReq(30, false, false)
_, err := xa.CallBranch(req, examples.Busi+"/TransOutXa")
req := busi.GenTransReq(30, false, false)
_, err := xa.CallBranch(req, busi.Busi+"/TransOutXa")
assert.Nil(t, err)
getXc().Conf.Driver = "no-driver"
_, err = xa.CallBranch(req, examples.Busi+"/TransInXa")
_, err = xa.CallBranch(req, busi.Busi+"/TransInXa")
assert.Error(t, err)
getXc().Conf.Driver = oldDriver // make abort succeed
return nil, err
@ -45,8 +45,8 @@ func TestXaCoverDTMError(t *testing.T) {
func TestXaCoverGidError(t *testing.T) {
gid := "errgid-' '"
err := getXc().XaGlobalTransaction(gid, func(xa *dtmcli.Xa) (*resty.Response, error) {
req := examples.GenTransReq(30, false, false)
_, err := xa.CallBranch(req, examples.Busi+"/TransOutXa")
req := busi.GenTransReq(30, false, false)
_, err := xa.CallBranch(req, busi.Busi+"/TransOutXa")
assert.Error(t, err)
return nil, err
})

22
test/xa_grpc_test.go

@ -13,24 +13,24 @@ import (
"github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/dtmgrpc"
"github.com/dtm-labs/dtm/examples"
"github.com/dtm-labs/dtm/test/busi"
"github.com/stretchr/testify/assert"
"google.golang.org/protobuf/types/known/emptypb"
)
func getXcg() *dtmgrpc.XaGrpcClient {
return examples.XaGrpcClient
return busi.XaGrpcClient
}
func TestXaGrpcNormal(t *testing.T) {
gid := dtmimp.GetFuncName()
err := getXcg().XaGlobalTransaction(gid, func(xa *dtmgrpc.XaGrpc) error {
req := examples.GenBusiReq(30, false, false)
req := busi.GenBusiReq(30, false, false)
r := &emptypb.Empty{}
err := xa.CallBranch(req, examples.BusiGrpc+"/examples.Busi/TransOutXa", r)
err := xa.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransOutXa", r)
if err != nil {
return err
}
return xa.CallBranch(req, examples.BusiGrpc+"/examples.Busi/TransInXa", r)
return xa.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransInXa", r)
})
assert.Equal(t, nil, err)
waitTransProcessed(gid)
@ -41,13 +41,13 @@ func TestXaGrpcNormal(t *testing.T) {
func TestXaGrpcRollback(t *testing.T) {
gid := dtmimp.GetFuncName()
err := getXcg().XaGlobalTransaction(gid, func(xa *dtmgrpc.XaGrpc) error {
req := examples.GenBusiReq(30, false, true)
req := busi.GenBusiReq(30, false, true)
r := &emptypb.Empty{}
err := xa.CallBranch(req, examples.BusiGrpc+"/examples.Busi/TransOutXa", r)
err := xa.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransOutXa", r)
if err != nil {
return err
}
return xa.CallBranch(req, examples.BusiGrpc+"/examples.Busi/TransInXa", r)
return xa.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransInXa", r)
})
assert.Error(t, err)
waitTransProcessed(gid)
@ -59,17 +59,17 @@ func TestXaGrpcType(t *testing.T) {
_, err := dtmgrpc.XaGrpcFromRequest(context.Background())
assert.Error(t, err)
err = examples.XaGrpcClient.XaLocalTransaction(context.Background(), nil, nil)
err = busi.XaGrpcClient.XaLocalTransaction(context.Background(), nil, nil)
assert.Error(t, err)
err = dtmimp.CatchP(func() {
examples.XaGrpcClient.XaGlobalTransaction("id1", func(xa *dtmgrpc.XaGrpc) error { panic(fmt.Errorf("hello")) })
busi.XaGrpcClient.XaGlobalTransaction("id1", func(xa *dtmgrpc.XaGrpc) error { panic(fmt.Errorf("hello")) })
})
assert.Error(t, err)
}
func TestXaGrpcLocalError(t *testing.T) {
xc := examples.XaGrpcClient
xc := busi.XaGrpcClient
err := xc.XaGlobalTransaction(dtmimp.GetFuncName(), func(xa *dtmgrpc.XaGrpc) error {
return fmt.Errorf("an error")
})

32
test/xa_test.go

@ -12,24 +12,24 @@ import (
"github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/examples"
"github.com/dtm-labs/dtm/test/busi"
"github.com/go-resty/resty/v2"
"github.com/stretchr/testify/assert"
)
func getXc() *dtmcli.XaClient {
return examples.XaClient
return busi.XaClient
}
func TestXaNormal(t *testing.T) {
gid := dtmimp.GetFuncName()
err := getXc().XaGlobalTransaction(gid, func(xa *dtmcli.Xa) (*resty.Response, error) {
req := examples.GenTransReq(30, false, false)
resp, err := xa.CallBranch(req, examples.Busi+"/TransOutXa")
req := busi.GenTransReq(30, false, false)
resp, err := xa.CallBranch(req, busi.Busi+"/TransOutXa")
if err != nil {
return resp, err
}
return xa.CallBranch(req, examples.Busi+"/TransInXa")
return xa.CallBranch(req, busi.Busi+"/TransInXa")
})
assert.Equal(t, nil, err)
waitTransProcessed(gid)
@ -40,10 +40,10 @@ func TestXaNormal(t *testing.T) {
func TestXaDuplicate(t *testing.T) {
gid := dtmimp.GetFuncName()
err := getXc().XaGlobalTransaction(gid, func(xa *dtmcli.Xa) (*resty.Response, error) {
req := examples.GenTransReq(30, false, false)
_, err := xa.CallBranch(req, examples.Busi+"/TransOutXa")
req := busi.GenTransReq(30, false, false)
_, err := xa.CallBranch(req, busi.Busi+"/TransOutXa")
assert.Nil(t, err)
sdb, err := dtmimp.StandaloneDB(config.ExamplesDB)
sdb, err := dtmimp.StandaloneDB(busi.BusiConf)
assert.Nil(t, err)
if dtmcli.GetCurrentDBType() == dtmcli.DBTypeMysql {
_, err = dtmimp.DBExec(sdb, "xa recover")
@ -51,7 +51,7 @@ func TestXaDuplicate(t *testing.T) {
}
_, err = dtmimp.DBExec(sdb, dtmimp.GetDBSpecial().GetXaSQL("commit", gid+"-01")) // 先把某一个事务提交,模拟重复请求
assert.Nil(t, err)
return xa.CallBranch(req, examples.Busi+"/TransInXa")
return xa.CallBranch(req, busi.Busi+"/TransInXa")
})
assert.Nil(t, err)
waitTransProcessed(gid)
@ -62,12 +62,12 @@ func TestXaDuplicate(t *testing.T) {
func TestXaRollback(t *testing.T) {
gid := dtmimp.GetFuncName()
err := getXc().XaGlobalTransaction(gid, func(xa *dtmcli.Xa) (*resty.Response, error) {
req := examples.GenTransReq(30, false, true)
resp, err := xa.CallBranch(req, examples.Busi+"/TransOutXa")
req := busi.GenTransReq(30, false, true)
resp, err := xa.CallBranch(req, busi.Busi+"/TransOutXa")
if err != nil {
return resp, err
}
return xa.CallBranch(req, examples.Busi+"/TransInXa")
return xa.CallBranch(req, busi.Busi+"/TransInXa")
})
assert.Error(t, err)
waitTransProcessed(gid)
@ -92,7 +92,7 @@ func TestXaTimeout(t *testing.T) {
cronTransOnceForwardNow(300)
timeoutChan <- 0
}()
_ = <-timeoutChan
<-timeoutChan
return nil, nil
})
assert.Error(t, err)
@ -109,10 +109,10 @@ func TestXaNotTimeout(t *testing.T) {
timeoutChan <- 0
}()
_ = <-timeoutChan
req := examples.GenTransReq(30, false, false)
_, err := xa.CallBranch(req, examples.Busi+"/TransOutXa")
req := busi.GenTransReq(30, false, false)
_, err := xa.CallBranch(req, busi.Busi+"/TransOutXa")
assert.Nil(t, err)
examples.MainSwitch.NextResult.SetOnce(dtmcli.ResultOngoing) // make commit temp error
busi.MainSwitch.NextResult.SetOnce(dtmcli.ResultOngoing) // make commit temp error
return nil, nil
})
assert.Nil(t, err)

Loading…
Cancel
Save