From e35bf901af9946d4752c3c76371feb2cb9904c84 Mon Sep 17 00:00:00 2001 From: yedf2 <120050102@qq.com> Date: Fri, 8 Oct 2021 22:48:52 +0800 Subject: [PATCH 01/12] use DBExec --- bench/http.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bench/http.go b/bench/http.go index ee33f1a..7a93b22 100644 --- a/bench/http.go +++ b/bench/http.go @@ -50,7 +50,7 @@ func reloadData() { for i := 1; i <= total; i++ { ss = append(ss, fmt.Sprintf("(%d, 1000000)", i)) } - _, err := db.Exec(s + strings.Join(ss, ",")) + _, err := dtmcli.DBExec(db, s+strings.Join(ss, ",")) dtmcli.FatalIfError(err) dtmcli.Logf("%d users inserted. used: %dms", total, time.Since(began).Milliseconds()) } From e39bb3c42907939d2200cfd4ccc87a5d01af53d2 Mon Sep 17 00:00:00 2001 From: yedf2 <120050102@qq.com> Date: Fri, 8 Oct 2021 23:43:48 +0800 Subject: [PATCH 02/12] in barrier dangle op also return success --- dtmcli/barrier.go | 14 ++------------ test/barrier_tcc_test.go | 2 +- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/dtmcli/barrier.go b/dtmcli/barrier.go index f04caa6..4749256 100644 --- a/dtmcli/barrier.go +++ b/dtmcli/barrier.go @@ -1,7 +1,6 @@ package dtmcli import ( - "database/sql" "fmt" "net/url" ) @@ -76,17 +75,8 @@ func (bb *BranchBarrier) Call(tx Tx, busiCall BusiFunc) (rerr error) { originAffected, _ := insertBarrier(tx, ti.TransType, ti.Gid, ti.BranchID, originType, bid, ti.BranchType) currentAffected, rerr := insertBarrier(tx, ti.TransType, ti.Gid, ti.BranchID, ti.BranchType, bid, ti.BranchType) Logf("originAffected: %d currentAffected: %d", originAffected, currentAffected) - if (ti.BranchType == BranchCancel || ti.BranchType == BranchCompensate) && originAffected > 0 { // 这个是空补偿,返回成功 - return - } else if currentAffected == 0 { // 插入不成功 - var result sql.NullString - err := DBQueryRow(tx, "select 1 from dtm_barrier.barrier where trans_type=? and gid=? and branch_id=? and branch_type=? and barrier_id=? and reason=?", - ti.TransType, ti.Gid, ti.BranchID, ti.BranchType, bid, ti.BranchType).Scan(&result) - if err == sql.ErrNoRows { // 不是当前分支插入的,那么是cancel插入的,因此是悬挂操作,返回失败,AP收到这个返回,会尽快回滚 - rerr = ErrFailure - return - } - rerr = err //幂等和空补偿,直接返回 + if (ti.BranchType == BranchCancel || ti.BranchType == BranchCompensate) && originAffected > 0 || // 这个是空补偿 + currentAffected == 0 { // 这个是重复请求或者悬挂 return } rerr = busiCall(tx) diff --git a/test/barrier_tcc_test.go b/test/barrier_tcc_test.go index d936495..b864373 100644 --- a/test/barrier_tcc_test.go +++ b/test/barrier_tcc_test.go @@ -96,7 +96,7 @@ func tccBarrierDisorder(t *testing.T) { "branch_type": dtmcli.BranchTry, }). Post(tryURL) - assert.True(t, strings.Contains(r.String(), dtmcli.ResultFailure)) + assert.True(t, strings.Contains(r.String(), dtmcli.ResultSuccess)) // 这个是悬挂操作,为了简单起见,依旧让他返回成功 finishedChan <- "1" }() dtmcli.Logf("cron to timeout and then call cancel") From b2dda033398ed34cedeea7e74b7b3cc4de6cb8c2 Mon Sep 17 00:00:00 2001 From: yedf2 <120050102@qq.com> Date: Fri, 8 Oct 2021 23:44:10 +0800 Subject: [PATCH 03/12] add driver and compatible --- app/main.go | 2 ++ dtmcli/consts.go | 8 ++++++++ dtmcli/utils.go | 22 ++++++++++++++++++++++ dtmcli/utils_test.go | 9 +++++++++ test/main_test.go | 2 ++ 5 files changed, 43 insertions(+) diff --git a/app/main.go b/app/main.go index a4e04bd..c63dcd8 100644 --- a/app/main.go +++ b/app/main.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/yedf/dtm/bench" + "github.com/yedf/dtm/common" "github.com/yedf/dtm/dtmcli" "github.com/yedf/dtm/dtmsvr" "github.com/yedf/dtm/examples" @@ -26,6 +27,7 @@ Available commands: ` func main() { + dtmcli.DBDriver = common.DtmConfig.DB["driver"] if len(os.Args) == 1 { fmt.Println(usage) for name := range examples.Samples { diff --git a/dtmcli/consts.go b/dtmcli/consts.go index bbb3455..78aead8 100644 --- a/dtmcli/consts.go +++ b/dtmcli/consts.go @@ -29,4 +29,12 @@ const ( ResultSuccess = "SUCCESS" // ResultFailure for result of a trans/trans branch ResultFailure = "FAILURE" + + // DriverMysql const for driver mysql + DriverMysql = "mysql" + // DriverPostgres const for driver postgres + DriverPostgres = "postgres" ) + +// DBDriver dtm和dtmcli可以支持mysql和postgres,但不支持混合,通过全局变量指定当前要支持的驱动 +var DBDriver = DriverMysql diff --git a/dtmcli/utils.go b/dtmcli/utils.go index f0df2cf..f29d82e 100644 --- a/dtmcli/utils.go +++ b/dtmcli/utils.go @@ -210,6 +210,7 @@ func StandaloneDB(conf map[string]string) (*sql.DB, error) { // DBExec use raw db to exec func DBExec(db DB, sql string, values ...interface{}) (affected int64, rerr error) { + sql = makeSqlCompatible(sql) r, rerr := db.Exec(sql, values...) if rerr == nil { affected, rerr = r.RowsAffected() @@ -222,6 +223,7 @@ func DBExec(db DB, sql string, values ...interface{}) (affected int64, rerr erro // DBQueryRow use raw tx to query row func DBQueryRow(db DB, query string, args ...interface{}) *sql.Row { + query = makeSqlCompatible(query) Logf("querying: "+query, args...) return db.QueryRow(query, args...) } @@ -268,3 +270,23 @@ func CheckResult(res interface{}, err error) error { } return err } + +func makeSqlCompatible(sql string) string { + if DBDriver == DriverMysql { + return sql + } else if DBDriver == DriverPostgres { + pos := 1 + parts := []string{} + b := 0 + for i := 0; i < len(sql); i++ { + if sql[i] == '?' { + parts = append(parts, sql[b:i]) + b = i + 1 + parts = append(parts, fmt.Sprintf("$%d", pos)) + pos++ + } + } + return strings.Join(parts, "") + } + panic(fmt.Sprintf("unknown driver %s", DBDriver)) +} diff --git a/dtmcli/utils_test.go b/dtmcli/utils_test.go index a59d628..b7c9554 100644 --- a/dtmcli/utils_test.go +++ b/dtmcli/utils_test.go @@ -89,3 +89,12 @@ func TestFatal(t *testing.T) { }) assert.Error(t, err, fmt.Errorf("fatal")) } + +func TestMakeSqlCompatible(t *testing.T) { + old := DBDriver + DBDriver = DriverMysql + assert.Equal(t, "? ?", makeSqlCompatible("? ?")) + DBDriver = DriverPostgres + assert.Equal(t, "$1 $2", makeSqlCompatible("? ?")) + DBDriver = old +} diff --git a/test/main_test.go b/test/main_test.go index cf3bf77..0eebb53 100644 --- a/test/main_test.go +++ b/test/main_test.go @@ -6,11 +6,13 @@ import ( "time" "github.com/yedf/dtm/common" + "github.com/yedf/dtm/dtmcli" "github.com/yedf/dtm/dtmsvr" "github.com/yedf/dtm/examples" ) func TestMain(m *testing.M) { + dtmcli.DBDriver = common.DtmConfig.DB["driver"] dtmsvr.TransProcessedTestChan = make(chan string, 1) dtmsvr.CronForwardDuration = 60 * time.Second common.DtmConfig.UpdateBranchSync = 1 From 9518a93120c8a8ffb964476d8809158224a487ba Mon Sep 17 00:00:00 2001 From: yedf2 <120050102@qq.com> Date: Sat, 9 Oct 2021 19:57:34 +0800 Subject: [PATCH 04/12] postgres supported --- common/types.go | 11 +-- common/types_test.go | 2 + dtmcli/barrier.go | 6 +- dtmcli/barrier.postgres.sql | 20 ++++++ dtmcli/utils.go | 33 +++++++-- dtmcli/utils_test.go | 8 ++- dtmcli/xa_base.go | 12 ++-- dtmsvr/cron.go | 11 ++- dtmsvr/dtmsvr.go | 3 +- dtmsvr/dtmsvr.postgres.sql | 72 ++++++++++++++++++++ examples/base_http.go | 11 ++- examples/examples.postgres.sql | 62 +++++++++++++++++ examples/http_tcc_barrier.go | 2 +- go.mod | 4 +- go.sum | 120 +++++++++++++++++++++++++++++++++ helper/compose.postgres.yml | 10 +++ test/dtmsvr_test.go | 2 +- test/grpc_xa_test.go | 3 - test/xa_test.go | 3 - 19 files changed, 359 insertions(+), 36 deletions(-) create mode 100644 dtmcli/barrier.postgres.sql create mode 100644 dtmsvr/dtmsvr.postgres.sql create mode 100644 examples/examples.postgres.sql create mode 100644 helper/compose.postgres.yml diff --git a/common/types.go b/common/types.go index de04394..90573d4 100644 --- a/common/types.go +++ b/common/types.go @@ -10,9 +10,11 @@ import ( "sync" "time" - _ "github.com/go-sql-driver/mysql" + _ "github.com/go-sql-driver/mysql" // register mysql driver + _ "github.com/lib/pq" // register postgres driver "gopkg.in/yaml.v2" "gorm.io/driver/mysql" + "gorm.io/driver/postgres" "gorm.io/gorm" "github.com/yedf/dtm/dtmcli" @@ -26,10 +28,11 @@ type ModelBase struct { } func getGormDialetor(driver string, dsn string) gorm.Dialector { - if driver == "mysql" { - return mysql.Open(dsn) + if driver == dtmcli.DriverPostgres { + return postgres.Open(dsn) } - panic(fmt.Errorf("unkown driver: %s", driver)) + dtmcli.PanicIf(driver != dtmcli.DriverMysql, fmt.Errorf("unkown driver: %s", driver)) + return mysql.Open(dsn) } var dbs sync.Map diff --git a/common/types_test.go b/common/types_test.go index 8ab7303..20ccc38 100644 --- a/common/types_test.go +++ b/common/types_test.go @@ -24,6 +24,8 @@ func TestDbAlone(t *testing.T) { assert.Nil(t, err) _, err = dtmcli.DBExec(db, "select 1") assert.Equal(t, nil, err) + _, err = dtmcli.DBExec(db, "") + assert.Equal(t, nil, err) db.Close() _, err = dtmcli.DBExec(db, "select 1") assert.NotEqual(t, nil, err) diff --git a/dtmcli/barrier.go b/dtmcli/barrier.go index 4749256..64d76ec 100644 --- a/dtmcli/barrier.go +++ b/dtmcli/barrier.go @@ -44,7 +44,11 @@ func insertBarrier(tx Tx, transType string, gid string, branchID string, branchT if branchType == "" { return 0, nil } - return DBExec(tx, "insert ignore into dtm_barrier.barrier(trans_type, gid, branch_id, branch_type, barrier_id, reason) values(?,?,?,?,?,?)", transType, gid, branchID, branchType, barrierID, reason) + sql := map[string]string{ + "mysql": "insert ignore into dtm_barrier.barrier(trans_type, gid, branch_id, branch_type, barrier_id, reason) values(?,?,?,?,?,?)", + "postgres": "insert into dtm_barrier.barrier(trans_type, gid, branch_id, branch_type, barrier_id, reason) values(?,?,?,?,?,?) on conflict ON CONSTRAINT uniq_barrier do nothing", + }[DBDriver] + return DBExec(tx, sql, transType, gid, branchID, branchType, barrierID, reason) } // Call 子事务屏障,详细介绍见 https://zhuanlan.zhihu.com/p/388444465 diff --git a/dtmcli/barrier.postgres.sql b/dtmcli/barrier.postgres.sql new file mode 100644 index 0000000..dc3d069 --- /dev/null +++ b/dtmcli/barrier.postgres.sql @@ -0,0 +1,20 @@ +create schema if not exists dtm_barrier; + +drop table if exists dtm_barrier.barrier; + +CREATE SEQUENCE if not EXISTS dtm_barrier.barrier_seq; + +create table if not exists dtm_barrier.barrier( + id int NOT NULL DEFAULT NEXTVAL ('dtm_barrier.barrier_seq'), + trans_type varchar(45) default '' , + gid varchar(128) default'', + branch_id varchar(128) default '', + branch_type varchar(45) default '', + barrier_id varchar(45) default '', + reason varchar(45) default '', + create_time timestamp(0) DEFAULT NULL, + update_time timestamp(0) DEFAULT NULL, + PRIMARY KEY(id), + CONSTRAINT uniq_barrier unique(gid, branch_id, branch_type, barrier_id) +); + diff --git a/dtmcli/utils.go b/dtmcli/utils.go index f29d82e..aa5761f 100644 --- a/dtmcli/utils.go +++ b/dtmcli/utils.go @@ -8,6 +8,7 @@ import ( "os" "path" "runtime" + "runtime/debug" "strconv" "strings" "sync" @@ -135,6 +136,7 @@ var FatalExitFunc = func() { os.Exit(1) } // LogFatalf 采用红色打印错误类信息, 并退出 func LogFatalf(fmt string, args ...interface{}) { + fmt += "\n" + string(debug.Stack()) Logf("\x1b[31m\n"+fmt+"\x1b[0m\n", args...) FatalExitFunc() } @@ -210,7 +212,10 @@ func StandaloneDB(conf map[string]string) (*sql.DB, error) { // DBExec use raw db to exec func DBExec(db DB, sql string, values ...interface{}) (affected int64, rerr error) { - sql = makeSqlCompatible(sql) + if sql == "" { + return 0, nil + } + sql = makeSQLCompatible(sql) r, rerr := db.Exec(sql, values...) if rerr == nil { affected, rerr = r.RowsAffected() @@ -223,7 +228,7 @@ func DBExec(db DB, sql string, values ...interface{}) (affected int64, rerr erro // DBQueryRow use raw tx to query row func DBQueryRow(db DB, query string, args ...interface{}) *sql.Row { - query = makeSqlCompatible(query) + query = makeSQLCompatible(query) Logf("querying: "+query, args...) return db.QueryRow(query, args...) } @@ -271,10 +276,8 @@ func CheckResult(res interface{}, err error) error { return err } -func makeSqlCompatible(sql string) string { - if DBDriver == DriverMysql { - return sql - } else if DBDriver == DriverPostgres { +func makeSQLCompatible(sql string) string { + if DBDriver == DriverPostgres { pos := 1 parts := []string{} b := 0 @@ -286,7 +289,23 @@ func makeSqlCompatible(sql string) string { pos++ } } + parts = append(parts, sql[b:]) return strings.Join(parts, "") } - panic(fmt.Sprintf("unknown driver %s", DBDriver)) + PanicIf(DBDriver != DriverMysql, fmt.Errorf("unkown db driver: %s", DBDriver)) + return sql +} + +func getXaSQL(action string, xid string) string { + if DBDriver == DriverPostgres { + return map[string]string{ + "end": "", + "start": "begin", + "prepare": fmt.Sprintf("prepare transaction '%s'", xid), + "commit": fmt.Sprintf("commit prepared '%s'", xid), + "rollback": fmt.Sprintf("rollback prepared '%s'", xid), + }[action] + } + PanicIf(DBDriver != DriverMysql, fmt.Errorf("unkown db driver: %s", DBDriver)) + return fmt.Sprintf("xa %s '%s'", action, xid) } diff --git a/dtmcli/utils_test.go b/dtmcli/utils_test.go index b7c9554..364283e 100644 --- a/dtmcli/utils_test.go +++ b/dtmcli/utils_test.go @@ -90,11 +90,13 @@ func TestFatal(t *testing.T) { assert.Error(t, err, fmt.Errorf("fatal")) } -func TestMakeSqlCompatible(t *testing.T) { +func TestCompatible(t *testing.T) { old := DBDriver DBDriver = DriverMysql - assert.Equal(t, "? ?", makeSqlCompatible("? ?")) + assert.Equal(t, "? ?", makeSQLCompatible("? ?")) + assert.Equal(t, "xa start 'xa1'", getXaSQL("start", "xa1")) DBDriver = DriverPostgres - assert.Equal(t, "$1 $2", makeSqlCompatible("? ?")) + assert.Equal(t, "$1 $2", makeSQLCompatible("? ?")) + assert.Equal(t, "begin", getXaSQL("start", "xa1")) DBDriver = old } diff --git a/dtmcli/xa_base.go b/dtmcli/xa_base.go index 1b1367d..e1e31f8 100644 --- a/dtmcli/xa_base.go +++ b/dtmcli/xa_base.go @@ -2,7 +2,6 @@ package dtmcli import ( "database/sql" - "fmt" "strings" ) @@ -21,8 +20,9 @@ func (xc *XaClientBase) HandleCallback(gid string, branchID string, action strin } defer db.Close() xaID := gid + "-" + branchID - _, err = DBExec(db, fmt.Sprintf("xa %s '%s'", action, xaID)) - if err != nil && strings.Contains(err.Error(), "Error 1397: XAER_NOTA") { // 重复commit/rollback同一个id,报这个错误,忽略 + _, err = DBExec(db, getXaSQL(action, xaID)) + if err != nil && + (strings.Contains(err.Error(), "Error 1397: XAER_NOTA") || strings.Contains(err.Error(), "does not exist")) { // 重复commit/rollback同一个id,报这个错误,忽略 err = nil } return err @@ -39,9 +39,9 @@ func (xc *XaClientBase) HandleLocalTrans(xa *TransBase, cb func(*sql.DB) (interf defer func() { db.Close() }() defer func() { x := recover() - _, err := DBExec(db, fmt.Sprintf("XA end '%s'", xaBranch)) + _, err := DBExec(db, getXaSQL("end", xaBranch)) if x == nil && rerr == nil && err == nil { - _, err = DBExec(db, fmt.Sprintf("XA prepare '%s'", xaBranch)) + _, err = DBExec(db, getXaSQL("prepare", xaBranch)) } if rerr == nil { rerr = err @@ -50,7 +50,7 @@ func (xc *XaClientBase) HandleLocalTrans(xa *TransBase, cb func(*sql.DB) (interf panic(x) } }() - _, rerr = DBExec(db, fmt.Sprintf("XA start '%s'", xaBranch)) + _, rerr = DBExec(db, getXaSQL("start", xaBranch)) if rerr != nil { return } diff --git a/dtmsvr/cron.go b/dtmsvr/cron.go index 739f04d..f301754 100644 --- a/dtmsvr/cron.go +++ b/dtmsvr/cron.go @@ -41,11 +41,18 @@ func lockOneTrans(expireIn time.Duration) *TransGlobal { trans := TransGlobal{} owner := GenGid() db := dbGet() + getTime := func(second int) string { + return fmt.Sprintf(map[string]string{ + "mysql": "date_add(now(), interval %d second)", + "postgres": "current_timestamp + interval '%d second'", + }[dtmcli.DBDriver], second) + } + expire := int(expireIn / time.Second) + whereTime := fmt.Sprintf("next_cron_time < %s and next_cron_time > %s and update_time < %s", getTime(expire), getTime(-3600), getTime(expire-3)) // 这里next_cron_time需要限定范围,否则数据量累计之后,会导致查询变慢 // 限定update_time < now - 3,否则会出现刚被这个应用取出,又被另一个取出 dbr := db.Must().Model(&trans). - Where("next_cron_time < date_add(now(), interval ? second) and next_cron_time > date_add(now(), interval -3600 second) and update_time < date_add(now(), interval ? second) and status in ('prepared', 'aborting', 'submitted')", int(expireIn/time.Second), -3+int(expireIn/time.Second)). - Limit(1).Update("owner", owner) + Where(whereTime+"and status in ('prepared', 'aborting', 'submitted')").Limit(1).Update("owner", owner) if dbr.RowsAffected == 0 { return nil } diff --git a/dtmsvr/dtmsvr.go b/dtmsvr/dtmsvr.go index f05fe6e..216bb3a 100644 --- a/dtmsvr/dtmsvr.go +++ b/dtmsvr/dtmsvr.go @@ -77,7 +77,8 @@ func updateBranchAsync() { } for len(updates) > 0 { dbr := dbGet().Clauses(clause.OnConflict{ - DoUpdates: clause.AssignmentColumns([]string{"status", "finish_time"}), + OnConstraint: "trans_branch_pkey", + DoUpdates: clause.AssignmentColumns([]string{"status", "finish_time"}), }).Create(updates) dtmcli.Logf("flushed %d branch status to db. affected: %d", len(updates), dbr.RowsAffected) if dbr.Error != nil { diff --git a/dtmsvr/dtmsvr.postgres.sql b/dtmsvr/dtmsvr.postgres.sql new file mode 100644 index 0000000..ccb4164 --- /dev/null +++ b/dtmsvr/dtmsvr.postgres.sql @@ -0,0 +1,72 @@ +CREATE SCHEMA if not EXISTS dtm /* SQLINES DEMO *** RACTER SET utf8mb4 */; + +drop table IF EXISTS dtm.trans_global; +-- SQLINES LICENSE FOR EVALUATION USE ONLY +CREATE SEQUENCE if not EXISTS dtm.trans_global_seq; + +CREATE TABLE if not EXISTS dtm.trans_global ( + id int NOT NULL DEFAULT NEXTVAL ('dtm.trans_global_seq'), + gid varchar(128) NOT NULL , + trans_type varchar(45) not null , + status varchar(45) NOT NULL , + query_prepared varchar(128) NOT NULL , + protocol varchar(45) not null, + create_time timestamp(0) DEFAULT NULL, + update_time timestamp(0) DEFAULT NULL, + commit_time timestamp(0) DEFAULT NULL, + finish_time timestamp(0) DEFAULT NULL, + rollback_time timestamp(0) DEFAULT NULL, + next_cron_interval int default null , + next_cron_time timestamp(0) default null , + owner varchar(128) not null default '' , + PRIMARY KEY (id), + CONSTRAINT gid UNIQUE (gid) +) ; + +create index if not EXISTS owner on dtm.trans_global(owner); +CREATE INDEX if not EXISTS create_time ON dtm.trans_global (create_time); +CREATE INDEX if not EXISTS update_time ON dtm.trans_global (update_time); +create index if not EXISTS next_cron_time on dtm.trans_global (next_cron_time); + +drop table IF EXISTS dtm.trans_branch; +-- SQLINES LICENSE FOR EVALUATION USE ONLY +CREATE SEQUENCE if not EXISTS dtm.trans_branch_seq; + +CREATE TABLE IF NOT EXISTS dtm.trans_branch ( + id int NOT NULL DEFAULT NEXTVAL ('dtm.trans_branch_seq'), + gid varchar(128) NOT NULL , + url varchar(128) NOT NULL , + data TEXT , + branch_id VARCHAR(128) NOT NULL , + branch_type varchar(45) NOT NULL , + status varchar(45) NOT NULL , + finish_time timestamp(0) DEFAULT NULL, + rollback_time timestamp(0) DEFAULT NULL, + create_time timestamp(0) DEFAULT NULL, + update_time timestamp(0) DEFAULT NULL, + PRIMARY KEY (id), + CONSTRAINT gid_uniq UNIQUE (gid,branch_id, branch_type) +) ; + +CREATE INDEX if not EXISTS create_time ON dtm.trans_branch (create_time); +CREATE INDEX if not EXISTS update_time ON dtm.trans_branch (update_time); + +drop table IF EXISTS dtm.trans_log; +-- SQLINES LICENSE FOR EVALUATION USE ONLY +CREATE SEQUENCE if not EXISTS dtm.trans_log_seq; + +CREATE TABLE IF NOT EXISTS dtm.trans_log ( + id int NOT NULL DEFAULT NEXTVAL ('dtm.trans_log_seq'), + gid varchar(128) NOT NULL , + branch_id varchar(128) DEFAULT NULL , + action varchar(45) DEFAULT NULL , + old_status varchar(45) NOT NULL DEFAULT '' , + new_status varchar(45) NOT NULL , + detail TEXT NOT NULL , + create_time timestamp(0) DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (id) +) ; + +CREATE INDEX if not EXISTS gid ON dtm.trans_log (gid); +CREATE INDEX if not EXISTS create_time ON dtm.trans_log (create_time); + diff --git a/examples/base_http.go b/examples/base_http.go index 3e50017..68e14cc 100644 --- a/examples/base_http.go +++ b/examples/base_http.go @@ -9,6 +9,7 @@ import ( "github.com/yedf/dtm/common" "github.com/yedf/dtm/dtmcli" "gorm.io/driver/mysql" + "gorm.io/driver/postgres" "gorm.io/gorm" ) @@ -129,9 +130,13 @@ func BaseAddRoute(app *gin.Engine) { if reqFrom(c).TransOutResult == dtmcli.ResultFailure { return dtmcli.MapFailure, nil } - gdb, err := gorm.Open(mysql.New(mysql.Config{ - Conn: db, - }), &gorm.Config{}) + var dia gorm.Dialector = nil + if dtmcli.DBDriver == dtmcli.DriverMysql { + dia = mysql.New(mysql.Config{Conn: db}) + } else if dtmcli.DBDriver == dtmcli.DriverPostgres { + dia = postgres.New(postgres.Config{Conn: db}) + } + gdb, err := gorm.Open(dia, &gorm.Config{}) if err != nil { return nil, err } diff --git a/examples/examples.postgres.sql b/examples/examples.postgres.sql new file mode 100644 index 0000000..d657aed --- /dev/null +++ b/examples/examples.postgres.sql @@ -0,0 +1,62 @@ +CREATE SCHEMA if not exists dtm_busi /* SQLINES DEMO *** RACTER SET utf8mb4 */; +create SCHEMA if not exists dtm_barrier /* SQLINES DEMO *** RACTER SET utf8mb4 */; + +drop table if exists dtm_busi.user_account; +-- SQLINES LICENSE FOR EVALUATION USE ONLY +create sequence if not exists dtm_busi.user_account_seq; + +create table if not exists dtm_busi.user_account( + id int PRIMARY KEY DEFAULT NEXTVAL ('dtm_busi.user_account_seq'), + user_id int UNIQUE , + balance DECIMAL(10, 2) not null default '0', + create_time timestamp(0) DEFAULT now(), + update_time timestamp(0) DEFAULT now() +); +-- SQLINES LICENSE FOR EVALUATION USE ONLY +create index if not exists create_idx on dtm_busi.user_account(create_time); +-- SQLINES LICENSE FOR EVALUATION USE ONLY +create index if not exists update_idx on dtm_busi.user_account(update_time); + +TRUNCATE dtm_busi.user_account; +insert into dtm_busi.user_account (user_id, balance) values (1, 10000), (2, 10000); + +drop table if exists dtm_busi.user_account_trading; +-- SQLINES LICENSE FOR EVALUATION USE ONLY +create sequence if not exists dtm_busi.user_account_trading_seq; + +create table if not exists dtm_busi.user_account_trading( -- SQLINES DEMO *** �冻结的金额 + id int PRIMARY KEY DEFAULT NEXTVAL ('dtm_busi.user_account_trading_seq'), + user_id int UNIQUE , + trading_balance DECIMAL(10, 2) not null default '0', + create_time timestamp(0) DEFAULT now(), + update_time timestamp(0) DEFAULT now() +); +-- SQLINES LICENSE FOR EVALUATION USE ONLY +create index if not exists create_idx on dtm_busi.user_account_trading(create_time); +-- SQLINES LICENSE FOR EVALUATION USE ONLY +create index if not exists update_idx on dtm_busi.user_account_trading(update_time); + +TRUNCATE dtm_busi.user_account_trading; +insert into dtm_busi.user_account_trading (user_id, trading_balance) values (1, 0), (2, 0); + + +drop table if exists dtm_barrier.barrier; +-- SQLINES LICENSE FOR EVALUATION USE ONLY +create sequence if not exists dtm_barrier.barrier_seq; + +create table if not exists dtm_barrier.barrier( + id int PRIMARY KEY DEFAULT NEXTVAL ('dtm_barrier.barrier_seq'), + trans_type varchar(45) default '' , + gid varchar(128) default'', + branch_id varchar(128) default '', + branch_type varchar(45) default '', + reason varchar(45) default '' , + result varchar(2047) default null , + create_time timestamp(0) DEFAULT now(), + update_time timestamp(0) DEFAULT now(), + UNIQUE (gid, branch_id, branch_type) +); +-- SQLINES LICENSE FOR EVALUATION USE ONLY +create index if not exists create_idx on dtm_barrier.barrier(create_time); +-- SQLINES LICENSE FOR EVALUATION USE ONLY +create index if not exists update_idx on dtm_barrier.barrier(update_time); diff --git a/examples/http_tcc_barrier.go b/examples/http_tcc_barrier.go index 818a04e..7e1c39c 100644 --- a/examples/http_tcc_barrier.go +++ b/examples/http_tcc_barrier.go @@ -37,7 +37,7 @@ const transInUID = 1 const transOutUID = 2 func adjustTrading(db dtmcli.DB, uid int, amount int) error { - affected, err := dtmcli.DBExec(db, "update dtm_busi.user_account_trading set trading_balance=trading_balance + ? where user_id=? and trading_balance + ? + (select balance from dtm_busi.user_account where id=?) >= 0", amount, uid, amount, uid) + affected, err := dtmcli.DBExec(db, "update dtm_busi.user_account_trading set trading_balance=trading_balance + ? where user_id=? and trading_balance + ? + (select balance from dtm_busi.user_account where user_id=?) >= 0", amount, uid, amount, uid) if err == nil && affected == 0 { return fmt.Errorf("update error, maybe balance not enough") } diff --git a/go.mod b/go.mod index 2a0cd34..c7370fa 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/go-sql-driver/mysql v1.5.0 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/kr/pretty v0.1.0 // indirect + github.com/lib/pq v1.10.3 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect github.com/prometheus/client_golang v1.11.0 @@ -21,6 +22,7 @@ require ( google.golang.org/protobuf v1.27.1 gopkg.in/yaml.v2 v2.3.0 gorm.io/driver/mysql v1.0.3 - gorm.io/gorm v1.21.12 + gorm.io/driver/postgres v1.1.2 + gorm.io/gorm v1.21.15 // gotest.tools v2.2.0+incompatible ) diff --git a/go.sum b/go.sum index cf5bf72..061d383 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -20,6 +21,10 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -53,6 +58,7 @@ github.com/go-resty/resty/v2 v2.6.0/go.mod h1:PwvJS6hvaPkjtjNg9ph+VrSD92bi5Zq73w github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -81,10 +87,57 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= +github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= +github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.10.0 h1:4EYhlDVEMsJ30nNj0mmgwIUXoq7e9sMJrVC2ED6QlCU= +github.com/jackc/pgconn v1.10.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1eI= +github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= +github.com/jackc/pgtype v1.8.1 h1:9k0IXtdJXHJbyAWQgbWr1lU+MEhPXZz6RIXxfR5oxXs= +github.com/jackc/pgtype v1.8.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.13.0 h1:JCjhT5vmhMAf/YwBHLvrBn4OGdIQBiFG6ym8Zmdx570= +github.com/jackc/pgx/v4 v4.13.0/go.mod h1:9P4X524sErlaxj0XSGZk7s+LD0eOyu1ZDUrrpznYDF0= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= @@ -102,15 +155,27 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg= +github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= @@ -149,11 +214,20 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -167,19 +241,38 @@ github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLY github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -193,10 +286,12 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -215,10 +310,15 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -229,25 +329,38 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -291,6 +404,8 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -303,8 +418,13 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/mysql v1.0.3 h1:+JKBYPfn1tygR1/of/Fh2T8iwuVwzt+PEJmKaXzMQXg= gorm.io/driver/mysql v1.0.3/go.mod h1:twGxftLBlFgNVNakL7F+P/x9oYqoymG3YYT8cAfI9oI= +gorm.io/driver/postgres v1.1.2 h1:Amy3hCvLqM+/ICzjCnQr8wKFLVJTeOTdlMT7kCP+J1Q= +gorm.io/driver/postgres v1.1.2/go.mod h1:/AGV0zvqF3mt9ZtzLzQmXWQ/5vr+1V1TyHZGZVjzmwI= gorm.io/gorm v1.20.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= gorm.io/gorm v1.21.12 h1:3fQM0Eiz7jcJEhPggHEpoYnsGZqynMzverL77DV40RM= gorm.io/gorm v1.21.12/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= +gorm.io/gorm v1.21.15 h1:gAyaDoPw0lCyrSFWhBlahbUA1U4P5RViC1uIqoB+1Rk= +gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/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= diff --git a/helper/compose.postgres.yml b/helper/compose.postgres.yml new file mode 100644 index 0000000..11b409e --- /dev/null +++ b/helper/compose.postgres.yml @@ -0,0 +1,10 @@ +version: '3.3' +services: + postgres: + image: 'postgres:13' + command: postgres --max_prepared_transactions=1000 + environment: + POSTGRES_PASSWORD: mysecretpassword + + ports: + - '5432:5432' diff --git a/test/dtmsvr_test.go b/test/dtmsvr_test.go index d2fa6cd..f5d5524 100644 --- a/test/dtmsvr_test.go +++ b/test/dtmsvr_test.go @@ -92,7 +92,7 @@ func TestSqlDB(t *testing.T) { BranchID: "branch_id2", BranchType: dtmcli.BranchAction, } - db.Must().Exec("insert ignore into dtm_barrier.barrier(trans_type, gid, branch_id, branch_type, reason) values('saga', 'gid1', 'branch_id1', 'action', 'saga')") + db.Must().Exec("insert into dtm_barrier.barrier(trans_type, gid, branch_id, branch_type, reason) values('saga', 'gid1', 'branch_id1', 'action', 'saga')") tx, err := db.ToSQLDB().Begin() asserts.Nil(err) err = barrier.Call(tx, func(db dtmcli.DB) error { diff --git a/test/grpc_xa_test.go b/test/grpc_xa_test.go index 42f5295..f0c4bd2 100644 --- a/test/grpc_xa_test.go +++ b/test/grpc_xa_test.go @@ -11,9 +11,6 @@ import ( ) func TestGrpcXa(t *testing.T) { - if config.DB["driver"] != "mysql" { - return - } xaGrpcType(t) xaGrpcLocalError(t) xaGrpcNormal(t) diff --git a/test/xa_test.go b/test/xa_test.go index 8c36586..793039b 100644 --- a/test/xa_test.go +++ b/test/xa_test.go @@ -12,9 +12,6 @@ import ( ) func TestXa(t *testing.T) { - if config.DB["driver"] != "mysql" { - return - } xaLocalError(t) xaNormal(t) xaDuplicate(t) From d385b5ef604faa5d9c2b4926242c090610d1feec Mon Sep 17 00:00:00 2001 From: yedf2 <120050102@qq.com> Date: Sat, 9 Oct 2021 20:20:29 +0800 Subject: [PATCH 05/12] optimize test --- dtmcli/barrier.go | 3 --- dtmcli/utils.go | 7 ------- test/barrier_tcc_test.go | 17 ++++++++++++++++- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/dtmcli/barrier.go b/dtmcli/barrier.go index 64d76ec..59b6582 100644 --- a/dtmcli/barrier.go +++ b/dtmcli/barrier.go @@ -57,9 +57,6 @@ func insertBarrier(tx Tx, transType string, gid string, branchID string, branchT func (bb *BranchBarrier) Call(tx Tx, busiCall BusiFunc) (rerr error) { bb.BarrierID = bb.BarrierID + 1 bid := fmt.Sprintf("%02d", bb.BarrierID) - if rerr != nil { - return - } defer func() { // Logf("barrier call error is %v", rerr) if x := recover(); x != nil { diff --git a/dtmcli/utils.go b/dtmcli/utils.go index aa5761f..3b800a1 100644 --- a/dtmcli/utils.go +++ b/dtmcli/utils.go @@ -226,13 +226,6 @@ func DBExec(db DB, sql string, values ...interface{}) (affected int64, rerr erro return } -// DBQueryRow use raw tx to query row -func DBQueryRow(db DB, query string, args ...interface{}) *sql.Row { - query = makeSQLCompatible(query) - Logf("querying: "+query, args...) - return db.QueryRow(query, args...) -} - // GetDsn get dsn from map config func GetDsn(conf map[string]string) string { host := MayReplaceLocalhost(conf["host"]) diff --git a/test/barrier_tcc_test.go b/test/barrier_tcc_test.go index b864373..8bb656b 100644 --- a/test/barrier_tcc_test.go +++ b/test/barrier_tcc_test.go @@ -1,6 +1,8 @@ package test import ( + "context" + "database/sql" "fmt" "strings" "testing" @@ -18,7 +20,7 @@ func TestBarrierTcc(t *testing.T) { tccBarrierDisorder(t) tccBarrierNormal(t) tccBarrierRollback(t) - + barrierPanic(t) } func tccBarrierRollback(t *testing.T) { @@ -115,3 +117,16 @@ func tccBarrierDisorder(t *testing.T) { assert.Equal(t, []string{dtmcli.StatusSucceed, dtmcli.StatusPrepared, dtmcli.StatusPrepared}, getBranchesStatus(gid)) assert.Equal(t, dtmcli.StatusFailed, getTransStatus(gid)) } + +func barrierPanic(t *testing.T) { + bb := &dtmcli.BranchBarrier{TransType: "saga", Gid: "gid1", BranchID: "bid1", BranchType: "action", BarrierID: 1} + var err error + func() { + defer dtmcli.P2E(&err) + tx, _ := dbGet().ToSQLDB().BeginTx(context.Background(), &sql.TxOptions{}) + bb.Call(tx, func(db dtmcli.DB) error { + panic(fmt.Errorf("an error")) + }) + }() + assert.Error(t, err, fmt.Errorf("an error")) +} From b5dad6a8a923d19b79acdb98f287b2f9f7d8a29e Mon Sep 17 00:00:00 2001 From: yedf2 <120050102@qq.com> Date: Mon, 11 Oct 2021 07:47:41 +0800 Subject: [PATCH 06/12] change to en default readme --- README-cn.md | 120 ++++++++++++++++++++++++++++++++++++++++ README.md | 153 ++++++++++++++++++++++++++------------------------- 2 files changed, 199 insertions(+), 74 deletions(-) create mode 100644 README-cn.md diff --git a/README-cn.md b/README-cn.md new file mode 100644 index 0000000..d64d137 --- /dev/null +++ b/README-cn.md @@ -0,0 +1,120 @@ +![license](https://img.shields.io/github/license/yedf/dtm) +[![Build Status](https://travis-ci.com/yedf/dtm.svg?branch=main)](https://travis-ci.com/yedf/dtm) +[![Coverage Status](https://coveralls.io/repos/github/yedf/dtm/badge.svg?branch=main)](https://coveralls.io/github/yedf/dtm?branch=main) +[![Go Report Card](https://goreportcard.com/badge/github.com/yedf/dtm)](https://goreportcard.com/report/github.com/yedf/dtm) +[![Go Reference](https://pkg.go.dev/badge/github.com/yedf/dtm.svg)](https://pkg.go.dev/github.com/yedf/dtm) +[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#database) + +# [English Docs](https://en.dtm.pub) +# GO语言分布式事务管理服务 + +DTM是首款golang的开源分布式事务管理器,优雅的解决了幂等、空补偿、悬挂等分布式事务难题。提供了简单易用、高性能、易水平扩展的分布式事务解决方案。 + +受邀参加中国数据库大会分享[多语言环境下分布式事务实践](http://dtcc.it168.com/yicheng.html#b9) + +## 谁在使用dtm + +[Ivydad 常青藤爸爸](https://ivydad.com) + +[Eglass 视咖镜小二](https://epeijing.cn) + +[极欧科技](http://jiou.me) + +[金数智联]() + +## 亮点 + +* 极易接入 + - 支持HTTP,提供非常简单的接口,极大降低上手分布式事务的难度,新手也能快速接入 +* 使用简单 + - 开发者不再担心悬挂、空补偿、幂等各类问题,框架层代为处理 +* 跨语言 + - 可适合多语言栈的公司使用。方便go、python、php、nodejs、ruby、c# 各类语言使用。 +* 易部署、易扩展 + - 仅依赖mysql,部署简单,易集群化,易水平扩展 +* 多种分布式事务协议支持 + - TCC、SAGA、XA、事务消息 + +## 与其他框架对比 + +目前开源的分布式事务框架,暂未看到非Java语言有成熟的框架。而Java语言的较多,有阿里的SEATA、华为的ServiceComb-Pack,京东的shardingsphere,以及himly,tcc-transaction,ByteTCC等等,其中以seata应用最为广泛。 + +下面是dtm和seata的主要特性对比: + +| 特性| DTM | SEATA |备注| +|:-----:|:----:|:----:|:----:| +| 支持语言 |Go、Java、python、php、c#...|Java|dtm可轻松接入一门新语言| +|异常处理| [子事务屏障自动处理](https://zhuanlan.zhihu.com/p/388444465) |手动处理 |dtm解决了幂等、悬挂、空补偿| +| TCC事务| ||| +| XA事务|||| +|AT事务|||AT与XA类似,性能更好,但有脏回滚| +| SAGA事务 |简单模式 |状态机复杂模式 |dtm的状态机模式在规划中| +|事务消息|||dtm提供类似rocketmq的事务消息| +|单服务多数据源|||| +|通信协议|HTTP、gRPC|dubbo等协议,无HTTP|dtm对云原生更加友好| +|star数量|github stars|github stars|dtm从20210604发布0.1,发展快| + +从上面对比的特性来看,如果您的语言栈包含了Java之外的语言,那么dtm是您的首选。如果您的语言栈是Java,您也可以选择接入dtm,使用子事务屏障技术,简化您的业务编写。 + +## [教程与文档](https://dtm.pub) + +## [各语言客户端及示例](https://dtm.pub/summary/code.html#go) + +## 快速开始 + +### 安装 + +`git clone https://github.com/yedf/dtm` + +### dtm依赖于mysql + +配置mysql: + +`cp conf.sample.yml conf.yml # 修改conf.yml` + +### 启动并运行saga示例 +`go run app/main.go saga` + +## 开始使用 + +### 使用 +``` GO + // 具体业务微服务地址 + const qsBusi = "http://localhost:8081/api/busi_saga" + req := &gin.H{"amount": 30} // 微服务的载荷 + // DtmServer为DTM服务的地址,是一个url + DtmServer := "http://localhost:8080/api/dtmsvr" + saga := dtmcli.NewSaga(DtmServer, dtmcli.MustGenGid(DtmServer)). + // 添加一个TransOut的子事务,正向操作为url: qsBusi+"/TransOut", 补偿操作为url: qsBusi+"/TransOutCompensate" + Add(qsBusi+"/TransOut", qsBusi+"/TransOutCompensate", req). + // 添加一个TransIn的子事务,正向操作为url: qsBusi+"/TransIn", 补偿操作为url: qsBusi+"/TransInCompensate" + Add(qsBusi+"/TransIn", qsBusi+"/TransInCompensate", req) + // 提交saga事务,dtm会完成所有的子事务/回滚所有的子事务 + err := saga.Submit() +``` + +成功运行后,可以看到TransOut、TransIn依次被调用,完成了整个分布式事务 + +### 时序图 + +上述saga分布式事务的时序图如下: + + + +### 完整示例 +参考[examples/quick_start.go](./examples/quick_start.go) + +## 交流群 +请加 yedf2008 好友或者扫码加好友,验证回复 dtm 按照指引进群 + +![yedf2008](http://service.ivydad.com/cover/dubbingb6b5e2c0-2d2a-cd59-f7c5-c6b90aceb6f1.jpeg) + +如果您觉得[dtm](https://github.com/yedf/dtm)不错,或者对您有帮助,请赏颗星吧! + +## 谁在使用 +
+ 常青藤爸爸 + 镜小二 + 极欧科技 + 金数智联 +
diff --git a/README.md b/README.md index d64d137..84b6cd2 100644 --- a/README.md +++ b/README.md @@ -5,116 +5,121 @@ [![Go Reference](https://pkg.go.dev/badge/github.com/yedf/dtm.svg)](https://pkg.go.dev/github.com/yedf/dtm) [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#database) -# [English Docs](https://en.dtm.pub) -# GO语言分布式事务管理服务 +# [中文文档](http://dtm.pub) -DTM是首款golang的开源分布式事务管理器,优雅的解决了幂等、空补偿、悬挂等分布式事务难题。提供了简单易用、高性能、易水平扩展的分布式事务解决方案。 +# A Cross Language Transaction Manager -受邀参加中国数据库大会分享[多语言环境下分布式事务实践](http://dtcc.it168.com/yicheng.html#b9) +## Who's using DTM -## 谁在使用dtm +[Ivydad](https://ivydad.com) -[Ivydad 常青藤爸爸](https://ivydad.com) +[Eglass](https://epeijing.cn) -[Eglass 视咖镜小二](https://epeijing.cn) +[Jiou](http://jiou.me) -[极欧科技](http://jiou.me) +[GoldenData]() -[金数智联]() +## What is DTM -## 亮点 +DTM is the first distributed transaction management framework in Golang. Unlike other frameworks, DTM provides extremely easy access interfaces of HTTP and gRPC, supports multiple language bindings, and handles tricky problems of unordered sub-transactions at the framework level. -* 极易接入 - - 支持HTTP,提供非常简单的接口,极大降低上手分布式事务的难度,新手也能快速接入 -* 使用简单 - - 开发者不再担心悬挂、空补偿、幂等各类问题,框架层代为处理 -* 跨语言 - - 可适合多语言栈的公司使用。方便go、python、php、nodejs、ruby、c# 各类语言使用。 -* 易部署、易扩展 - - 仅依赖mysql,部署简单,易集群化,易水平扩展 -* 多种分布式事务协议支持 - - TCC、SAGA、XA、事务消息 +## Features -## 与其他框架对比 +* Extremely easy to adapt + - Support HTTP and gRPC, provide easy-to-use programming interfaces, lower substantially the barrier of getting started with distributed transactions. Newcomers can adapt quickly. -目前开源的分布式事务框架,暂未看到非Java语言有成熟的框架。而Java语言的较多,有阿里的SEATA、华为的ServiceComb-Pack,京东的shardingsphere,以及himly,tcc-transaction,ByteTCC等等,其中以seata应用最为广泛。 +* Easy to use + - Relieving developers from worrying about suspension, null compensation, idempotent transaction, and other tricky problems, the framework layer handles them all. -下面是dtm和seata的主要特性对比: +* Language-agnostic + - Suit for companies with multiple-language stacks. + Easy to write bindings for Go, Python, PHP, Node.js, Ruby, and other languages. -| 特性| DTM | SEATA |备注| -|:-----:|:----:|:----:|:----:| -| 支持语言 |Go、Java、python、php、c#...|Java|dtm可轻松接入一门新语言| -|异常处理| [子事务屏障自动处理](https://zhuanlan.zhihu.com/p/388444465) |手动处理 |dtm解决了幂等、悬挂、空补偿| -| TCC事务| ||| -| XA事务|||| -|AT事务|||AT与XA类似,性能更好,但有脏回滚| -| SAGA事务 |简单模式 |状态机复杂模式 |dtm的状态机模式在规划中| -|事务消息|||dtm提供类似rocketmq的事务消息| -|单服务多数据源|||| -|通信协议|HTTP、gRPC|dubbo等协议,无HTTP|dtm对云原生更加友好| -|star数量|github stars|github stars|dtm从20210604发布0.1,发展快| +* Easy to deploy, easy to extend + - DTM depends only on MySQL, easy to deploy, cluster, and scale horizontally. -从上面对比的特性来看,如果您的语言栈包含了Java之外的语言,那么dtm是您的首选。如果您的语言栈是Java,您也可以选择接入dtm,使用子事务屏障技术,简化您的业务编写。 +* Support for multiple distributed transaction protocol + - TCC, SAGA, XA, Transactional messages. -## [教程与文档](https://dtm.pub) +## DTM vs. others -## [各语言客户端及示例](https://dtm.pub/summary/code.html#go) +There is no mature open-source distributed transaction framework for non-Java languages. +Mature open-source distributed transaction frameworks for Java language include Ali's Seata, Huawei's ServiceComb-Pack, Jingdong's shardingsphere, himly, tcc-transaction, ByteTCC, and so on, of which Seata is most widely used. -## 快速开始 +The following is a comparison of the main features of dtm and Seata. -### 安装 -`git clone https://github.com/yedf/dtm` +| Features | DTM | Seata | Remarks | +| :-----: | :----: | :----: | :----: | +| Supported languages | Golang, Python, PHP, and others | Java | dtm allows easy access from a new language | +| Exception handling | [Sub-transaction barrier](https://zhuanlan.zhihu.com/p/388444465) | manual | dtm solves idempotent transaction, hanging, null compensation | +| TCC | | | | +| XA | | | | +| AT | | | AT is similar to XA with better performance but with dirty rollback | +| SAGA | Simple mode | complicated state-machine mode | dtm's state-machine mode is being planned | +| Transactional Messaging | | | dtm provides Transactional Messaging similar to RocketMQ | +| Multiple DBs in a service |||| +| Communication protocols | HTTP, gRPC | Dubbo, no HTTP | | +| Star count | github stars | github stars | dtm 0.1 is released from 20210604 and under fast development | + +From the features' comparison above, if your language stack includes languages other than Java, then dtm is the one for you. +If your language stack is Java, you can also choose to access dtm and use sub-transaction barrier technology to simplify your business development. + +## [Cook Book](https://en.dtm.pub) + +# Quick start -### dtm依赖于mysql +### Install -配置mysql: +`git clone https://github.com/yedf/dtm` + +### Configure Mysql -`cp conf.sample.yml conf.yml # 修改conf.yml` +`cp conf.sample.yml conf.yml # Modify conf.yml` -### 启动并运行saga示例 -`go run app/main.go saga` +### Start the example +`go run app/main.go` -## 开始使用 +# Code -### 使用 -``` GO - // 具体业务微服务地址 +### Use +``` go + // business micro-service address const qsBusi = "http://localhost:8081/api/busi_saga" - req := &gin.H{"amount": 30} // 微服务的载荷 - // DtmServer为DTM服务的地址,是一个url + // The address where DtmServer serves DTM, which is a url DtmServer := "http://localhost:8080/api/dtmsvr" - saga := dtmcli.NewSaga(DtmServer, dtmcli.MustGenGid(DtmServer)). - // 添加一个TransOut的子事务,正向操作为url: qsBusi+"/TransOut", 补偿操作为url: qsBusi+"/TransOutCompensate" - Add(qsBusi+"/TransOut", qsBusi+"/TransOutCompensate", req). - // 添加一个TransIn的子事务,正向操作为url: qsBusi+"/TransIn", 补偿操作为url: qsBusi+"/TransInCompensate" - Add(qsBusi+"/TransIn", qsBusi+"/TransInCompensate", req) - // 提交saga事务,dtm会完成所有的子事务/回滚所有的子事务 - err := saga.Submit() + req := &gin.H{"amount": 30} // micro-service payload + // DtmServer is the address of DTM micro-service + saga := dtmcli.NewSaga(DtmServer, dtmcli.MustGenGid(DtmServer)). + // add a TransOut subtraction,forward operation with url: qsBusi+"/TransOut", reverse compensation operation with url: qsBusi+"/TransOutCompensate" + Add(qsBusi+"/TransOut", qsBusi+"/TransOutCompensate", req). + // add a TransIn subtraction, forward operation with url: qsBusi+"/TransIn", reverse compensation operation with url: qsBusi+"/TransInCompensate" + Add(qsBusi+"/TransIn", qsBusi+"/TransInCompensate", req) + // submit the created saga transaction,dtm ensures all subtractions either complete or get revoked + err := saga.Submit() ``` +### Complete example -成功运行后,可以看到TransOut、TransIn依次被调用,完成了整个分布式事务 +Refer to [examples/quick_start.go](./examples/quick_start.go). -### 时序图 +### Slack -上述saga分布式事务的时序图如下: +You can join the [DTM slack channel here](https://join.slack.com/t/dtm-w6k9662/shared_invite/zt-vkrph4k1-eFqEFnMkbmlXqfUo5GWHWw). - +### Wechat -### 完整示例 -参考[examples/quick_start.go](./examples/quick_start.go) - -## 交流群 -请加 yedf2008 好友或者扫码加好友,验证回复 dtm 按照指引进群 +Add wechat friend with id yedf2008, or scan the OR code. Fill in dtm as verification. ![yedf2008](http://service.ivydad.com/cover/dubbingb6b5e2c0-2d2a-cd59-f7c5-c6b90aceb6f1.jpeg) -如果您觉得[dtm](https://github.com/yedf/dtm)不错,或者对您有帮助,请赏颗星吧! +### Give a star! ⭐ + +If you think this project is good, or helpful to you, please give a star! -## 谁在使用 +### Who is using
- 常青藤爸爸 - 镜小二 - 极欧科技 - 金数智联 + Ivydad + Eglass + Jiou + GoldenData
From c695c6f2e4c104cee362af6d00f8f45397d02bdb Mon Sep 17 00:00:00 2001 From: yedf2 <120050102@qq.com> Date: Mon, 11 Oct 2021 11:44:13 +0800 Subject: [PATCH 07/12] change to default cn --- README.md | 153 ++++++++++++++++++++++++++---------------------------- 1 file changed, 74 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index 84b6cd2..d64d137 100644 --- a/README.md +++ b/README.md @@ -5,121 +5,116 @@ [![Go Reference](https://pkg.go.dev/badge/github.com/yedf/dtm.svg)](https://pkg.go.dev/github.com/yedf/dtm) [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#database) -# [中文文档](http://dtm.pub) +# [English Docs](https://en.dtm.pub) +# GO语言分布式事务管理服务 -# A Cross Language Transaction Manager +DTM是首款golang的开源分布式事务管理器,优雅的解决了幂等、空补偿、悬挂等分布式事务难题。提供了简单易用、高性能、易水平扩展的分布式事务解决方案。 -## Who's using DTM +受邀参加中国数据库大会分享[多语言环境下分布式事务实践](http://dtcc.it168.com/yicheng.html#b9) -[Ivydad](https://ivydad.com) +## 谁在使用dtm -[Eglass](https://epeijing.cn) +[Ivydad 常青藤爸爸](https://ivydad.com) -[Jiou](http://jiou.me) +[Eglass 视咖镜小二](https://epeijing.cn) -[GoldenData]() +[极欧科技](http://jiou.me) -## What is DTM +[金数智联]() -DTM is the first distributed transaction management framework in Golang. Unlike other frameworks, DTM provides extremely easy access interfaces of HTTP and gRPC, supports multiple language bindings, and handles tricky problems of unordered sub-transactions at the framework level. +## 亮点 -## Features +* 极易接入 + - 支持HTTP,提供非常简单的接口,极大降低上手分布式事务的难度,新手也能快速接入 +* 使用简单 + - 开发者不再担心悬挂、空补偿、幂等各类问题,框架层代为处理 +* 跨语言 + - 可适合多语言栈的公司使用。方便go、python、php、nodejs、ruby、c# 各类语言使用。 +* 易部署、易扩展 + - 仅依赖mysql,部署简单,易集群化,易水平扩展 +* 多种分布式事务协议支持 + - TCC、SAGA、XA、事务消息 -* Extremely easy to adapt - - Support HTTP and gRPC, provide easy-to-use programming interfaces, lower substantially the barrier of getting started with distributed transactions. Newcomers can adapt quickly. +## 与其他框架对比 -* Easy to use - - Relieving developers from worrying about suspension, null compensation, idempotent transaction, and other tricky problems, the framework layer handles them all. +目前开源的分布式事务框架,暂未看到非Java语言有成熟的框架。而Java语言的较多,有阿里的SEATA、华为的ServiceComb-Pack,京东的shardingsphere,以及himly,tcc-transaction,ByteTCC等等,其中以seata应用最为广泛。 -* Language-agnostic - - Suit for companies with multiple-language stacks. - Easy to write bindings for Go, Python, PHP, Node.js, Ruby, and other languages. +下面是dtm和seata的主要特性对比: -* Easy to deploy, easy to extend - - DTM depends only on MySQL, easy to deploy, cluster, and scale horizontally. +| 特性| DTM | SEATA |备注| +|:-----:|:----:|:----:|:----:| +| 支持语言 |Go、Java、python、php、c#...|Java|dtm可轻松接入一门新语言| +|异常处理| [子事务屏障自动处理](https://zhuanlan.zhihu.com/p/388444465) |手动处理 |dtm解决了幂等、悬挂、空补偿| +| TCC事务| ||| +| XA事务|||| +|AT事务|||AT与XA类似,性能更好,但有脏回滚| +| SAGA事务 |简单模式 |状态机复杂模式 |dtm的状态机模式在规划中| +|事务消息|||dtm提供类似rocketmq的事务消息| +|单服务多数据源|||| +|通信协议|HTTP、gRPC|dubbo等协议,无HTTP|dtm对云原生更加友好| +|star数量|github stars|github stars|dtm从20210604发布0.1,发展快| -* Support for multiple distributed transaction protocol - - TCC, SAGA, XA, Transactional messages. +从上面对比的特性来看,如果您的语言栈包含了Java之外的语言,那么dtm是您的首选。如果您的语言栈是Java,您也可以选择接入dtm,使用子事务屏障技术,简化您的业务编写。 -## DTM vs. others +## [教程与文档](https://dtm.pub) -There is no mature open-source distributed transaction framework for non-Java languages. -Mature open-source distributed transaction frameworks for Java language include Ali's Seata, Huawei's ServiceComb-Pack, Jingdong's shardingsphere, himly, tcc-transaction, ByteTCC, and so on, of which Seata is most widely used. +## [各语言客户端及示例](https://dtm.pub/summary/code.html#go) -The following is a comparison of the main features of dtm and Seata. +## 快速开始 - -| Features | DTM | Seata | Remarks | -| :-----: | :----: | :----: | :----: | -| Supported languages | Golang, Python, PHP, and others | Java | dtm allows easy access from a new language | -| Exception handling | [Sub-transaction barrier](https://zhuanlan.zhihu.com/p/388444465) | manual | dtm solves idempotent transaction, hanging, null compensation | -| TCC | | | | -| XA | | | | -| AT | | | AT is similar to XA with better performance but with dirty rollback | -| SAGA | Simple mode | complicated state-machine mode | dtm's state-machine mode is being planned | -| Transactional Messaging | | | dtm provides Transactional Messaging similar to RocketMQ | -| Multiple DBs in a service |||| -| Communication protocols | HTTP, gRPC | Dubbo, no HTTP | | -| Star count | github stars | github stars | dtm 0.1 is released from 20210604 and under fast development | - -From the features' comparison above, if your language stack includes languages other than Java, then dtm is the one for you. -If your language stack is Java, you can also choose to access dtm and use sub-transaction barrier technology to simplify your business development. - -## [Cook Book](https://en.dtm.pub) - -# Quick start - -### Install +### 安装 `git clone https://github.com/yedf/dtm` -### Configure Mysql +### dtm依赖于mysql + +配置mysql: -`cp conf.sample.yml conf.yml # Modify conf.yml` +`cp conf.sample.yml conf.yml # 修改conf.yml` -### Start the example -`go run app/main.go` +### 启动并运行saga示例 +`go run app/main.go saga` -# Code +## 开始使用 -### Use -``` go - // business micro-service address +### 使用 +``` GO + // 具体业务微服务地址 const qsBusi = "http://localhost:8081/api/busi_saga" - // The address where DtmServer serves DTM, which is a url + req := &gin.H{"amount": 30} // 微服务的载荷 + // DtmServer为DTM服务的地址,是一个url DtmServer := "http://localhost:8080/api/dtmsvr" - req := &gin.H{"amount": 30} // micro-service payload - // DtmServer is the address of DTM micro-service - saga := dtmcli.NewSaga(DtmServer, dtmcli.MustGenGid(DtmServer)). - // add a TransOut subtraction,forward operation with url: qsBusi+"/TransOut", reverse compensation operation with url: qsBusi+"/TransOutCompensate" - Add(qsBusi+"/TransOut", qsBusi+"/TransOutCompensate", req). - // add a TransIn subtraction, forward operation with url: qsBusi+"/TransIn", reverse compensation operation with url: qsBusi+"/TransInCompensate" - Add(qsBusi+"/TransIn", qsBusi+"/TransInCompensate", req) - // submit the created saga transaction,dtm ensures all subtractions either complete or get revoked - err := saga.Submit() + saga := dtmcli.NewSaga(DtmServer, dtmcli.MustGenGid(DtmServer)). + // 添加一个TransOut的子事务,正向操作为url: qsBusi+"/TransOut", 补偿操作为url: qsBusi+"/TransOutCompensate" + Add(qsBusi+"/TransOut", qsBusi+"/TransOutCompensate", req). + // 添加一个TransIn的子事务,正向操作为url: qsBusi+"/TransIn", 补偿操作为url: qsBusi+"/TransInCompensate" + Add(qsBusi+"/TransIn", qsBusi+"/TransInCompensate", req) + // 提交saga事务,dtm会完成所有的子事务/回滚所有的子事务 + err := saga.Submit() ``` -### Complete example -Refer to [examples/quick_start.go](./examples/quick_start.go). +成功运行后,可以看到TransOut、TransIn依次被调用,完成了整个分布式事务 -### Slack +### 时序图 -You can join the [DTM slack channel here](https://join.slack.com/t/dtm-w6k9662/shared_invite/zt-vkrph4k1-eFqEFnMkbmlXqfUo5GWHWw). +上述saga分布式事务的时序图如下: -### Wechat + -Add wechat friend with id yedf2008, or scan the OR code. Fill in dtm as verification. +### 完整示例 +参考[examples/quick_start.go](./examples/quick_start.go) -![yedf2008](http://service.ivydad.com/cover/dubbingb6b5e2c0-2d2a-cd59-f7c5-c6b90aceb6f1.jpeg) +## 交流群 +请加 yedf2008 好友或者扫码加好友,验证回复 dtm 按照指引进群 -### Give a star! ⭐ +![yedf2008](http://service.ivydad.com/cover/dubbingb6b5e2c0-2d2a-cd59-f7c5-c6b90aceb6f1.jpeg) -If you think this project is good, or helpful to you, please give a star! +如果您觉得[dtm](https://github.com/yedf/dtm)不错,或者对您有帮助,请赏颗星吧! -### Who is using +## 谁在使用
- Ivydad - Eglass - Jiou - GoldenData + 常青藤爸爸 + 镜小二 + 极欧科技 + 金数智联
From cdc610b2f395cde50ccc111507f5c19b98a253e9 Mon Sep 17 00:00:00 2001 From: yedf2 <120050102@qq.com> Date: Mon, 11 Oct 2021 18:03:59 +0800 Subject: [PATCH 08/12] default to english readme --- README.md | 153 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 79 insertions(+), 74 deletions(-) diff --git a/README.md b/README.md index d64d137..84b6cd2 100644 --- a/README.md +++ b/README.md @@ -5,116 +5,121 @@ [![Go Reference](https://pkg.go.dev/badge/github.com/yedf/dtm.svg)](https://pkg.go.dev/github.com/yedf/dtm) [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#database) -# [English Docs](https://en.dtm.pub) -# GO语言分布式事务管理服务 +# [中文文档](http://dtm.pub) -DTM是首款golang的开源分布式事务管理器,优雅的解决了幂等、空补偿、悬挂等分布式事务难题。提供了简单易用、高性能、易水平扩展的分布式事务解决方案。 +# A Cross Language Transaction Manager -受邀参加中国数据库大会分享[多语言环境下分布式事务实践](http://dtcc.it168.com/yicheng.html#b9) +## Who's using DTM -## 谁在使用dtm +[Ivydad](https://ivydad.com) -[Ivydad 常青藤爸爸](https://ivydad.com) +[Eglass](https://epeijing.cn) -[Eglass 视咖镜小二](https://epeijing.cn) +[Jiou](http://jiou.me) -[极欧科技](http://jiou.me) +[GoldenData]() -[金数智联]() +## What is DTM -## 亮点 +DTM is the first distributed transaction management framework in Golang. Unlike other frameworks, DTM provides extremely easy access interfaces of HTTP and gRPC, supports multiple language bindings, and handles tricky problems of unordered sub-transactions at the framework level. -* 极易接入 - - 支持HTTP,提供非常简单的接口,极大降低上手分布式事务的难度,新手也能快速接入 -* 使用简单 - - 开发者不再担心悬挂、空补偿、幂等各类问题,框架层代为处理 -* 跨语言 - - 可适合多语言栈的公司使用。方便go、python、php、nodejs、ruby、c# 各类语言使用。 -* 易部署、易扩展 - - 仅依赖mysql,部署简单,易集群化,易水平扩展 -* 多种分布式事务协议支持 - - TCC、SAGA、XA、事务消息 +## Features -## 与其他框架对比 +* Extremely easy to adapt + - Support HTTP and gRPC, provide easy-to-use programming interfaces, lower substantially the barrier of getting started with distributed transactions. Newcomers can adapt quickly. -目前开源的分布式事务框架,暂未看到非Java语言有成熟的框架。而Java语言的较多,有阿里的SEATA、华为的ServiceComb-Pack,京东的shardingsphere,以及himly,tcc-transaction,ByteTCC等等,其中以seata应用最为广泛。 +* Easy to use + - Relieving developers from worrying about suspension, null compensation, idempotent transaction, and other tricky problems, the framework layer handles them all. -下面是dtm和seata的主要特性对比: +* Language-agnostic + - Suit for companies with multiple-language stacks. + Easy to write bindings for Go, Python, PHP, Node.js, Ruby, and other languages. -| 特性| DTM | SEATA |备注| -|:-----:|:----:|:----:|:----:| -| 支持语言 |Go、Java、python、php、c#...|Java|dtm可轻松接入一门新语言| -|异常处理| [子事务屏障自动处理](https://zhuanlan.zhihu.com/p/388444465) |手动处理 |dtm解决了幂等、悬挂、空补偿| -| TCC事务| ||| -| XA事务|||| -|AT事务|||AT与XA类似,性能更好,但有脏回滚| -| SAGA事务 |简单模式 |状态机复杂模式 |dtm的状态机模式在规划中| -|事务消息|||dtm提供类似rocketmq的事务消息| -|单服务多数据源|||| -|通信协议|HTTP、gRPC|dubbo等协议,无HTTP|dtm对云原生更加友好| -|star数量|github stars|github stars|dtm从20210604发布0.1,发展快| +* Easy to deploy, easy to extend + - DTM depends only on MySQL, easy to deploy, cluster, and scale horizontally. -从上面对比的特性来看,如果您的语言栈包含了Java之外的语言,那么dtm是您的首选。如果您的语言栈是Java,您也可以选择接入dtm,使用子事务屏障技术,简化您的业务编写。 +* Support for multiple distributed transaction protocol + - TCC, SAGA, XA, Transactional messages. -## [教程与文档](https://dtm.pub) +## DTM vs. others -## [各语言客户端及示例](https://dtm.pub/summary/code.html#go) +There is no mature open-source distributed transaction framework for non-Java languages. +Mature open-source distributed transaction frameworks for Java language include Ali's Seata, Huawei's ServiceComb-Pack, Jingdong's shardingsphere, himly, tcc-transaction, ByteTCC, and so on, of which Seata is most widely used. -## 快速开始 +The following is a comparison of the main features of dtm and Seata. -### 安装 -`git clone https://github.com/yedf/dtm` +| Features | DTM | Seata | Remarks | +| :-----: | :----: | :----: | :----: | +| Supported languages | Golang, Python, PHP, and others | Java | dtm allows easy access from a new language | +| Exception handling | [Sub-transaction barrier](https://zhuanlan.zhihu.com/p/388444465) | manual | dtm solves idempotent transaction, hanging, null compensation | +| TCC | | | | +| XA | | | | +| AT | | | AT is similar to XA with better performance but with dirty rollback | +| SAGA | Simple mode | complicated state-machine mode | dtm's state-machine mode is being planned | +| Transactional Messaging | | | dtm provides Transactional Messaging similar to RocketMQ | +| Multiple DBs in a service |||| +| Communication protocols | HTTP, gRPC | Dubbo, no HTTP | | +| Star count | github stars | github stars | dtm 0.1 is released from 20210604 and under fast development | + +From the features' comparison above, if your language stack includes languages other than Java, then dtm is the one for you. +If your language stack is Java, you can also choose to access dtm and use sub-transaction barrier technology to simplify your business development. + +## [Cook Book](https://en.dtm.pub) + +# Quick start -### dtm依赖于mysql +### Install -配置mysql: +`git clone https://github.com/yedf/dtm` + +### Configure Mysql -`cp conf.sample.yml conf.yml # 修改conf.yml` +`cp conf.sample.yml conf.yml # Modify conf.yml` -### 启动并运行saga示例 -`go run app/main.go saga` +### Start the example +`go run app/main.go` -## 开始使用 +# Code -### 使用 -``` GO - // 具体业务微服务地址 +### Use +``` go + // business micro-service address const qsBusi = "http://localhost:8081/api/busi_saga" - req := &gin.H{"amount": 30} // 微服务的载荷 - // DtmServer为DTM服务的地址,是一个url + // The address where DtmServer serves DTM, which is a url DtmServer := "http://localhost:8080/api/dtmsvr" - saga := dtmcli.NewSaga(DtmServer, dtmcli.MustGenGid(DtmServer)). - // 添加一个TransOut的子事务,正向操作为url: qsBusi+"/TransOut", 补偿操作为url: qsBusi+"/TransOutCompensate" - Add(qsBusi+"/TransOut", qsBusi+"/TransOutCompensate", req). - // 添加一个TransIn的子事务,正向操作为url: qsBusi+"/TransIn", 补偿操作为url: qsBusi+"/TransInCompensate" - Add(qsBusi+"/TransIn", qsBusi+"/TransInCompensate", req) - // 提交saga事务,dtm会完成所有的子事务/回滚所有的子事务 - err := saga.Submit() + req := &gin.H{"amount": 30} // micro-service payload + // DtmServer is the address of DTM micro-service + saga := dtmcli.NewSaga(DtmServer, dtmcli.MustGenGid(DtmServer)). + // add a TransOut subtraction,forward operation with url: qsBusi+"/TransOut", reverse compensation operation with url: qsBusi+"/TransOutCompensate" + Add(qsBusi+"/TransOut", qsBusi+"/TransOutCompensate", req). + // add a TransIn subtraction, forward operation with url: qsBusi+"/TransIn", reverse compensation operation with url: qsBusi+"/TransInCompensate" + Add(qsBusi+"/TransIn", qsBusi+"/TransInCompensate", req) + // submit the created saga transaction,dtm ensures all subtractions either complete or get revoked + err := saga.Submit() ``` +### Complete example -成功运行后,可以看到TransOut、TransIn依次被调用,完成了整个分布式事务 +Refer to [examples/quick_start.go](./examples/quick_start.go). -### 时序图 +### Slack -上述saga分布式事务的时序图如下: +You can join the [DTM slack channel here](https://join.slack.com/t/dtm-w6k9662/shared_invite/zt-vkrph4k1-eFqEFnMkbmlXqfUo5GWHWw). - +### Wechat -### 完整示例 -参考[examples/quick_start.go](./examples/quick_start.go) - -## 交流群 -请加 yedf2008 好友或者扫码加好友,验证回复 dtm 按照指引进群 +Add wechat friend with id yedf2008, or scan the OR code. Fill in dtm as verification. ![yedf2008](http://service.ivydad.com/cover/dubbingb6b5e2c0-2d2a-cd59-f7c5-c6b90aceb6f1.jpeg) -如果您觉得[dtm](https://github.com/yedf/dtm)不错,或者对您有帮助,请赏颗星吧! +### Give a star! ⭐ + +If you think this project is good, or helpful to you, please give a star! -## 谁在使用 +### Who is using
- 常青藤爸爸 - 镜小二 - 极欧科技 - 金数智联 + Ivydad + Eglass + Jiou + GoldenData
From b7ff466e3ed1d542e9b74d0fcdef20bc8a486da8 Mon Sep 17 00:00:00 2001 From: yedf2 <120050102@qq.com> Date: Tue, 12 Oct 2021 09:26:58 +0800 Subject: [PATCH 09/12] default to cn --- README.md | 153 ++++++++++++++++++++++++++---------------------------- 1 file changed, 74 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index 84b6cd2..d64d137 100644 --- a/README.md +++ b/README.md @@ -5,121 +5,116 @@ [![Go Reference](https://pkg.go.dev/badge/github.com/yedf/dtm.svg)](https://pkg.go.dev/github.com/yedf/dtm) [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#database) -# [中文文档](http://dtm.pub) +# [English Docs](https://en.dtm.pub) +# GO语言分布式事务管理服务 -# A Cross Language Transaction Manager +DTM是首款golang的开源分布式事务管理器,优雅的解决了幂等、空补偿、悬挂等分布式事务难题。提供了简单易用、高性能、易水平扩展的分布式事务解决方案。 -## Who's using DTM +受邀参加中国数据库大会分享[多语言环境下分布式事务实践](http://dtcc.it168.com/yicheng.html#b9) -[Ivydad](https://ivydad.com) +## 谁在使用dtm -[Eglass](https://epeijing.cn) +[Ivydad 常青藤爸爸](https://ivydad.com) -[Jiou](http://jiou.me) +[Eglass 视咖镜小二](https://epeijing.cn) -[GoldenData]() +[极欧科技](http://jiou.me) -## What is DTM +[金数智联]() -DTM is the first distributed transaction management framework in Golang. Unlike other frameworks, DTM provides extremely easy access interfaces of HTTP and gRPC, supports multiple language bindings, and handles tricky problems of unordered sub-transactions at the framework level. +## 亮点 -## Features +* 极易接入 + - 支持HTTP,提供非常简单的接口,极大降低上手分布式事务的难度,新手也能快速接入 +* 使用简单 + - 开发者不再担心悬挂、空补偿、幂等各类问题,框架层代为处理 +* 跨语言 + - 可适合多语言栈的公司使用。方便go、python、php、nodejs、ruby、c# 各类语言使用。 +* 易部署、易扩展 + - 仅依赖mysql,部署简单,易集群化,易水平扩展 +* 多种分布式事务协议支持 + - TCC、SAGA、XA、事务消息 -* Extremely easy to adapt - - Support HTTP and gRPC, provide easy-to-use programming interfaces, lower substantially the barrier of getting started with distributed transactions. Newcomers can adapt quickly. +## 与其他框架对比 -* Easy to use - - Relieving developers from worrying about suspension, null compensation, idempotent transaction, and other tricky problems, the framework layer handles them all. +目前开源的分布式事务框架,暂未看到非Java语言有成熟的框架。而Java语言的较多,有阿里的SEATA、华为的ServiceComb-Pack,京东的shardingsphere,以及himly,tcc-transaction,ByteTCC等等,其中以seata应用最为广泛。 -* Language-agnostic - - Suit for companies with multiple-language stacks. - Easy to write bindings for Go, Python, PHP, Node.js, Ruby, and other languages. +下面是dtm和seata的主要特性对比: -* Easy to deploy, easy to extend - - DTM depends only on MySQL, easy to deploy, cluster, and scale horizontally. +| 特性| DTM | SEATA |备注| +|:-----:|:----:|:----:|:----:| +| 支持语言 |Go、Java、python、php、c#...|Java|dtm可轻松接入一门新语言| +|异常处理| [子事务屏障自动处理](https://zhuanlan.zhihu.com/p/388444465) |手动处理 |dtm解决了幂等、悬挂、空补偿| +| TCC事务| ||| +| XA事务|||| +|AT事务|||AT与XA类似,性能更好,但有脏回滚| +| SAGA事务 |简单模式 |状态机复杂模式 |dtm的状态机模式在规划中| +|事务消息|||dtm提供类似rocketmq的事务消息| +|单服务多数据源|||| +|通信协议|HTTP、gRPC|dubbo等协议,无HTTP|dtm对云原生更加友好| +|star数量|github stars|github stars|dtm从20210604发布0.1,发展快| -* Support for multiple distributed transaction protocol - - TCC, SAGA, XA, Transactional messages. +从上面对比的特性来看,如果您的语言栈包含了Java之外的语言,那么dtm是您的首选。如果您的语言栈是Java,您也可以选择接入dtm,使用子事务屏障技术,简化您的业务编写。 -## DTM vs. others +## [教程与文档](https://dtm.pub) -There is no mature open-source distributed transaction framework for non-Java languages. -Mature open-source distributed transaction frameworks for Java language include Ali's Seata, Huawei's ServiceComb-Pack, Jingdong's shardingsphere, himly, tcc-transaction, ByteTCC, and so on, of which Seata is most widely used. +## [各语言客户端及示例](https://dtm.pub/summary/code.html#go) -The following is a comparison of the main features of dtm and Seata. +## 快速开始 - -| Features | DTM | Seata | Remarks | -| :-----: | :----: | :----: | :----: | -| Supported languages | Golang, Python, PHP, and others | Java | dtm allows easy access from a new language | -| Exception handling | [Sub-transaction barrier](https://zhuanlan.zhihu.com/p/388444465) | manual | dtm solves idempotent transaction, hanging, null compensation | -| TCC | | | | -| XA | | | | -| AT | | | AT is similar to XA with better performance but with dirty rollback | -| SAGA | Simple mode | complicated state-machine mode | dtm's state-machine mode is being planned | -| Transactional Messaging | | | dtm provides Transactional Messaging similar to RocketMQ | -| Multiple DBs in a service |||| -| Communication protocols | HTTP, gRPC | Dubbo, no HTTP | | -| Star count | github stars | github stars | dtm 0.1 is released from 20210604 and under fast development | - -From the features' comparison above, if your language stack includes languages other than Java, then dtm is the one for you. -If your language stack is Java, you can also choose to access dtm and use sub-transaction barrier technology to simplify your business development. - -## [Cook Book](https://en.dtm.pub) - -# Quick start - -### Install +### 安装 `git clone https://github.com/yedf/dtm` -### Configure Mysql +### dtm依赖于mysql + +配置mysql: -`cp conf.sample.yml conf.yml # Modify conf.yml` +`cp conf.sample.yml conf.yml # 修改conf.yml` -### Start the example -`go run app/main.go` +### 启动并运行saga示例 +`go run app/main.go saga` -# Code +## 开始使用 -### Use -``` go - // business micro-service address +### 使用 +``` GO + // 具体业务微服务地址 const qsBusi = "http://localhost:8081/api/busi_saga" - // The address where DtmServer serves DTM, which is a url + req := &gin.H{"amount": 30} // 微服务的载荷 + // DtmServer为DTM服务的地址,是一个url DtmServer := "http://localhost:8080/api/dtmsvr" - req := &gin.H{"amount": 30} // micro-service payload - // DtmServer is the address of DTM micro-service - saga := dtmcli.NewSaga(DtmServer, dtmcli.MustGenGid(DtmServer)). - // add a TransOut subtraction,forward operation with url: qsBusi+"/TransOut", reverse compensation operation with url: qsBusi+"/TransOutCompensate" - Add(qsBusi+"/TransOut", qsBusi+"/TransOutCompensate", req). - // add a TransIn subtraction, forward operation with url: qsBusi+"/TransIn", reverse compensation operation with url: qsBusi+"/TransInCompensate" - Add(qsBusi+"/TransIn", qsBusi+"/TransInCompensate", req) - // submit the created saga transaction,dtm ensures all subtractions either complete or get revoked - err := saga.Submit() + saga := dtmcli.NewSaga(DtmServer, dtmcli.MustGenGid(DtmServer)). + // 添加一个TransOut的子事务,正向操作为url: qsBusi+"/TransOut", 补偿操作为url: qsBusi+"/TransOutCompensate" + Add(qsBusi+"/TransOut", qsBusi+"/TransOutCompensate", req). + // 添加一个TransIn的子事务,正向操作为url: qsBusi+"/TransIn", 补偿操作为url: qsBusi+"/TransInCompensate" + Add(qsBusi+"/TransIn", qsBusi+"/TransInCompensate", req) + // 提交saga事务,dtm会完成所有的子事务/回滚所有的子事务 + err := saga.Submit() ``` -### Complete example -Refer to [examples/quick_start.go](./examples/quick_start.go). +成功运行后,可以看到TransOut、TransIn依次被调用,完成了整个分布式事务 -### Slack +### 时序图 -You can join the [DTM slack channel here](https://join.slack.com/t/dtm-w6k9662/shared_invite/zt-vkrph4k1-eFqEFnMkbmlXqfUo5GWHWw). +上述saga分布式事务的时序图如下: -### Wechat + -Add wechat friend with id yedf2008, or scan the OR code. Fill in dtm as verification. +### 完整示例 +参考[examples/quick_start.go](./examples/quick_start.go) -![yedf2008](http://service.ivydad.com/cover/dubbingb6b5e2c0-2d2a-cd59-f7c5-c6b90aceb6f1.jpeg) +## 交流群 +请加 yedf2008 好友或者扫码加好友,验证回复 dtm 按照指引进群 -### Give a star! ⭐ +![yedf2008](http://service.ivydad.com/cover/dubbingb6b5e2c0-2d2a-cd59-f7c5-c6b90aceb6f1.jpeg) -If you think this project is good, or helpful to you, please give a star! +如果您觉得[dtm](https://github.com/yedf/dtm)不错,或者对您有帮助,请赏颗星吧! -### Who is using +## 谁在使用
- Ivydad - Eglass - Jiou - GoldenData + 常青藤爸爸 + 镜小二 + 极欧科技 + 金数智联
From c9aa583edfc0be606803c9b5a548002b8a662ace Mon Sep 17 00:00:00 2001 From: yedf2 <120050102@qq.com> Date: Tue, 12 Oct 2021 23:51:49 +0800 Subject: [PATCH 10/12] compose.yml use yedf/dtm --- Dockerfile | 6 ------ compose.yml | 6 ++++-- 2 files changed, 4 insertions(+), 8 deletions(-) delete mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 6082eb6..0000000 --- a/Dockerfile +++ /dev/null @@ -1,6 +0,0 @@ -FROM golang:1.15 -WORKDIR /app/dtm -RUN go env -w GO111MODULE=on -RUN go env -w GOPROXY=https://goproxy.io,direct -EXPOSE 8080 -CMD [ "/bin/bash", "-c", "go build app/main.go && /app/dtm/main dev"] diff --git a/compose.yml b/compose.yml index 7da7b86..cb9553d 100644 --- a/compose.yml +++ b/compose.yml @@ -1,13 +1,15 @@ version: '3.3' services: api: - build: . + image: 'yedf/dtm' environment: IS_DOCKER: '1' ports: - '8080:8080' volumes: - - .:/app/dtm + - .:/app/work + command: ['/app/dtm/main', 'dev'] + working_dir: /app/work mysql: image: 'mysql:5.7' environment: From b8f369c3248bf77bd878a276bebbfd1a6c3542c5 Mon Sep 17 00:00:00 2001 From: yedf2 <120050102@qq.com> Date: Fri, 15 Oct 2021 13:41:23 +0800 Subject: [PATCH 11/12] split db specific op to a stand alone file --- dtmcli/db_special.go | 70 +++++++++++++++++++++++++++++++++++++++ dtmcli/db_special_test.go | 27 +++++++++++++++ dtmcli/utils.go | 36 +------------------- dtmcli/utils_test.go | 11 ------ dtmcli/xa_base.go | 8 ++--- dtmsvr/cron.go | 7 +--- 6 files changed, 103 insertions(+), 56 deletions(-) create mode 100644 dtmcli/db_special.go create mode 100644 dtmcli/db_special_test.go diff --git a/dtmcli/db_special.go b/dtmcli/db_special.go new file mode 100644 index 0000000..5807d7a --- /dev/null +++ b/dtmcli/db_special.go @@ -0,0 +1,70 @@ +package dtmcli + +import ( + "errors" + "fmt" + "strings" +) + +// DBSpecial db specific operations +type DBSpecial interface { + TimestampAdd(second int) string + GetPlaceHoldSQL(sql string) string + GetXaSQL(command string, xid string) string +} + +type mysqlDBSpecial struct{} + +func (*mysqlDBSpecial) TimestampAdd(second int) string { + return fmt.Sprintf("date_add(now(), interval %d second)", second) +} + +func (*mysqlDBSpecial) GetPlaceHoldSQL(sql string) string { + return sql +} + +func (*mysqlDBSpecial) GetXaSQL(command string, xid string) string { + return fmt.Sprintf("xa %s '%s'", command, xid) +} + +type postgresDBSpecial struct{} + +func (*postgresDBSpecial) TimestampAdd(second int) string { + return fmt.Sprintf("current_timestamp + interval '%d second'", second) +} + +func (*postgresDBSpecial) GetXaSQL(command string, xid string) string { + return map[string]string{ + "end": "", + "start": "begin", + "prepare": fmt.Sprintf("prepare transaction '%s'", xid), + "commit": fmt.Sprintf("commit prepared '%s'", xid), + "rollback": fmt.Sprintf("rollback prepared '%s'", xid), + }[command] +} + +func (*postgresDBSpecial) GetPlaceHoldSQL(sql string) string { + pos := 1 + parts := []string{} + b := 0 + for i := 0; i < len(sql); i++ { + if sql[i] == '?' { + parts = append(parts, sql[b:i]) + b = i + 1 + parts = append(parts, fmt.Sprintf("$%d", pos)) + pos++ + } + } + parts = append(parts, sql[b:]) + return strings.Join(parts, "") +} + +// GetDBSpecial get DBSpecial for DBDriver +func GetDBSpecial() DBSpecial { + if DBDriver == DriverMysql { + return &mysqlDBSpecial{} + } else if DBDriver == DriverPostgres { + return &postgresDBSpecial{} + } + panic(errors.New("unknown DBDriver, please set it to a valid driver: " + DBDriver)) +} diff --git a/dtmcli/db_special_test.go b/dtmcli/db_special_test.go new file mode 100644 index 0000000..4ed5607 --- /dev/null +++ b/dtmcli/db_special_test.go @@ -0,0 +1,27 @@ +package dtmcli + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDBSpecial(t *testing.T) { + old := DBDriver + DBDriver = "no-driver" + assert.Error(t, CatchP(func() { + GetDBSpecial() + })) + DBDriver = DriverMysql + sp := GetDBSpecial() + + assert.Equal(t, "? ?", sp.GetPlaceHoldSQL("? ?")) + assert.Equal(t, "xa start 'xa1'", sp.GetXaSQL("start", "xa1")) + assert.Equal(t, "date_add(now(), interval 1000 second)", sp.TimestampAdd(1000)) + DBDriver = DriverPostgres + sp = GetDBSpecial() + assert.Equal(t, "$1 $2", sp.GetPlaceHoldSQL("? ?")) + assert.Equal(t, "begin", sp.GetXaSQL("start", "xa1")) + assert.Equal(t, "current_timestamp + interval '1000 second'", sp.TimestampAdd(1000)) + DBDriver = old +} diff --git a/dtmcli/utils.go b/dtmcli/utils.go index 3b800a1..8fec13e 100644 --- a/dtmcli/utils.go +++ b/dtmcli/utils.go @@ -215,7 +215,7 @@ func DBExec(db DB, sql string, values ...interface{}) (affected int64, rerr erro if sql == "" { return 0, nil } - sql = makeSQLCompatible(sql) + sql = GetDBSpecial().GetPlaceHoldSQL(sql) r, rerr := db.Exec(sql, values...) if rerr == nil { affected, rerr = r.RowsAffected() @@ -268,37 +268,3 @@ func CheckResult(res interface{}, err error) error { } return err } - -func makeSQLCompatible(sql string) string { - if DBDriver == DriverPostgres { - pos := 1 - parts := []string{} - b := 0 - for i := 0; i < len(sql); i++ { - if sql[i] == '?' { - parts = append(parts, sql[b:i]) - b = i + 1 - parts = append(parts, fmt.Sprintf("$%d", pos)) - pos++ - } - } - parts = append(parts, sql[b:]) - return strings.Join(parts, "") - } - PanicIf(DBDriver != DriverMysql, fmt.Errorf("unkown db driver: %s", DBDriver)) - return sql -} - -func getXaSQL(action string, xid string) string { - if DBDriver == DriverPostgres { - return map[string]string{ - "end": "", - "start": "begin", - "prepare": fmt.Sprintf("prepare transaction '%s'", xid), - "commit": fmt.Sprintf("commit prepared '%s'", xid), - "rollback": fmt.Sprintf("rollback prepared '%s'", xid), - }[action] - } - PanicIf(DBDriver != DriverMysql, fmt.Errorf("unkown db driver: %s", DBDriver)) - return fmt.Sprintf("xa %s '%s'", action, xid) -} diff --git a/dtmcli/utils_test.go b/dtmcli/utils_test.go index 364283e..a59d628 100644 --- a/dtmcli/utils_test.go +++ b/dtmcli/utils_test.go @@ -89,14 +89,3 @@ func TestFatal(t *testing.T) { }) assert.Error(t, err, fmt.Errorf("fatal")) } - -func TestCompatible(t *testing.T) { - old := DBDriver - DBDriver = DriverMysql - assert.Equal(t, "? ?", makeSQLCompatible("? ?")) - assert.Equal(t, "xa start 'xa1'", getXaSQL("start", "xa1")) - DBDriver = DriverPostgres - assert.Equal(t, "$1 $2", makeSQLCompatible("? ?")) - assert.Equal(t, "begin", getXaSQL("start", "xa1")) - DBDriver = old -} diff --git a/dtmcli/xa_base.go b/dtmcli/xa_base.go index e1e31f8..a8c3ceb 100644 --- a/dtmcli/xa_base.go +++ b/dtmcli/xa_base.go @@ -20,7 +20,7 @@ func (xc *XaClientBase) HandleCallback(gid string, branchID string, action strin } defer db.Close() xaID := gid + "-" + branchID - _, err = DBExec(db, getXaSQL(action, xaID)) + _, err = DBExec(db, GetDBSpecial().GetXaSQL(action, xaID)) if err != nil && (strings.Contains(err.Error(), "Error 1397: XAER_NOTA") || strings.Contains(err.Error(), "does not exist")) { // 重复commit/rollback同一个id,报这个错误,忽略 err = nil @@ -39,9 +39,9 @@ func (xc *XaClientBase) HandleLocalTrans(xa *TransBase, cb func(*sql.DB) (interf defer func() { db.Close() }() defer func() { x := recover() - _, err := DBExec(db, getXaSQL("end", xaBranch)) + _, err := DBExec(db, GetDBSpecial().GetXaSQL("end", xaBranch)) if x == nil && rerr == nil && err == nil { - _, err = DBExec(db, getXaSQL("prepare", xaBranch)) + _, err = DBExec(db, GetDBSpecial().GetXaSQL("prepare", xaBranch)) } if rerr == nil { rerr = err @@ -50,7 +50,7 @@ func (xc *XaClientBase) HandleLocalTrans(xa *TransBase, cb func(*sql.DB) (interf panic(x) } }() - _, rerr = DBExec(db, getXaSQL("start", xaBranch)) + _, rerr = DBExec(db, GetDBSpecial().GetXaSQL("start", xaBranch)) if rerr != nil { return } diff --git a/dtmsvr/cron.go b/dtmsvr/cron.go index f301754..86cd0aa 100644 --- a/dtmsvr/cron.go +++ b/dtmsvr/cron.go @@ -41,12 +41,7 @@ func lockOneTrans(expireIn time.Duration) *TransGlobal { trans := TransGlobal{} owner := GenGid() db := dbGet() - getTime := func(second int) string { - return fmt.Sprintf(map[string]string{ - "mysql": "date_add(now(), interval %d second)", - "postgres": "current_timestamp + interval '%d second'", - }[dtmcli.DBDriver], second) - } + getTime := dtmcli.GetDBSpecial().TimestampAdd expire := int(expireIn / time.Second) whereTime := fmt.Sprintf("next_cron_time < %s and next_cron_time > %s and update_time < %s", getTime(expire), getTime(-3600), getTime(expire-3)) // 这里next_cron_time需要限定范围,否则数据量累计之后,会导致查询变慢 From be36d4b30eb872e26fb1739b9a1c1380cdc80bd6 Mon Sep 17 00:00:00 2001 From: yedf2 <120050102@qq.com> Date: Fri, 15 Oct 2021 16:34:55 +0800 Subject: [PATCH 12/12] refactor postgres support --- app/main.go | 2 +- common/types.go | 4 +-- dtmcli/barrier.go | 5 +--- dtmcli/consts.go | 11 +++----- dtmcli/db_special.go | 41 +++++++++++++++++++++++------ dtmcli/db_special_test.go | 11 ++++---- examples/base_http.go | 4 +-- helper/sync-dtmcli.sh | 54 +++++++++++++++++++++++++++++++++++++++ test/main_test.go | 2 +- 9 files changed, 103 insertions(+), 31 deletions(-) create mode 100644 helper/sync-dtmcli.sh diff --git a/app/main.go b/app/main.go index c63dcd8..e9f4094 100644 --- a/app/main.go +++ b/app/main.go @@ -27,7 +27,7 @@ Available commands: ` func main() { - dtmcli.DBDriver = common.DtmConfig.DB["driver"] + dtmcli.SetCurrentDBType(common.DtmConfig.DB["driver"]) if len(os.Args) == 1 { fmt.Println(usage) for name := range examples.Samples { diff --git a/common/types.go b/common/types.go index 90573d4..5ada90b 100644 --- a/common/types.go +++ b/common/types.go @@ -28,10 +28,10 @@ type ModelBase struct { } func getGormDialetor(driver string, dsn string) gorm.Dialector { - if driver == dtmcli.DriverPostgres { + if driver == dtmcli.DBTypePostgres { return postgres.Open(dsn) } - dtmcli.PanicIf(driver != dtmcli.DriverMysql, fmt.Errorf("unkown driver: %s", driver)) + dtmcli.PanicIf(driver != dtmcli.DBTypeMysql, fmt.Errorf("unkown driver: %s", driver)) return mysql.Open(dsn) } diff --git a/dtmcli/barrier.go b/dtmcli/barrier.go index 59b6582..dbc47f5 100644 --- a/dtmcli/barrier.go +++ b/dtmcli/barrier.go @@ -44,10 +44,7 @@ func insertBarrier(tx Tx, transType string, gid string, branchID string, branchT if branchType == "" { return 0, nil } - sql := map[string]string{ - "mysql": "insert ignore into dtm_barrier.barrier(trans_type, gid, branch_id, branch_type, barrier_id, reason) values(?,?,?,?,?,?)", - "postgres": "insert into dtm_barrier.barrier(trans_type, gid, branch_id, branch_type, barrier_id, reason) values(?,?,?,?,?,?) on conflict ON CONSTRAINT uniq_barrier do nothing", - }[DBDriver] + sql := GetDBSpecial().GetInsertIgnoreTemplate("dtm_barrier.barrier(trans_type, gid, branch_id, branch_type, barrier_id, reason) values(?,?,?,?,?,?)", "uniq_barrier") return DBExec(tx, sql, transType, gid, branchID, branchType, barrierID, reason) } diff --git a/dtmcli/consts.go b/dtmcli/consts.go index 78aead8..8d2b1d7 100644 --- a/dtmcli/consts.go +++ b/dtmcli/consts.go @@ -30,11 +30,8 @@ const ( // ResultFailure for result of a trans/trans branch ResultFailure = "FAILURE" - // DriverMysql const for driver mysql - DriverMysql = "mysql" - // DriverPostgres const for driver postgres - DriverPostgres = "postgres" + // DBTypeMysql const for driver mysql + DBTypeMysql = "mysql" + // DBTypePostgres const for driver postgres + DBTypePostgres = "postgres" ) - -// DBDriver dtm和dtmcli可以支持mysql和postgres,但不支持混合,通过全局变量指定当前要支持的驱动 -var DBDriver = DriverMysql diff --git a/dtmcli/db_special.go b/dtmcli/db_special.go index 5807d7a..12769eb 100644 --- a/dtmcli/db_special.go +++ b/dtmcli/db_special.go @@ -1,7 +1,6 @@ package dtmcli import ( - "errors" "fmt" "strings" ) @@ -10,9 +9,13 @@ import ( type DBSpecial interface { TimestampAdd(second int) string GetPlaceHoldSQL(sql string) string + GetInsertIgnoreTemplate(tableAndValues string, pgConstraint string) string GetXaSQL(command string, xid string) string } +var dbSpecials = map[string]DBSpecial{} +var currentDBType = DBTypeMysql + type mysqlDBSpecial struct{} func (*mysqlDBSpecial) TimestampAdd(second int) string { @@ -27,6 +30,14 @@ func (*mysqlDBSpecial) GetXaSQL(command string, xid string) string { return fmt.Sprintf("xa %s '%s'", command, xid) } +func (*mysqlDBSpecial) GetInsertIgnoreTemplate(tableAndValues string, pgConstraint string) string { + return fmt.Sprintf("insert ignore into %s", tableAndValues) +} + +func init() { + dbSpecials[DBTypeMysql] = &mysqlDBSpecial{} +} + type postgresDBSpecial struct{} func (*postgresDBSpecial) TimestampAdd(second int) string { @@ -59,12 +70,26 @@ func (*postgresDBSpecial) GetPlaceHoldSQL(sql string) string { return strings.Join(parts, "") } -// GetDBSpecial get DBSpecial for DBDriver +func (*postgresDBSpecial) GetInsertIgnoreTemplate(tableAndValues string, pgConstraint string) string { + return fmt.Sprintf("insert into %s on conflict ON CONSTRAINT %s do nothing", tableAndValues, pgConstraint) +} +func init() { + dbSpecials[DBTypePostgres] = &postgresDBSpecial{} +} + +// GetDBSpecial get DBSpecial for currentDBType func GetDBSpecial() DBSpecial { - if DBDriver == DriverMysql { - return &mysqlDBSpecial{} - } else if DBDriver == DriverPostgres { - return &postgresDBSpecial{} - } - panic(errors.New("unknown DBDriver, please set it to a valid driver: " + DBDriver)) + return dbSpecials[currentDBType] +} + +// SetCurrentDBType set currentDBType +func SetCurrentDBType(dbType string) { + spec := dbSpecials[dbType] + PanicIf(spec == nil, fmt.Errorf("unknown db type %s", dbType)) + currentDBType = dbType +} + +// GetCurrentDBType get currentDBType +func GetCurrentDBType() string { + return currentDBType } diff --git a/dtmcli/db_special_test.go b/dtmcli/db_special_test.go index 4ed5607..586f64c 100644 --- a/dtmcli/db_special_test.go +++ b/dtmcli/db_special_test.go @@ -7,21 +7,20 @@ import ( ) func TestDBSpecial(t *testing.T) { - old := DBDriver - DBDriver = "no-driver" + old := currentDBType assert.Error(t, CatchP(func() { - GetDBSpecial() + SetCurrentDBType("no-driver") })) - DBDriver = DriverMysql + SetCurrentDBType(DBTypeMysql) sp := GetDBSpecial() assert.Equal(t, "? ?", sp.GetPlaceHoldSQL("? ?")) assert.Equal(t, "xa start 'xa1'", sp.GetXaSQL("start", "xa1")) assert.Equal(t, "date_add(now(), interval 1000 second)", sp.TimestampAdd(1000)) - DBDriver = DriverPostgres + SetCurrentDBType(DBTypePostgres) sp = GetDBSpecial() assert.Equal(t, "$1 $2", sp.GetPlaceHoldSQL("? ?")) assert.Equal(t, "begin", sp.GetXaSQL("start", "xa1")) assert.Equal(t, "current_timestamp + interval '1000 second'", sp.TimestampAdd(1000)) - DBDriver = old + SetCurrentDBType(old) } diff --git a/examples/base_http.go b/examples/base_http.go index 68e14cc..049119a 100644 --- a/examples/base_http.go +++ b/examples/base_http.go @@ -131,9 +131,9 @@ func BaseAddRoute(app *gin.Engine) { return dtmcli.MapFailure, nil } var dia gorm.Dialector = nil - if dtmcli.DBDriver == dtmcli.DriverMysql { + if dtmcli.GetCurrentDBType() == dtmcli.DBTypeMysql { dia = mysql.New(mysql.Config{Conn: db}) - } else if dtmcli.DBDriver == dtmcli.DriverPostgres { + } else if dtmcli.GetCurrentDBType() == dtmcli.DBTypePostgres { dia = postgres.New(postgres.Config{Conn: db}) } gdb, err := gorm.Open(dia, &gorm.Config{}) diff --git a/helper/sync-dtmcli.sh b/helper/sync-dtmcli.sh new file mode 100644 index 0000000..2fabffd --- /dev/null +++ b/helper/sync-dtmcli.sh @@ -0,0 +1,54 @@ +#! /bin/bash +set -x +if [ x$1 == x ]; then + echo please specify you version like vx.x.x; + exit 1; +fi + +if [ ${1:1:1} != v ]; then + echo please specify you version like vx.x.x; + exit 1; +fi + +cd ../dtmcli +cp ../dtm/dtmcli/*.go ./ +rm -f *_test.go +go mod tidy +go build || exit 1 + +git add . +git commit -m'update from dtm' +git push +# git tag $1 +# git push --tags + +cd ../dtmcli-go-sample +go get -u github.com/yedf/dtmcli +go mod tidy +go build || exit 1 +git add . +git commit -m'update from dtm' +git push + +cd ../dtmgrpc +cp ../dtm/dtmgrpc/*.go ./ +cp ../dtm/dtmgrpc/*.proto ./ + +sed -i '' -e 's/yedf\/dtm\//yedf\//g' *.go *.proto +rm -rf *_test.go +go get -u github.com/yedf/dtmcli +go mod tidy +go build || exit 1 +git add . +git commit -m'update from dtm' +git push +# git tag $1 +# git push --tags + +cd ../dtmgrpc-go-sample +go get -u github.com/yedf/dtmcli +go get -u github.com/yedf/dtmgrpc +go build || exit 1 +git add . +git commit -m'update from dtm' +git push \ No newline at end of file diff --git a/test/main_test.go b/test/main_test.go index 0eebb53..5b472fe 100644 --- a/test/main_test.go +++ b/test/main_test.go @@ -12,7 +12,7 @@ import ( ) func TestMain(m *testing.M) { - dtmcli.DBDriver = common.DtmConfig.DB["driver"] + dtmcli.SetCurrentDBType(common.DtmConfig.DB["driver"]) dtmsvr.TransProcessedTestChan = make(chan string, 1) dtmsvr.CronForwardDuration = 60 * time.Second common.DtmConfig.UpdateBranchSync = 1