|
- package tm
-
- import (
- "fmt"
- )
-
- import (
- "github.com/pkg/errors"
- )
-
- import (
- "github.com/transaction-wg/seata-golang/pkg/base/meta"
- "github.com/transaction-wg/seata-golang/pkg/client/config"
- context2 "github.com/transaction-wg/seata-golang/pkg/client/context"
- "github.com/transaction-wg/seata-golang/pkg/client/rpc_client"
- "github.com/transaction-wg/seata-golang/pkg/util/log"
- )
-
- const (
- DEFAULT_GLOBAL_TX_TIMEOUT = 60000
- DEFAULT_GLOBAL_TX_NAME = "default"
- )
-
- type SuspendedResourcesHolder struct {
- Xid string
- }
-
- type GlobalTransaction interface {
- Begin(ctx *context2.RootContext) error
- BeginWithTimeout(timeout int32, ctx *context2.RootContext) error
- BeginWithTimeoutAndName(timeout int32, name string, ctx *context2.RootContext) error
- Commit(ctx *context2.RootContext) error
- Rollback(ctx *context2.RootContext) error
- Suspend(unbindXid bool, ctx *context2.RootContext) (*SuspendedResourcesHolder, error)
- Resume(suspendedResourcesHolder *SuspendedResourcesHolder, ctx *context2.RootContext) error
- GetStatus(ctx *context2.RootContext) (meta.GlobalStatus, error)
- GetXid(ctx *context2.RootContext) string
- GlobalReport(globalStatus meta.GlobalStatus, ctx *context2.RootContext) error
- GetLocalStatus() meta.GlobalStatus
- }
-
- type GlobalTransactionRole byte
-
- const (
- // The Launcher. The one begins the current global transaction.
- Launcher GlobalTransactionRole = iota
-
- // The Participant. The one just joins into a existing global transaction.
- Participant
- )
-
- func (role GlobalTransactionRole) String() string {
- switch role {
- case Launcher:
- return "Launcher"
- case Participant:
- return "Participant"
- default:
- return fmt.Sprintf("%d", role)
- }
- }
-
- type DefaultGlobalTransaction struct {
- conf config.TMConfig
- Xid string
- Status meta.GlobalStatus
- Role GlobalTransactionRole
- transactionManager TransactionManager
- }
-
- func (gtx *DefaultGlobalTransaction) Begin(ctx *context2.RootContext) error {
- return gtx.BeginWithTimeout(DEFAULT_GLOBAL_TX_TIMEOUT, ctx)
- }
-
- func (gtx *DefaultGlobalTransaction) BeginWithTimeout(timeout int32, ctx *context2.RootContext) error {
- return gtx.BeginWithTimeoutAndName(timeout, DEFAULT_GLOBAL_TX_NAME, ctx)
- }
-
- func (gtx *DefaultGlobalTransaction) BeginWithTimeoutAndName(timeout int32, name string, ctx *context2.RootContext) error {
- if gtx.Role != Launcher {
- if gtx.Xid == "" {
- return errors.New("xid should not be empty")
- }
- log.Debugf("Ignore Begin(): just involved in global transaction [XID: %s]", gtx.Xid)
- return nil
- }
- if gtx.Xid != "" {
- return errors.New("xid should be empty")
- }
- if ctx.InGlobalTransaction() {
- return errors.New("xid should be empty")
- }
- xid, err := gtx.transactionManager.Begin("", "", name, timeout)
- if err != nil {
- return errors.WithStack(err)
- }
- gtx.Xid = xid
- gtx.Status = meta.GlobalStatusBegin
- ctx.Bind(xid)
- log.Infof("Begin new global transaction [XID: %s]", xid)
- return nil
- }
-
- func (gtx *DefaultGlobalTransaction) Commit(ctx *context2.RootContext) error {
- defer func() {
- ctxXid := ctx.GetXID()
- if ctxXid != "" && gtx.Xid == ctxXid {
- ctx.Unbind()
- }
- }()
- if gtx.Role == Participant {
- log.Debugf("Ignore Commit(): just involved in global transaction [XID: %s]", gtx.Xid)
- return nil
- }
- if gtx.Xid == "" {
- return errors.New("xid should not be empty")
- }
- retry := gtx.conf.CommitRetryCount
- for retry > 0 {
- status, err := gtx.transactionManager.Commit(gtx.Xid)
- if err != nil {
- log.Errorf("Failed to report global commit [XID: %s],Retry Countdown: %d, reason: %s", gtx.Xid, retry, err.Error())
- } else {
- gtx.Status = status
- break
- }
- retry--
- if retry == 0 {
- return errors.New("Failed to report global commit")
- }
- }
- log.Infof("[XID: %s] commit status: %s", gtx.Xid, gtx.Status.String())
- return nil
- }
-
- func (gtx *DefaultGlobalTransaction) Rollback(ctx *context2.RootContext) error {
- defer func() {
- ctxXid := ctx.GetXID()
- if ctxXid != "" && gtx.Xid == ctxXid {
- ctx.Unbind()
- }
- }()
- if gtx.Role == Participant {
- log.Debugf("Ignore Rollback(): just involved in global transaction [XID: %s]", gtx.Xid)
- return nil
- }
- if gtx.Xid == "" {
- return errors.New("xid should not be empty")
- }
- retry := gtx.conf.RollbackRetryCount
- for retry > 0 {
- status, err := gtx.transactionManager.Rollback(gtx.Xid)
- if err != nil {
- log.Errorf("Failed to report global rollback [XID: %s],Retry Countdown: %d, reason: %s", gtx.Xid, retry, err.Error())
- } else {
- gtx.Status = status
- break
- }
- retry--
- if retry == 0 {
- return errors.New("Failed to report global rollback")
- }
- }
- log.Infof("[XID: %s] rollback status: %s", gtx.Xid, gtx.Status.String())
- return nil
- }
-
- func (gtx *DefaultGlobalTransaction) Suspend(unbindXid bool, ctx *context2.RootContext) (*SuspendedResourcesHolder, error) {
- xid := ctx.GetXID()
- if xid != "" && unbindXid {
- ctx.Unbind()
- log.Debugf("Suspending current transaction,xid = %s", xid)
- }
- return &SuspendedResourcesHolder{Xid: xid}, nil
- }
-
- func (gtx *DefaultGlobalTransaction) Resume(suspendedResourcesHolder *SuspendedResourcesHolder, ctx *context2.RootContext) error {
- if suspendedResourcesHolder == nil {
- return nil
- }
- xid := suspendedResourcesHolder.Xid
- if xid != "" {
- ctx.Bind(xid)
- log.Debugf("Resumimg the transaction,xid = %s", xid)
- }
- return nil
- }
-
- func (gtx *DefaultGlobalTransaction) GetStatus(ctx *context2.RootContext) (meta.GlobalStatus, error) {
- if gtx.Xid == "" {
- return meta.GlobalStatusUnknown, nil
- }
- status, err := gtx.transactionManager.GetStatus(gtx.Xid)
- if err != nil {
- return 0, errors.WithStack(err)
- }
-
- gtx.Status = status
- return gtx.Status, nil
- }
-
- func (gtx *DefaultGlobalTransaction) GetXid(ctx *context2.RootContext) string {
- return gtx.Xid
- }
-
- func (gtx *DefaultGlobalTransaction) GlobalReport(globalStatus meta.GlobalStatus, ctx *context2.RootContext) error {
- defer func() {
- ctxXid := ctx.GetXID()
- if ctxXid != "" && gtx.Xid == ctxXid {
- ctx.Unbind()
- }
- }()
-
- if gtx.Xid == "" {
- return errors.New("xid should not be empty")
- }
-
- if globalStatus == 0 {
- return errors.New("globalStatus should not be zero")
- }
-
- status, err := gtx.transactionManager.GlobalReport(gtx.Xid, globalStatus)
- if err != nil {
- return errors.WithStack(err)
- }
-
- gtx.Status = status
- log.Infof("[XID: %s] report status: %s", gtx.Xid, gtx.Status.String())
- return nil
- }
-
- func (gtx *DefaultGlobalTransaction) GetLocalStatus() meta.GlobalStatus {
- return gtx.Status
- }
-
- func CreateNew() *DefaultGlobalTransaction {
- return &DefaultGlobalTransaction{
- conf: config.GetTMConfig(),
- Xid: "",
- Status: meta.GlobalStatusUnknown,
- Role: Launcher,
- transactionManager: &DefaultTransactionManager{rpcClient: rpc_client.GetRpcRemoteClient()},
- }
- }
-
- func GetCurrent(ctx *context2.RootContext) *DefaultGlobalTransaction {
- xid := ctx.GetXID()
- if xid == "" {
- return nil
- }
- return &DefaultGlobalTransaction{
- conf: config.GetTMConfig(),
- Xid: xid,
- Status: meta.GlobalStatusBegin,
- Role: Participant,
- transactionManager: &DefaultTransactionManager{rpcClient: rpc_client.GetRpcRemoteClient()},
- }
- }
-
- func GetCurrentOrCreate(ctx *context2.RootContext) *DefaultGlobalTransaction {
- tx := GetCurrent(ctx)
- if tx == nil {
- return CreateNew()
- }
- return tx
- }
|