dtm/dtmcli/tcc.go
2021-07-25 16:24:45 +08:00

107 lines
2.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package dtmcli
import (
"fmt"
"strings"
"github.com/gin-gonic/gin"
"github.com/go-resty/resty/v2"
"github.com/sirupsen/logrus"
"github.com/yedf/dtm/common"
)
// Tcc struct of tcc
type Tcc struct {
IDGenerator
Dtm string
Gid string
}
// TccGlobalFunc type of global tcc call
type TccGlobalFunc func(tcc *Tcc) error
// TccGlobalTransaction begin a tcc global transaction
// dtm dtm服务器地址
// gid 全局事务id
// tccFunc tcc事务函数里面会定义全局事务的分支
func TccGlobalTransaction(dtm string, gid string, tccFunc TccGlobalFunc) (rerr error) {
data := &M{
"gid": gid,
"trans_type": "tcc",
}
defer func() {
var resp *resty.Response
var err error
var x interface{}
if x = recover(); x != nil || rerr != nil {
resp, err = common.RestyClient.R().SetBody(data).Post(dtm + "/abort")
} else {
resp, err = common.RestyClient.R().SetBody(data).Post(dtm + "/submit")
}
err2 := CheckDtmResponse(resp, err)
if err2 != nil {
logrus.Errorf("submitting or abort global transaction error: %v", err2)
}
if x != nil {
panic(x)
}
}()
tcc := &Tcc{Dtm: dtm, Gid: gid}
resp, err := common.RestyClient.R().SetBody(data).Post(tcc.Dtm + "/prepare")
rerr = CheckDtmResponse(resp, err)
if rerr != nil {
return
}
rerr = tccFunc(tcc)
return
}
// TccFromReq tcc from request info
func TccFromReq(c *gin.Context) (*Tcc, error) {
tcc := &Tcc{
Dtm: c.Query("dtm"),
Gid: c.Query("gid"),
IDGenerator: IDGenerator{parentID: c.Query("branch_id")},
}
if tcc.Dtm == "" || tcc.Gid == "" {
return nil, fmt.Errorf("bad tcc info. dtm: %s, gid: %s", tcc.Dtm, tcc.Gid)
}
return tcc, nil
}
// CallBranch call a tcc branch
// 函数首先注册子事务的所有分支成功后调用try分支返回try分支的调用结果
func (t *Tcc) CallBranch(body interface{}, tryURL string, confirmURL string, cancelURL string) (*resty.Response, error) {
branchID := t.NewBranchID()
resp, err := common.RestyClient.R().
SetBody(&M{
"gid": t.Gid,
"branch_id": branchID,
"trans_type": "tcc",
"status": "prepared",
"data": string(common.MustMarshal(body)),
"try": tryURL,
"confirm": confirmURL,
"cancel": cancelURL,
}).
Post(t.Dtm + "/registerTccBranch")
err = CheckDtmResponse(resp, err)
if err != nil {
return resp, err
}
resp, err = common.RestyClient.R().
SetBody(body).
SetQueryParams(common.MS{
"dtm": t.Dtm,
"gid": t.Gid,
"branch_id": branchID,
"trans_type": "tcc",
"branch_type": "try",
}).
Post(tryURL)
if err == nil && strings.Contains(resp.String(), "FAILURE") {
err = fmt.Errorf("branch return failure: %s", resp.String())
}
return resp, err
}