@@ -1,8 +0,0 @@ | |||||
# Default ignored files | |||||
/shelf/ | |||||
/workspace.xml | |||||
# Editor-based HTTP Client requests | |||||
/httpRequests/ | |||||
# Datasource local storage ignored files | |||||
/dataSources/ | |||||
/dataSources.local.xml |
@@ -1,15 +0,0 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<project version="4"> | |||||
<component name="GitToolBoxProjectSettings"> | |||||
<option name="commitMessageIssueKeyValidationOverride"> | |||||
<BoolValueOverride> | |||||
<option name="enabled" value="true" /> | |||||
</BoolValueOverride> | |||||
</option> | |||||
<option name="commitMessageValidationConfigOverride"> | |||||
<CommitMessageValidationOverride> | |||||
<option name="enabled" value="true" /> | |||||
</CommitMessageValidationOverride> | |||||
</option> | |||||
</component> | |||||
</project> |
@@ -1,8 +0,0 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<project version="4"> | |||||
<component name="ProjectModuleManager"> | |||||
<modules> | |||||
<module fileurl="file://$PROJECT_DIR$/.idea/seata-go.iml" filepath="$PROJECT_DIR$/.idea/seata-go.iml" /> | |||||
</modules> | |||||
</component> | |||||
</project> |
@@ -1,9 +0,0 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<module type="WEB_MODULE" version="4"> | |||||
<component name="Go" enabled="true" /> | |||||
<component name="NewModuleRootManager"> | |||||
<content url="file://$MODULE_DIR$" /> | |||||
<orderEntry type="inheritedJdk" /> | |||||
<orderEntry type="sourceFolder" forTests="false" /> | |||||
</component> | |||||
</module> |
@@ -1,6 +0,0 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<project version="4"> | |||||
<component name="VcsDirectoryMappings"> | |||||
<mapping directory="$PROJECT_DIR$" vcs="Git" /> | |||||
</component> | |||||
</project> |
@@ -6,7 +6,6 @@ require ( | |||||
github.com/BurntSushi/toml v1.1.0 // indirect | github.com/BurntSushi/toml v1.1.0 // indirect | ||||
github.com/apache/dubbo-getty v1.4.8 | github.com/apache/dubbo-getty v1.4.8 | ||||
github.com/dubbogo/gost v1.11.23 | github.com/dubbogo/gost v1.11.23 | ||||
github.com/dubbogo/tools v1.0.9 // indirect | |||||
github.com/golang/snappy v0.0.4 // indirect | github.com/golang/snappy v0.0.4 // indirect | ||||
github.com/natefinch/lumberjack v2.0.0+incompatible | github.com/natefinch/lumberjack v2.0.0+incompatible | ||||
github.com/pkg/errors v0.9.1 | github.com/pkg/errors v0.9.1 | ||||
@@ -1,5 +0,0 @@ | |||||
package common | |||||
const ( | |||||
XID = "xid" | |||||
) |
@@ -0,0 +1,10 @@ | |||||
package common | |||||
const ( | |||||
StartTime = "action-start-time" | |||||
HostName = "host-name" | |||||
TccBusinessActionContext = "tcc-business-action-context" | |||||
CONTEXT_VARIABLE = "contextVariable" | |||||
XID = "xid" | |||||
) |
@@ -0,0 +1,78 @@ | |||||
package model | |||||
import ( | |||||
"context" | |||||
"github.com/seata/seata-go/pkg/common" | |||||
"github.com/seata/seata-go/pkg/rm/tcc/api" | |||||
) | |||||
type ContextVariable struct { | |||||
Xid string | |||||
Status *GlobalStatus | |||||
Role *GlobalTransactionRole | |||||
BusinessActionContext *api.BusinessActionContext | |||||
} | |||||
func InitSeataContext(ctx context.Context) context.Context { | |||||
return context.WithValue(ctx, common.CONTEXT_VARIABLE, &ContextVariable{}) | |||||
} | |||||
func IsSeataContext(ctx context.Context) bool { | |||||
return ctx.Value(common.CONTEXT_VARIABLE) != nil | |||||
} | |||||
func GetBusinessActionContext(ctx context.Context) *api.BusinessActionContext { | |||||
variable := ctx.Value(common.TccBusinessActionContext) | |||||
if variable == nil { | |||||
return nil | |||||
} | |||||
return variable.(*api.BusinessActionContext) | |||||
} | |||||
func SetBusinessActionContext(ctx context.Context, businessActionContext *api.BusinessActionContext) { | |||||
variable := ctx.Value(common.TccBusinessActionContext) | |||||
if variable != nil { | |||||
variable.(*ContextVariable).BusinessActionContext = businessActionContext | |||||
} | |||||
} | |||||
func GetTransactionRole(ctx context.Context) *GlobalTransactionRole { | |||||
variable := ctx.Value(common.CONTEXT_VARIABLE) | |||||
if variable == nil { | |||||
return nil | |||||
} | |||||
return variable.(*ContextVariable).Role | |||||
} | |||||
func SetTransactionRole(ctx context.Context, role GlobalTransactionRole) { | |||||
variable := ctx.Value(common.CONTEXT_VARIABLE) | |||||
if variable != nil { | |||||
variable.(*ContextVariable).Role = &role | |||||
} | |||||
} | |||||
func GetXID(ctx context.Context) string { | |||||
variable := ctx.Value(common.CONTEXT_VARIABLE) | |||||
if variable == nil { | |||||
return "" | |||||
} | |||||
return variable.(*ContextVariable).Xid | |||||
} | |||||
func HasXID(ctx context.Context) bool { | |||||
return GetXID(ctx) != "" | |||||
} | |||||
func SetXID(ctx context.Context, xid string) { | |||||
variable := ctx.Value(common.CONTEXT_VARIABLE) | |||||
if variable != nil { | |||||
variable.(*ContextVariable).Xid = xid | |||||
} | |||||
} | |||||
func UnbindXid(ctx context.Context) { | |||||
variable := ctx.Value(common.CONTEXT_VARIABLE) | |||||
if variable != nil { | |||||
variable.(*ContextVariable).Xid = "" | |||||
} | |||||
} |
@@ -1,25 +1,26 @@ | |||||
package model | package model | ||||
import ( | import ( | ||||
"context" | |||||
"sync" | "sync" | ||||
) | ) | ||||
// Control a branch transaction commit or rollback | // Control a branch transaction commit or rollback | ||||
type ResourceManagerInbound interface { | type ResourceManagerInbound interface { | ||||
// Commit a branch transaction | // Commit a branch transaction | ||||
BranchCommit(branchType BranchType, xid string, branchId int64, resourceId string, applicationData []byte) (BranchStatus, error) | |||||
BranchCommit(ctx context.Context, branchType BranchType, xid string, branchId int64, resourceId string, applicationData []byte) (BranchStatus, error) | |||||
// Rollback a branch transaction | // Rollback a branch transaction | ||||
BranchRollback(branchType BranchType, xid string, branchId int64, resourceId string, applicationData []byte) (BranchStatus, error) | |||||
BranchRollback(ctx context.Context, ranchType BranchType, xid string, branchId int64, resourceId string, applicationData []byte) (BranchStatus, error) | |||||
} | } | ||||
// Resource Manager: send outbound request to TC | // Resource Manager: send outbound request to TC | ||||
type ResourceManagerOutbound interface { | type ResourceManagerOutbound interface { | ||||
// Branch register long | // Branch register long | ||||
BranchRegister(branchType BranchType, resourceId, clientId, xid, applicationData, lockKeys string) (int64, error) | |||||
BranchRegister(ctx context.Context, ranchType BranchType, resourceId, clientId, xid, applicationData, lockKeys string) (int64, error) | |||||
// Branch report | // Branch report | ||||
BranchReport(branchType BranchType, xid string, branchId int64, status BranchStatus, applicationData string) error | |||||
BranchReport(ctx context.Context, ranchType BranchType, xid string, branchId int64, status BranchStatus, applicationData string) error | |||||
// Lock query boolean | // Lock query boolean | ||||
LockQuery(branchType BranchType, resourceId, xid, lockKeys string) (bool, error) | |||||
LockQuery(ctx context.Context, ranchType BranchType, resourceId, xid, lockKeys string) (bool, error) | |||||
} | } | ||||
// Resource Manager: common behaviors | // Resource Manager: common behaviors | ||||
@@ -1,5 +1,12 @@ | |||||
package model | package model | ||||
type GlobalTransactionRole int8 | |||||
const ( | |||||
LAUNCHER GlobalTransactionRole = 0 | |||||
PARTICIPANT GlobalTransactionRole = 1 | |||||
) | |||||
type TransactionManager interface { | type TransactionManager interface { | ||||
// Begin a new global transaction. | // Begin a new global transaction. | ||||
Begin(applicationId, transactionServiceGroup, name string, timeout int64) (string, error) | Begin(applicationId, transactionServiceGroup, name string, timeout int64) (string, error) | ||||
@@ -1,13 +1,21 @@ | |||||
package config | package config | ||||
type TMConfig struct { | type TMConfig struct { | ||||
CommitRetryCount int32 `default:"5" yaml:"commit_retry_count" json:"commit_retry_count,omitempty"` | |||||
RollbackRetryCount int32 `default:"5" yaml:"rollback_retry_count" json:"rollback_retry_count,omitempty"` | |||||
CommitRetryCount uint16 `default:"5" yaml:"commit_retry_count" json:"commit_retry_count,omitempty"` | |||||
RollbackRetryCount uint16 `default:"5" yaml:"rollback_retry_count" json:"rollback_retry_count,omitempty"` | |||||
DefaultGlobalTransactionTimeout uint16 `default:"60000" yaml:"default_global_transaction_timeout" json:"default_global_transaction_timeout,omitempty"` | |||||
DegradeCheck bool `default:"false" yaml:"degrade_check" json:"degrade_check,omitempty"` | |||||
DegradeCheckAllowTimes uint16 `default:"10" yaml:"degrade_check_allow_times" json:"degrade_check_allow_times,omitempty"` | |||||
DegradeCheckPeriod uint16 `default:"2000" yaml:"degrade_check_period" json:"degrade_check_period,omitempty"` | |||||
} | } | ||||
func GetDefaultTmConfig() TMConfig { | func GetDefaultTmConfig() TMConfig { | ||||
return TMConfig{ | return TMConfig{ | ||||
CommitRetryCount: 5, | |||||
RollbackRetryCount: 5, | |||||
CommitRetryCount: 5, | |||||
RollbackRetryCount: 5, | |||||
DefaultGlobalTransactionTimeout: 60000, | |||||
DegradeCheck: false, | |||||
DegradeCheckAllowTimes: 10, | |||||
DegradeCheckPeriod: 2000, | |||||
} | } | ||||
} | } |
@@ -1,6 +1,7 @@ | |||||
package imports | package imports | ||||
import ( | import ( | ||||
_ "github.com/seata/seata-go/pkg/rm/tcc" | |||||
_ "github.com/seata/seata-go/pkg/rpc/getty" | _ "github.com/seata/seata-go/pkg/rpc/getty" | ||||
_ "github.com/seata/seata-go/pkg/rpc/processor/client" | _ "github.com/seata/seata-go/pkg/rpc/processor/client" | ||||
) | ) |
@@ -15,6 +15,7 @@ import ( | |||||
type SerializerType byte | type SerializerType byte | ||||
// TODO 待重构 | |||||
const ( | const ( | ||||
SEATA = byte(0x1) | SEATA = byte(0x1) | ||||
PROTOBUF = byte(0x2) | PROTOBUF = byte(0x2) | ||||
@@ -13,6 +13,7 @@ import ( | |||||
"github.com/seata/seata-go/pkg/protocol" | "github.com/seata/seata-go/pkg/protocol" | ||||
) | ) | ||||
// TODO 待重构 | |||||
func AbstractResultMessageDecoder(in []byte) (interface{}, int) { | func AbstractResultMessageDecoder(in []byte) (interface{}, int) { | ||||
var ( | var ( | ||||
length16 uint16 = 0 | length16 uint16 = 0 | ||||
@@ -13,6 +13,7 @@ import ( | |||||
"github.com/seata/seata-go/pkg/utils/log" | "github.com/seata/seata-go/pkg/utils/log" | ||||
) | ) | ||||
// TODO 待重构 | |||||
func AbstractResultMessageEncoder(in interface{}) []byte { | func AbstractResultMessageEncoder(in interface{}) []byte { | ||||
var ( | var ( | ||||
zero16 int16 = 0 | zero16 int16 = 0 | ||||
@@ -13,18 +13,18 @@ const ( | |||||
// V1HeadLength v1 head length | // V1HeadLength v1 head length | ||||
V1HeadLength = 16 | V1HeadLength = 16 | ||||
// MSGTypeRequestSync request message type | |||||
// Request message type | |||||
MSGTypeRequestSync RequestType = 0 | MSGTypeRequestSync RequestType = 0 | ||||
// MSGTypeResponse response message type | |||||
// Response message type | |||||
MSGTypeResponse RequestType = 1 | MSGTypeResponse RequestType = 1 | ||||
// MSGTypeRequestOneway request one way | |||||
// Request which no need response | |||||
MSGTypeRequestOneway RequestType = 2 | MSGTypeRequestOneway RequestType = 2 | ||||
// MSGTypeHeartbeatRequest heart beat request | |||||
// Heartbeat Request | |||||
MSGTypeHeartbeatRequest RequestType = 3 | MSGTypeHeartbeatRequest RequestType = 3 | ||||
// MSGTypeHeartbeatResponse heart beat response | |||||
// Heartbeat Response | |||||
MSGTypeHeartbeatResponse RequestType = 4 | MSGTypeHeartbeatResponse RequestType = 4 | ||||
) | ) |
@@ -6,21 +6,15 @@ type AbstractResultMessage struct { | |||||
} | } | ||||
type AbstractIdentifyRequest struct { | type AbstractIdentifyRequest struct { | ||||
Version string | |||||
ApplicationId string `json:"applicationId"` | |||||
Version string | |||||
ApplicationId string `json:"applicationId"` | |||||
TransactionServiceGroup string | TransactionServiceGroup string | ||||
ExtraData []byte | |||||
ExtraData []byte | |||||
} | } | ||||
type AbstractIdentifyResponse struct { | type AbstractIdentifyResponse struct { | ||||
AbstractResultMessage | AbstractResultMessage | ||||
Version string | |||||
ExtraData []byte | |||||
Version string | |||||
ExtraData []byte | |||||
Identified bool | Identified bool | ||||
} | } |
@@ -1,6 +1,7 @@ | |||||
package transaction | package transaction | ||||
import ( | import ( | ||||
"context" | |||||
"github.com/seata/seata-go/pkg/protocol" | "github.com/seata/seata-go/pkg/protocol" | ||||
) | ) | ||||
@@ -12,7 +13,7 @@ type RMInboundHandler interface { | |||||
* @param request the request | * @param request the request | ||||
* @return the branch commit response | * @return the branch commit response | ||||
*/ | */ | ||||
HandleBranchCommitRequest(request protocol.BranchCommitRequest) (*protocol.BranchCommitResponse, error) | |||||
HandleBranchCommitRequest(ctx context.Context, request protocol.BranchCommitRequest) (*protocol.BranchCommitResponse, error) | |||||
/** | /** | ||||
* Handle branch rollback response. | * Handle branch rollback response. | ||||
@@ -21,12 +22,12 @@ type RMInboundHandler interface { | |||||
* @return the branch rollback response | * @return the branch rollback response | ||||
*/ | */ | ||||
HandleBranchRollbackRequest(request protocol.BranchRollbackRequest) (*protocol.BranchRollbackResponse, error) | |||||
HandleBranchRollbackRequest(ctx context.Context, request protocol.BranchRollbackRequest) (*protocol.BranchRollbackResponse, error) | |||||
/** | /** | ||||
* Handle delete undo log . | * Handle delete undo log . | ||||
* | * | ||||
* @param request the request | * @param request the request | ||||
*/ | */ | ||||
HandleUndoLogDeleteRequest(request protocol.UndoLogDeleteRequest) error | |||||
HandleUndoLogDeleteRequest(ctx context.Context, request protocol.UndoLogDeleteRequest) error | |||||
} | } |
@@ -0,0 +1,163 @@ | |||||
package api | |||||
type Propagation int8 | |||||
type TransactionInfo struct { | |||||
TimeOut int32 | |||||
Name string | |||||
Propagation Propagation | |||||
LockRetryInternal int64 | |||||
LockRetryTimes int64 | |||||
} | |||||
const ( | |||||
/** | |||||
* The REQUIRED. | |||||
* The default propagation. | |||||
* | |||||
* <p> | |||||
* If transaction is existing, execute with current transaction, | |||||
* else execute with new transaction. | |||||
* </p> | |||||
* | |||||
* <p> | |||||
* The logic is similar to the following code: | |||||
* <code><pre> | |||||
* if (tx == null) { | |||||
* try { | |||||
* tx = beginNewTransaction(); // begin new transaction, is not existing | |||||
* Object rs = business.execute(); // execute with new transaction | |||||
* commitTransaction(tx); | |||||
* return rs; | |||||
* } catch (Exception ex) { | |||||
* rollbackTransaction(tx); | |||||
* throw ex; | |||||
* } | |||||
* } else { | |||||
* return business.execute(); // execute with current transaction | |||||
* } | |||||
* </pre></code> | |||||
* </p> | |||||
*/ | |||||
REQUIRED Propagation = iota | |||||
/** | |||||
* The REQUIRES_NEW. | |||||
* | |||||
* <p> | |||||
* If transaction is existing, suspend it, and then execute business with new transaction. | |||||
* </p> | |||||
* | |||||
* <p> | |||||
* The logic is similar to the following code: | |||||
* <code><pre> | |||||
* try { | |||||
* if (tx != null) { | |||||
* suspendedResource = suspendTransaction(tx); // suspend current transaction | |||||
* } | |||||
* try { | |||||
* tx = beginNewTransaction(); // begin new transaction | |||||
* Object rs = business.execute(); // execute with new transaction | |||||
* commitTransaction(tx); | |||||
* return rs; | |||||
* } catch (Exception ex) { | |||||
* rollbackTransaction(tx); | |||||
* throw ex; | |||||
* } | |||||
* } finally { | |||||
* if (suspendedResource != null) { | |||||
* resumeTransaction(suspendedResource); // resume transaction | |||||
* } | |||||
* } | |||||
* </pre></code> | |||||
* </p> | |||||
*/ | |||||
REQUIRES_NEW | |||||
/** | |||||
* The NOT_SUPPORTED. | |||||
* | |||||
* <p> | |||||
* If transaction is existing, suspend it, and then execute business without transaction. | |||||
* </p> | |||||
* | |||||
* <p> | |||||
* The logic is similar to the following code: | |||||
* <code><pre> | |||||
* try { | |||||
* if (tx != null) { | |||||
* suspendedResource = suspendTransaction(tx); // suspend current transaction | |||||
* } | |||||
* return business.execute(); // execute without transaction | |||||
* } finally { | |||||
* if (suspendedResource != null) { | |||||
* resumeTransaction(suspendedResource); // resume transaction | |||||
* } | |||||
* } | |||||
* </pre></code> | |||||
* </p> | |||||
*/ | |||||
NOT_SUPPORTED | |||||
/** | |||||
* The SUPPORTS. | |||||
* | |||||
* <p> | |||||
* If transaction is not existing, execute without global transaction, | |||||
* else execute business with current transaction. | |||||
* </p> | |||||
* | |||||
* <p> | |||||
* The logic is similar to the following code: | |||||
* <code><pre> | |||||
* if (tx != null) { | |||||
* return business.execute(); // execute with current transaction | |||||
* } else { | |||||
* return business.execute(); // execute without transaction | |||||
* } | |||||
* </pre></code> | |||||
* </p> | |||||
*/ | |||||
SUPPORTS | |||||
/** | |||||
* The NEVER. | |||||
* | |||||
* <p> | |||||
* If transaction is existing, throw exception, | |||||
* else execute business without transaction. | |||||
* </p> | |||||
* | |||||
* <p> | |||||
* The logic is similar to the following code: | |||||
* <code><pre> | |||||
* if (tx != null) { | |||||
* throw new TransactionException("existing transaction"); | |||||
* } | |||||
* return business.execute(); // execute without transaction | |||||
* </pre></code> | |||||
* </p> | |||||
*/ | |||||
NEVER | |||||
/** | |||||
* The MANDATORY. | |||||
* | |||||
* <p> | |||||
* If transaction is not existing, throw exception, | |||||
* else execute business with current transaction. | |||||
* </p> | |||||
* | |||||
* <p> | |||||
* The logic is similar to the following code: | |||||
* <code><pre> | |||||
* if (tx == null) { | |||||
* throw new TransactionException("not existing transaction"); | |||||
* } | |||||
* return business.execute(); // execute with current transaction | |||||
* </pre></code> | |||||
* </p> | |||||
*/ | |||||
MANDATORY | |||||
) |
@@ -0,0 +1,8 @@ | |||||
package api | |||||
import "context" | |||||
type TransactionalExecutor interface { | |||||
Execute(ctx context.Context, param interface{}) (interface{}, error) | |||||
GetTransactionInfo() TransactionInfo | |||||
} |
@@ -0,0 +1,92 @@ | |||||
package api | |||||
import ( | |||||
"context" | |||||
"github.com/pkg/errors" | |||||
"github.com/seata/seata-go/pkg/common/model" | |||||
"github.com/seata/seata-go/pkg/tm" | |||||
"github.com/seata/seata-go/pkg/utils/log" | |||||
"sync" | |||||
) | |||||
var ( | |||||
// singletone ResourceManagerFacade | |||||
transactionTemplate *TransactionTemplate | |||||
onceTransactionTemplate = &sync.Once{} | |||||
) | |||||
func GetTransactionTemplate() *TransactionTemplate { | |||||
if transactionTemplate == nil { | |||||
onceTransactionTemplate.Do(func() { | |||||
transactionTemplate = &TransactionTemplate{} | |||||
}) | |||||
} | |||||
return transactionTemplate | |||||
} | |||||
type TransactionTemplate struct { | |||||
} | |||||
func (t *TransactionTemplate) Execute(ctx context.Context, business TransactionalExecutor, param interface{}) (interface{}, error) { | |||||
if !model.IsSeataContext(ctx) { | |||||
err := errors.New("context should be inited as seata context!") | |||||
log.Error(err) | |||||
return nil, err | |||||
} | |||||
if model.GetTransactionRole(ctx) == nil { | |||||
model.SetTransactionRole(ctx, model.LAUNCHER) | |||||
} | |||||
var tx *tm.GlobalTransaction | |||||
if model.HasXID(ctx) { | |||||
tx = &tm.GlobalTransaction{ | |||||
Xid: model.GetXID(ctx), | |||||
Status: model.Begin, | |||||
Role: model.PARTICIPANT, | |||||
} | |||||
} | |||||
// todo: Handle the transaction propagation. | |||||
if tx == nil { | |||||
tx = &tm.GlobalTransaction{ | |||||
Xid: model.GetXID(ctx), | |||||
Status: model.UnKnown, | |||||
Role: model.LAUNCHER, | |||||
} | |||||
} | |||||
// todo: set current tx config to holder | |||||
// begin global transaction | |||||
err := t.BeginTransaction(ctx, tx, business.GetTransactionInfo().TimeOut, business.GetTransactionInfo().Name) | |||||
if err != nil { | |||||
log.Infof("transactionTemplate: begin transaction failed, error %v", err) | |||||
return nil, err | |||||
} | |||||
// do your business | |||||
res, err := business.Execute(ctx, param) | |||||
if err != nil { | |||||
log.Infof("transactionTemplate: execute business failed, error %v", err) | |||||
return nil, tm.GetGlobalTransactionManager().Rollback(ctx, tx) | |||||
} | |||||
// commit global transaction | |||||
err = t.CommitTransaction(ctx, tx) | |||||
if err != nil { | |||||
log.Infof("transactionTemplate: commit transaction failed, error %v", err) | |||||
// rollback transaction | |||||
return nil, tm.GetGlobalTransactionManager().Rollback(ctx, tx) | |||||
} | |||||
return res, err | |||||
} | |||||
func (TransactionTemplate) BeginTransaction(ctx context.Context, tx *tm.GlobalTransaction, timeout int32, name string) error { | |||||
return tm.GetGlobalTransactionManager().Begin(ctx, tx, timeout, name) | |||||
} | |||||
func (TransactionTemplate) CommitTransaction(ctx context.Context, tx *tm.GlobalTransaction) error { | |||||
return tm.GetGlobalTransactionManager().Commit(ctx, tx) | |||||
} |
@@ -1,28 +0,0 @@ | |||||
package rm | |||||
import ( | |||||
"github.com/seata/seata-go/pkg/common/model" | |||||
) | |||||
// TODO | |||||
type RMRemoting struct { | |||||
} | |||||
// Branch register long | |||||
func (RMRemoting) BranchRegister(branchType model.BranchType, resourceId, clientId, xid, applicationData, lockKeys string) (int64, error) { | |||||
return 0, nil | |||||
} | |||||
// Branch report | |||||
func (RMRemoting) BranchReport(branchType model.BranchType, xid string, branchId int64, status model.BranchStatus, applicationData string) error { | |||||
return nil | |||||
} | |||||
// Lock query boolean | |||||
func (RMRemoting) LockQuery(branchType model.BranchType, resourceId, xid, lockKeys string) (bool, error) { | |||||
return false, nil | |||||
} | |||||
func (RMRemoting) RegisterResource(resource model.Resource) error { | |||||
return nil | |||||
} |
@@ -1,8 +1,10 @@ | |||||
package rm | package rm | ||||
import ( | import ( | ||||
"context" | |||||
"github.com/seata/seata-go/pkg/common/model" | "github.com/seata/seata-go/pkg/common/model" | ||||
"github.com/seata/seata-go/pkg/protocol" | "github.com/seata/seata-go/pkg/protocol" | ||||
"github.com/seata/seata-go/pkg/utils/log" | |||||
) | ) | ||||
type CommonRMHandler struct { | type CommonRMHandler struct { | ||||
@@ -14,13 +16,14 @@ func (h *CommonRMHandler) SetRMGetter(rmGetter model.ResourceManagerGetter) { | |||||
} | } | ||||
// Handle branch commit response. | // Handle branch commit response. | ||||
func (h *CommonRMHandler) HandleBranchCommitRequest(request protocol.BranchCommitRequest) (*protocol.BranchCommitResponse, error) { | |||||
func (h *CommonRMHandler) HandleBranchCommitRequest(ctx context.Context, request protocol.BranchCommitRequest) (*protocol.BranchCommitResponse, error) { | |||||
xid := request.Xid | xid := request.Xid | ||||
branchID := request.BranchId | branchID := request.BranchId | ||||
resourceID := request.ResourceId | resourceID := request.ResourceId | ||||
applicationData := request.ApplicationData | applicationData := request.ApplicationData | ||||
log.Infof("Branch committing: xid %s, branchID %s, resourceID %s, applicationData %s", xid, branchID, resourceID, applicationData) | |||||
status, err := h.rmGetter.GetResourceManager().BranchCommit(request.BranchType, xid, branchID, resourceID, applicationData) | |||||
status, err := h.rmGetter.GetResourceManager().BranchCommit(ctx, request.BranchType, xid, branchID, resourceID, applicationData) | |||||
if err != nil { | if err != nil { | ||||
// TODO: handle error | // TODO: handle error | ||||
return nil, err | return nil, err | ||||
@@ -36,13 +39,13 @@ func (h *CommonRMHandler) HandleBranchCommitRequest(request protocol.BranchCommi | |||||
// Handle branch rollback response. | // Handle branch rollback response. | ||||
// TODO | // TODO | ||||
func (h *CommonRMHandler) HandleBranchRollbackRequest(request protocol.BranchRollbackRequest) (*protocol.BranchRollbackResponse, error) { | |||||
func (h *CommonRMHandler) HandleBranchRollbackRequest(ctx context.Context, request protocol.BranchRollbackRequest) (*protocol.BranchRollbackResponse, error) { | |||||
return nil, nil | return nil, nil | ||||
} | } | ||||
// Handle delete undo log . | // Handle delete undo log . | ||||
// TODO | // TODO | ||||
func (h *CommonRMHandler) HandleUndoLogDeleteRequest(request protocol.UndoLogDeleteRequest) error { | |||||
func (h *CommonRMHandler) HandleUndoLogDeleteRequest(ctx context.Context, request protocol.UndoLogDeleteRequest) error { | |||||
return nil | return nil | ||||
} | } | ||||
@@ -1,6 +1,7 @@ | |||||
package rm | package rm | ||||
import ( | import ( | ||||
"context" | |||||
"fmt" | "fmt" | ||||
"sync" | "sync" | ||||
) | ) | ||||
@@ -30,7 +31,7 @@ type ResourceManagerFacade struct { | |||||
} | } | ||||
// 将事务管理器注册到这里 | // 将事务管理器注册到这里 | ||||
func RegisterResource(resourceManager model2.ResourceManager) { | |||||
func RegisterResourceManager(resourceManager model2.ResourceManager) { | |||||
resourceManagerMap.Store(resourceManager.GetBranchType(), resourceManager) | resourceManagerMap.Store(resourceManager.GetBranchType(), resourceManager) | ||||
} | } | ||||
@@ -43,28 +44,28 @@ func (*ResourceManagerFacade) GetResourceManager(branchType model2.BranchType) m | |||||
} | } | ||||
// Commit a branch transaction | // Commit a branch transaction | ||||
func (d *ResourceManagerFacade) BranchCommit(branchType model2.BranchType, xid string, branchId int64, resourceId string, applicationData []byte) (model2.BranchStatus, error) { | |||||
return d.GetResourceManager(branchType).BranchCommit(branchType, xid, branchId, resourceId, applicationData) | |||||
func (d *ResourceManagerFacade) BranchCommit(ctx context.Context, branchType model2.BranchType, xid string, branchId int64, resourceId string, applicationData []byte) (model2.BranchStatus, error) { | |||||
return d.GetResourceManager(branchType).BranchCommit(ctx, branchType, xid, branchId, resourceId, applicationData) | |||||
} | } | ||||
// Rollback a branch transaction | // Rollback a branch transaction | ||||
func (d *ResourceManagerFacade) BranchRollback(branchType model2.BranchType, xid string, branchId int64, resourceId string, applicationData []byte) (model2.BranchStatus, error) { | |||||
return d.GetResourceManager(branchType).BranchRollback(branchType, xid, branchId, resourceId, applicationData) | |||||
func (d *ResourceManagerFacade) BranchRollback(ctx context.Context, branchType model2.BranchType, xid string, branchId int64, resourceId string, applicationData []byte) (model2.BranchStatus, error) { | |||||
return d.GetResourceManager(branchType).BranchRollback(ctx, branchType, xid, branchId, resourceId, applicationData) | |||||
} | } | ||||
// Branch register long | // Branch register long | ||||
func (d *ResourceManagerFacade) BranchRegister(branchType model2.BranchType, resourceId, clientId, xid, applicationData, lockKeys string) (int64, error) { | |||||
return d.GetResourceManager(branchType).BranchRegister(branchType, resourceId, clientId, xid, applicationData, lockKeys) | |||||
func (d *ResourceManagerFacade) BranchRegister(ctx context.Context, branchType model2.BranchType, resourceId, clientId, xid, applicationData, lockKeys string) (int64, error) { | |||||
return d.GetResourceManager(branchType).BranchRegister(ctx, branchType, resourceId, clientId, xid, applicationData, lockKeys) | |||||
} | } | ||||
// Branch report | // Branch report | ||||
func (d *ResourceManagerFacade) BranchReport(branchType model2.BranchType, xid string, branchId int64, status model2.BranchStatus, applicationData string) error { | |||||
return d.GetResourceManager(branchType).BranchReport(branchType, xid, branchId, status, applicationData) | |||||
func (d *ResourceManagerFacade) BranchReport(ctx context.Context, branchType model2.BranchType, xid string, branchId int64, status model2.BranchStatus, applicationData string) error { | |||||
return d.GetResourceManager(branchType).BranchReport(ctx, branchType, xid, branchId, status, applicationData) | |||||
} | } | ||||
// Lock query boolean | // Lock query boolean | ||||
func (d *ResourceManagerFacade) LockQuery(branchType model2.BranchType, resourceId, xid, lockKeys string) (bool, error) { | |||||
return d.GetResourceManager(branchType).LockQuery(branchType, resourceId, xid, lockKeys) | |||||
func (d *ResourceManagerFacade) LockQuery(ctx context.Context, branchType model2.BranchType, resourceId, xid, lockKeys string) (bool, error) { | |||||
return d.GetResourceManager(branchType).LockQuery(ctx, branchType, resourceId, xid, lockKeys) | |||||
} | } | ||||
// Register a model.Resource to be managed by model.Resource Manager | // Register a model.Resource to be managed by model.Resource Manager | ||||
@@ -1,6 +1,7 @@ | |||||
package rm | package rm | ||||
import ( | import ( | ||||
"context" | |||||
"sync" | "sync" | ||||
) | ) | ||||
@@ -28,20 +29,20 @@ func GetRMHandlerFacadeInstance() *RMHandlerFacade { | |||||
} | } | ||||
// Handle branch commit response. | // Handle branch commit response. | ||||
func (h *RMHandlerFacade) HandleBranchCommitRequest(request protocol.BranchCommitRequest) (*protocol.BranchCommitResponse, error) { | |||||
return h.getRMHandler(request.BranchType).HandleBranchCommitRequest(request) | |||||
func (h *RMHandlerFacade) HandleBranchCommitRequest(ctx context.Context, request protocol.BranchCommitRequest) (*protocol.BranchCommitResponse, error) { | |||||
return h.getRMHandler(request.BranchType).HandleBranchCommitRequest(ctx, request) | |||||
} | } | ||||
// Handle branch rollback response. | // Handle branch rollback response. | ||||
// TODO | // TODO | ||||
func (h *RMHandlerFacade) HandleBranchRollbackRequest(request protocol.BranchRollbackRequest) (*protocol.BranchRollbackResponse, error) { | |||||
return h.getRMHandler(request.BranchType).HandleBranchRollbackRequest(request) | |||||
func (h *RMHandlerFacade) HandleBranchRollbackRequest(ctx context.Context, request protocol.BranchRollbackRequest) (*protocol.BranchRollbackResponse, error) { | |||||
return h.getRMHandler(request.BranchType).HandleBranchRollbackRequest(ctx, request) | |||||
} | } | ||||
// Handle delete undo log . | // Handle delete undo log . | ||||
// TODO | // TODO | ||||
func (h *RMHandlerFacade) HandleUndoLogDeleteRequest(request protocol.UndoLogDeleteRequest) error { | |||||
return h.getRMHandler(request.BranchType).HandleUndoLogDeleteRequest(request) | |||||
func (h *RMHandlerFacade) HandleUndoLogDeleteRequest(ctx context.Context, request protocol.UndoLogDeleteRequest) error { | |||||
return h.getRMHandler(request.BranchType).HandleUndoLogDeleteRequest(ctx, request) | |||||
} | } | ||||
func (h *RMHandlerFacade) RegisteRMHandler(handler *CommonRMHandler) { | func (h *RMHandlerFacade) RegisteRMHandler(handler *CommonRMHandler) { | ||||
@@ -0,0 +1,100 @@ | |||||
package rm | |||||
import ( | |||||
"github.com/seata/seata-go/pkg/common/model" | |||||
"github.com/seata/seata-go/pkg/protocol" | |||||
"github.com/seata/seata-go/pkg/rpc/getty" | |||||
"github.com/seata/seata-go/pkg/utils/log" | |||||
"sync" | |||||
) | |||||
var ( | |||||
rmRemoting *RMRemoting | |||||
onceGettyRemoting = &sync.Once{} | |||||
) | |||||
func GetRMRemotingInstance() *RMRemoting { | |||||
if rmRemoting == nil { | |||||
onceGettyRemoting.Do(func() { | |||||
rmRemoting = &RMRemoting{} | |||||
}) | |||||
} | |||||
return rmRemoting | |||||
} | |||||
// TODO | |||||
type RMRemoting struct { | |||||
} | |||||
// Branch register long | |||||
func (RMRemoting) BranchRegister(branchType model.BranchType, resourceId, clientId, xid, applicationData, lockKeys string) (int64, error) { | |||||
return 0, nil | |||||
} | |||||
// Branch report | |||||
func (RMRemoting) BranchReport(branchType model.BranchType, xid string, branchId int64, status model.BranchStatus, applicationData string) error { | |||||
return nil | |||||
} | |||||
// Lock query boolean | |||||
func (RMRemoting) LockQuery(branchType model.BranchType, resourceId, xid, lockKeys string) (bool, error) { | |||||
return false, nil | |||||
} | |||||
func (r *RMRemoting) RegisterResource(resource model.Resource) error { | |||||
req := protocol.RegisterRMRequest{ | |||||
AbstractIdentifyRequest: protocol.AbstractIdentifyRequest{ | |||||
//todo replace with config | |||||
Version: "1.4.2", | |||||
ApplicationId: "tcc-sample", | |||||
TransactionServiceGroup: "my_test_tx_group", | |||||
}, | |||||
ResourceIds: resource.GetResourceId(), | |||||
} | |||||
res, err := getty.GetGettyRemotingClient().SendSyncRequest(req) | |||||
if err != nil { | |||||
log.Errorf("RegisterResourceManager error: {%#v}", err.Error()) | |||||
return err | |||||
} | |||||
if isRegisterSuccess(res) { | |||||
r.onRegisterRMSuccess(res.(protocol.RegisterRMResponse)) | |||||
} else { | |||||
r.onRegisterRMFailure(res.(protocol.RegisterRMResponse)) | |||||
} | |||||
return nil | |||||
} | |||||
func isRegisterSuccess(response interface{}) bool { | |||||
//if res, ok := response.(protocol.RegisterTMResponse); ok { | |||||
// return res.Identified | |||||
//} else if res, ok := response.(protocol.RegisterRMResponse); ok { | |||||
// return res.Identified | |||||
//} | |||||
//return false | |||||
if res, ok := response.(protocol.RegisterRMResponse); ok { | |||||
return res.Identified | |||||
} | |||||
return false | |||||
} | |||||
func (r *RMRemoting) onRegisterRMSuccess(response protocol.RegisterRMResponse) { | |||||
// TODO | |||||
log.Infof("register RM success. response: %#v", response) | |||||
} | |||||
func (r *RMRemoting) onRegisterRMFailure(response protocol.RegisterRMResponse) { | |||||
// TODO | |||||
log.Infof("register RM failure. response: %#v", response) | |||||
} | |||||
func (r *RMRemoting) onRegisterTMSuccess(response protocol.RegisterTMResponse) { | |||||
// TODO | |||||
log.Infof("register TM success. response: %#v", response) | |||||
} | |||||
func (r *RMRemoting) onRegisterTMFailure(response protocol.RegisterTMResponse) { | |||||
// TODO | |||||
log.Infof("register TM failure. response: %#v", response) | |||||
} |
@@ -0,0 +1,27 @@ | |||||
package rm | |||||
import ( | |||||
"github.com/seata/seata-go/pkg/common/model" | |||||
_ "github.com/seata/seata-go/pkg/imports" | |||||
"github.com/seata/seata-go/pkg/protocol" | |||||
"github.com/seata/seata-go/pkg/rpc/getty" | |||||
"github.com/seata/seata-go/pkg/utils/log" | |||||
) | |||||
func (RMRemoting) RegisterResource(resource model.Resource) error { | |||||
req := protocol.RegisterRMRequest{ | |||||
AbstractIdentifyRequest: protocol.AbstractIdentifyRequest{ | |||||
//todo replace with config | |||||
Version: "1.4.2", | |||||
ApplicationId: "tcc-sample", | |||||
TransactionServiceGroup: "my_test_tx_group", | |||||
}, | |||||
ResourceIds: resource.GetResourceId(), | |||||
} | |||||
err := getty.GetGettyRemotingClient().SendAsyncRequest(req) | |||||
if err != nil { | |||||
log.Error("RegisterResourceManager error: {%#v}", err.Error()) | |||||
return err | |||||
} | |||||
return nil | |||||
} |
@@ -4,5 +4,5 @@ type BusinessActionContext struct { | |||||
Xid string | Xid string | ||||
BranchId string | BranchId string | ||||
ActionName string | ActionName string | ||||
ActionContext map[string]interface{} | |||||
ActionContext interface{} | |||||
} | } |
@@ -1,23 +1,20 @@ | |||||
package tcc | package tcc | ||||
import ( | |||||
"reflect" | |||||
) | |||||
import ( | import ( | ||||
"github.com/seata/seata-go/pkg/common/model" | "github.com/seata/seata-go/pkg/common/model" | ||||
) | ) | ||||
type TCCResource struct { | type TCCResource struct { | ||||
ResourceGroupId string `default:"DEFAULT"` | |||||
AppName string | |||||
ActionName string | |||||
TargetBean interface{} | |||||
PrepareMethod reflect.Method | |||||
CommitMethodName string | |||||
CommitMethod reflect.Method | |||||
RollbackMethodName string | |||||
RollbackMethod reflect.Method | |||||
TCCServiceBean TCCService | |||||
ResourceGroupId string `default:"DEFAULT"` | |||||
AppName string | |||||
ActionName string | |||||
//TargetBean interface{} | |||||
//PrepareMethod reflect.Method | |||||
//CommitMethodName string | |||||
//CommitMethod reflect.Method | |||||
//RollbackMethodName string | |||||
//RollbackMethod reflect.Method | |||||
} | } | ||||
func (t *TCCResource) GetResourceGroupId() string { | func (t *TCCResource) GetResourceGroupId() string { | ||||
@@ -1,6 +1,12 @@ | |||||
package tcc | package tcc | ||||
import ( | import ( | ||||
"context" | |||||
"fmt" | |||||
"github.com/seata/seata-go/pkg/protocol" | |||||
"github.com/seata/seata-go/pkg/rm/tcc/api" | |||||
"github.com/seata/seata-go/pkg/rpc/getty" | |||||
"github.com/seata/seata-go/pkg/utils/log" | |||||
"sync" | "sync" | ||||
) | ) | ||||
@@ -9,34 +15,121 @@ import ( | |||||
"github.com/seata/seata-go/pkg/rm" | "github.com/seata/seata-go/pkg/rm" | ||||
) | ) | ||||
type TCCRm struct { | |||||
rmRemoting rm.RMRemoting | |||||
var ( | |||||
tCCResourceManager *TCCResourceManager | |||||
onceTCCResourceManager = &sync.Once{} | |||||
) | |||||
func init() { | |||||
rm.RegisterResourceManager(GetTCCResourceManagerInstance()) | |||||
} | |||||
func GetTCCResourceManagerInstance() *TCCResourceManager { | |||||
if tCCResourceManager == nil { | |||||
onceTCCResourceManager.Do(func() { | |||||
tCCResourceManager = &TCCResourceManager{ | |||||
resourceManagerMap: sync.Map{}, | |||||
rmRemoting: rm.GetRMRemotingInstance(), | |||||
} | |||||
}) | |||||
} | |||||
return tCCResourceManager | |||||
} | |||||
type TCCResourceManager struct { | |||||
rmRemoting *rm.RMRemoting | |||||
// resourceID -> resource | // resourceID -> resource | ||||
resourceManagerMap sync.Map | resourceManagerMap sync.Map | ||||
} | } | ||||
func (t *TCCRm) RegisterResource(resource model.Resource) error { | |||||
// register transaction branch | |||||
func (t *TCCResourceManager) BranchRegister(ctx context.Context, branchType model.BranchType, resourceId, clientId, xid, applicationData, lockKeys string) (int64, error) { | |||||
request := protocol.BranchRegisterRequest{ | |||||
Xid: xid, | |||||
BranchType: t.GetBranchType(), | |||||
ResourceId: resourceId, | |||||
LockKey: lockKeys, | |||||
ApplicationData: []byte(applicationData), | |||||
} | |||||
response, err := getty.GetGettyRemotingClient().SendSyncRequest(request) | |||||
if err != nil || response == nil { | |||||
log.Errorf("BranchRegister error: %v, res %v", err.Error(), response) | |||||
return 0, err | |||||
} | |||||
return response.(protocol.BranchRegisterResponse).BranchId, nil | |||||
} | |||||
func (t *TCCResourceManager) BranchReport(ctx context.Context, ranchType model.BranchType, xid string, branchId int64, status model.BranchStatus, applicationData string) error { | |||||
//TODO implement me | |||||
panic("implement me") | |||||
} | |||||
func (t *TCCResourceManager) LockQuery(ctx context.Context, ranchType model.BranchType, resourceId, xid, lockKeys string) (bool, error) { | |||||
//TODO implement me | |||||
panic("implement me") | |||||
} | |||||
func (t *TCCResourceManager) UnregisterResource(resource model.Resource) error { | |||||
//TODO implement me | |||||
panic("implement me") | |||||
} | |||||
func (t *TCCResourceManager) RegisterResource(resource model.Resource) error { | |||||
if _, ok := resource.(*TCCResource); !ok { | |||||
panic(fmt.Sprintf("register tcc resource error, TCCResource is needed, param %v", resource)) | |||||
} | |||||
t.resourceManagerMap.Store(resource.GetResourceId(), resource) | t.resourceManagerMap.Store(resource.GetResourceId(), resource) | ||||
t.rmRemoting.RegisterResource(resource) | |||||
return nil | |||||
return t.rmRemoting.RegisterResource(resource) | |||||
} | } | ||||
func (t *TCCRm) GetManagedResources() sync.Map { | |||||
func (t *TCCResourceManager) GetManagedResources() sync.Map { | |||||
return t.resourceManagerMap | return t.resourceManagerMap | ||||
} | } | ||||
// Commit a branch transaction | // Commit a branch transaction | ||||
func (t *TCCRm) BranchCommit(branchType model.BranchType, xid, branchId int64, resourceId, applicationData string) (model.BranchStatus, error) { | |||||
// TODO | |||||
return 0, nil | |||||
func (t *TCCResourceManager) BranchCommit(ctx context.Context, ranchType model.BranchType, xid string, branchID int64, resourceID string, applicationData []byte) (model.BranchStatus, error) { | |||||
var tccResource *TCCResource | |||||
if resource, ok := t.resourceManagerMap.Load(resourceID); !ok { | |||||
err := fmt.Errorf("CC resource is not exist, resourceId: %s", resourceID) | |||||
return 0, err | |||||
} else { | |||||
tccResource, _ = resource.(*TCCResource) | |||||
} | |||||
err := tccResource.TCCServiceBean.Commit(ctx, t.getBusinessActionContext(xid, branchID, resourceID, applicationData)) | |||||
if err != nil { | |||||
return model.BranchStatusPhasetwoCommitFailedRetryable, err | |||||
} | |||||
return model.BranchStatusPhasetwoCommitted, err | |||||
} | |||||
func (t *TCCResourceManager) getBusinessActionContext(xid string, branchID int64, resourceID string, applicationData []byte) api.BusinessActionContext { | |||||
return api.BusinessActionContext{ | |||||
Xid: xid, | |||||
BranchId: string(branchID), | |||||
ActionName: resourceID, | |||||
// todo get ActionContext | |||||
//ActionContext:, | |||||
} | |||||
} | } | ||||
// Rollback a branch transaction | // Rollback a branch transaction | ||||
func (t *TCCRm) BranchRollback(branchType model.BranchType, xid string, branchId int64, resourceId, applicationData string) (model.BranchStatus, error) { | |||||
// TODO | |||||
return 0, nil | |||||
func (t *TCCResourceManager) BranchRollback(ctx context.Context, ranchType model.BranchType, xid string, branchID int64, resourceID string, applicationData []byte) (model.BranchStatus, error) { | |||||
var tccResource *TCCResource | |||||
if resource, ok := t.resourceManagerMap.Load(resourceID); !ok { | |||||
err := fmt.Errorf("CC resource is not exist, resourceId: %s", resourceID) | |||||
return 0, err | |||||
} else { | |||||
tccResource, _ = resource.(*TCCResource) | |||||
} | |||||
err := tccResource.TCCServiceBean.Rollback(ctx, t.getBusinessActionContext(xid, branchID, resourceID, applicationData)) | |||||
if err != nil { | |||||
return model.BranchStatusPhasetwoRollbacked, err | |||||
} | |||||
return model.BranchStatusPhasetwoRollbackFailedRetryable, err | |||||
} | } | ||||
func (t *TCCRm) GetBranchType() model.BranchType { | |||||
func (t *TCCResourceManager) GetBranchType() model.BranchType { | |||||
return model.BranchTypeTCC | return model.BranchTypeTCC | ||||
} | } |
@@ -2,6 +2,16 @@ package tcc | |||||
import ( | import ( | ||||
"context" | "context" | ||||
"encoding/json" | |||||
"fmt" | |||||
"github.com/pkg/errors" | |||||
"github.com/seata/seata-go/pkg/common" | |||||
"github.com/seata/seata-go/pkg/common/model" | |||||
"github.com/seata/seata-go/pkg/rm" | |||||
api2 "github.com/seata/seata-go/pkg/rm/tcc/api" | |||||
"github.com/seata/seata-go/pkg/utils" | |||||
"github.com/seata/seata-go/pkg/utils/log" | |||||
"time" | |||||
) | ) | ||||
import ( | import ( | ||||
@@ -11,10 +21,99 @@ import ( | |||||
type TCCService interface { | type TCCService interface { | ||||
Prepare(ctx context.Context, params interface{}) error | Prepare(ctx context.Context, params interface{}) error | ||||
Commit(ctx context.Context, businessActionContext api.BusinessActionContext) error | |||||
Rollback(ctx context.Context, businessActionContext api.BusinessActionContext) error | |||||
Commit(ctx context.Context, businessActionContext api2.BusinessActionContext) error | |||||
Rollback(ctx context.Context, businessActionContext api2.BusinessActionContext) error | |||||
GetActionName() string | GetActionName() string | ||||
GetRemoteType() remoting.RemoteType | GetRemoteType() remoting.RemoteType | ||||
GetServiceType() remoting.ServiceType | GetServiceType() remoting.ServiceType | ||||
} | } | ||||
type TCCServiceProxy struct { | |||||
TCCService | |||||
} | |||||
func NewTCCServiceProxy(tccService TCCService) TCCService { | |||||
if tccService == nil { | |||||
panic("param tccService should not be nil") | |||||
} | |||||
// register resource | |||||
tccResource := TCCResource{ | |||||
TCCServiceBean: tccService, | |||||
ResourceGroupId: "DEFAULT", | |||||
AppName: "", | |||||
ActionName: tccService.GetActionName(), | |||||
} | |||||
err := rm.GetResourceManagerFacadeInstance().GetResourceManager(model.BranchTypeTCC).RegisterResource(&tccResource) | |||||
if err != nil { | |||||
panic(fmt.Sprintf("NewTCCServiceProxy registerResource error: {%#v}", err.Error())) | |||||
} | |||||
return &TCCServiceProxy{ | |||||
TCCService: tccService, | |||||
} | |||||
} | |||||
func (t *TCCServiceProxy) Prepare(ctx context.Context, param interface{}) error { | |||||
var err error | |||||
if model.IsSeataContext(ctx) { | |||||
// execute transaction | |||||
_, err = api.GetTransactionTemplate().Execute(ctx, t, param) | |||||
} else { | |||||
log.Warn("context is not inited as seata context, will not execute transaction!") | |||||
err = t.TCCService.Prepare(ctx, param) | |||||
} | |||||
return err | |||||
} | |||||
// register transaction branch, and then execute business | |||||
func (t *TCCServiceProxy) Execute(ctx context.Context, param interface{}) (interface{}, error) { | |||||
// register transaction branch | |||||
err := t.RegisteBranch(ctx, param) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return nil, t.TCCService.Prepare(ctx, param) | |||||
} | |||||
func (t *TCCServiceProxy) RegisteBranch(ctx context.Context, param interface{}) error { | |||||
// register transaction branch | |||||
if !model.HasXID(ctx) { | |||||
err := errors.New("BranchRegister error, xid should not be nil") | |||||
log.Errorf(err.Error()) | |||||
return err | |||||
} | |||||
tccContext := make(map[string]interface{}, 0) | |||||
tccContext[common.StartTime] = time.Now().UnixNano() / 1e6 | |||||
tccContext[common.HostName] = utils.GetLocalIp() | |||||
tccContextStr, _ := json.Marshal(tccContext) | |||||
branchId, err := rm.GetResourceManagerFacadeInstance().GetResourceManager(model.BranchTypeTCC).BranchRegister( | |||||
ctx, model.BranchTypeTCC, t.GetActionName(), "", model.GetXID(ctx), string(tccContextStr), "") | |||||
if err != nil { | |||||
err = errors.New(fmt.Sprintf("BranchRegister error: %v", err.Error())) | |||||
log.Error(err.Error()) | |||||
return err | |||||
} | |||||
actionContext := &api2.BusinessActionContext{ | |||||
Xid: model.GetXID(ctx), | |||||
BranchId: string(branchId), | |||||
ActionName: t.GetActionName(), | |||||
ActionContext: param, | |||||
} | |||||
model.SetBusinessActionContext(ctx, actionContext) | |||||
return nil | |||||
} | |||||
func (t *TCCServiceProxy) GetTransactionInfo() api.TransactionInfo { | |||||
// todo replace with config | |||||
return api.TransactionInfo{ | |||||
TimeOut: 10000, | |||||
Name: t.GetActionName(), | |||||
//Propagation, Propagation | |||||
//LockRetryInternal, int64 | |||||
//LockRetryTimes int64 | |||||
} | |||||
} |
@@ -20,14 +20,7 @@ var ( | |||||
) | ) | ||||
type GettyRemotingClient struct { | type GettyRemotingClient struct { | ||||
//conf *config.ClientConfig | |||||
idGenerator *atomic.Uint32 | idGenerator *atomic.Uint32 | ||||
//futures *sync.Map | |||||
//mergeMsgMap *sync.Map | |||||
//rpcMessageChannel chan protocol.RpcMessage | |||||
//BranchCommitRequestChannel chan RpcRMMessage | |||||
//BranchRollbackRequestChannel chan RpcRMMessage | |||||
//GettySessionOnOpenChannel chan string | |||||
} | } | ||||
func GetGettyRemotingClient() *GettyRemotingClient { | func GetGettyRemotingClient() *GettyRemotingClient { | ||||
@@ -58,6 +51,17 @@ func (client *GettyRemotingClient) SendAsyncRequest(msg interface{}) error { | |||||
return GetGettyRemotingInstance().SendASync(rpcMessage) | return GetGettyRemotingInstance().SendASync(rpcMessage) | ||||
} | } | ||||
func (client *GettyRemotingClient) SendAsyncResponse(msg interface{}) error { | |||||
rpcMessage := protocol.RpcMessage{ | |||||
ID: int32(client.idGenerator.Inc()), | |||||
Type: protocol.MSGTypeResponse, | |||||
Codec: codec.SEATA, | |||||
Compressor: 0, | |||||
Body: msg, | |||||
} | |||||
return GetGettyRemotingInstance().SendASync(rpcMessage) | |||||
} | |||||
func (client *GettyRemotingClient) SendSyncRequest(msg interface{}) (interface{}, error) { | func (client *GettyRemotingClient) SendSyncRequest(msg interface{}) (interface{}, error) { | ||||
rpcMessage := protocol.RpcMessage{ | rpcMessage := protocol.RpcMessage{ | ||||
ID: int32(client.idGenerator.Inc()), | ID: int32(client.idGenerator.Inc()), | ||||
@@ -79,73 +83,3 @@ func (client *GettyRemotingClient) SendSyncRequestWithTimeout(msg interface{}, t | |||||
} | } | ||||
return GetGettyRemotingInstance().SendSyncWithTimeout(rpcMessage, timeout) | return GetGettyRemotingInstance().SendSyncWithTimeout(rpcMessage, timeout) | ||||
} | } | ||||
// | |||||
//func (client *GettyRemotingClient) RegisterResource(serverAddress string, request protocol.RegisterRMRequest) { | |||||
// session := clientSessionManager.AcquireGettySessionByServerAddress(serverAddress) | |||||
// if session != nil { | |||||
// err := client.sendAsyncRequestWithoutResponse(session, request) | |||||
// if err != nil { | |||||
// log.Errorf("register resource failed, session:{},resourceID:{}", session, request.ResourceIds) | |||||
// } | |||||
// } | |||||
//} | |||||
// | |||||
//func loadBalance(transactionServiceGroup string) string { | |||||
// addressList := getAddressList(transactionServiceGroup) | |||||
// if len(addressList) == 1 { | |||||
// return addressList[0] | |||||
// } | |||||
// return addressList[rand.Intn(len(addressList))] | |||||
//} | |||||
// | |||||
//func getAddressList(transactionServiceGroup string) []string { | |||||
// addressList := strings.Split(transactionServiceGroup, ",") | |||||
// return addressList | |||||
//} | |||||
// | |||||
//func (client *GettyRemotingClient) processMergedMessage() { | |||||
// ticker := time.NewTicker(5 * time.Millisecond) | |||||
// mergedMessage := protocol.MergedWarpMessage{ | |||||
// Msgs: make([]protocol.MessageTypeAware, 0), | |||||
// MsgIds: make([]int32, 0), | |||||
// } | |||||
// for { | |||||
// select { | |||||
// case rpcMessage := <-client.rpcMessageChannel: | |||||
// message := rpcMessage.Body.(protocol.MessageTypeAware) | |||||
// mergedMessage.Msgs = append(mergedMessage.Msgs, message) | |||||
// mergedMessage.MsgIds = append(mergedMessage.MsgIds, rpcMessage.ID) | |||||
// if len(mergedMessage.Msgs) == 20 { | |||||
// client.sendMergedMessage(mergedMessage) | |||||
// mergedMessage = protocol.MergedWarpMessage{ | |||||
// Msgs: make([]protocol.MessageTypeAware, 0), | |||||
// MsgIds: make([]int32, 0), | |||||
// } | |||||
// } | |||||
// case <-ticker.C: | |||||
// if len(mergedMessage.Msgs) > 0 { | |||||
// client.sendMergedMessage(mergedMessage) | |||||
// mergedMessage = protocol.MergedWarpMessage{ | |||||
// Msgs: make([]protocol.MessageTypeAware, 0), | |||||
// MsgIds: make([]int32, 0), | |||||
// } | |||||
// } | |||||
// } | |||||
// } | |||||
//} | |||||
// | |||||
//func (client *GettyRemotingClient) sendMergedMessage(mergedMessage protocol.MergedWarpMessage) { | |||||
// ss := clientSessionManager.AcquireGettySession() | |||||
// err := client.sendAsync(ss, mergedMessage) | |||||
// if err != nil { | |||||
// for _, id := range mergedMessage.MsgIds { | |||||
// resp, loaded := client.futures.Load(id) | |||||
// if loaded { | |||||
// response := resp.(*protocol.MessageFuture) | |||||
// response.Done <- true | |||||
// client.futures.Delete(id) | |||||
// } | |||||
// } | |||||
// } | |||||
//} |
@@ -60,6 +60,7 @@ func (client *GettyRemoting) SendASync(msg protocol.RpcMessage) error { | |||||
return err | return err | ||||
} | } | ||||
// TODO 待重构 | |||||
func (client *GettyRemoting) sendAsync(session getty.Session, msg protocol.RpcMessage, timeout time.Duration) (interface{}, error) { | func (client *GettyRemoting) sendAsync(session getty.Session, msg protocol.RpcMessage, timeout time.Duration) (interface{}, error) { | ||||
var err error | var err error | ||||
if session == nil || session.IsClosed() { | if session == nil || session.IsClosed() { | ||||
@@ -76,9 +77,15 @@ func (client *GettyRemoting) sendAsync(session getty.Session, msg protocol.RpcMe | |||||
log.Debugf("send message: %#v, session: %s", msg, session.Stat()) | log.Debugf("send message: %#v, session: %s", msg, session.Stat()) | ||||
if timeout > time.Duration(0) { | |||||
actualTimeOut := timeout | |||||
if timeout <= time.Duration(0) { | |||||
// todo timeoue use config | |||||
actualTimeOut = time.Duration(200) | |||||
} | |||||
wait := func() (interface{}, error) { | |||||
select { | select { | ||||
case <-gxtime.GetDefaultTimerWheel().After(timeout): | |||||
case <-gxtime.GetDefaultTimerWheel().After(actualTimeOut): | |||||
client.futures.Delete(msg.ID) | client.futures.Delete(msg.ID) | ||||
if session != nil { | if session != nil { | ||||
return nil, errors.Errorf("wait response timeout, ip: %s, request: %#v", session.RemoteAddr(), msg) | return nil, errors.Errorf("wait response timeout, ip: %s, request: %#v", session.RemoteAddr(), msg) | ||||
@@ -91,6 +98,11 @@ func (client *GettyRemoting) sendAsync(session getty.Session, msg protocol.RpcMe | |||||
} | } | ||||
} | } | ||||
if timeout > time.Duration(0) { | |||||
return wait() | |||||
} else { | |||||
go wait() | |||||
} | |||||
return nil, err | return nil, err | ||||
} | } | ||||
@@ -116,15 +128,15 @@ func (client *GettyRemoting) GetMergedMessage(msgID int32) *protocol.MergedWarpM | |||||
return nil | return nil | ||||
} | } | ||||
func (client *GettyRemoting) NotifytRpcMessageResponse(rpcMessage protocol.RpcMessage) { | |||||
func (client *GettyRemoting) NotifyRpcMessageResponse(rpcMessage protocol.RpcMessage) { | |||||
messageFuture := client.GetMessageFuture(rpcMessage.ID) | messageFuture := client.GetMessageFuture(rpcMessage.ID) | ||||
if messageFuture != nil { | if messageFuture != nil { | ||||
messageFuture.Response = rpcMessage.Body | messageFuture.Response = rpcMessage.Body | ||||
// todo messageFuture.Err怎么配置呢? | |||||
// todo add messageFuture.Err | |||||
//messageFuture.Err = rpcMessage.Err | //messageFuture.Err = rpcMessage.Err | ||||
messageFuture.Done <- true | messageFuture.Done <- true | ||||
//client.futures.Delete(rpcMessage.ID) | |||||
//client.msgFutures.Delete(rpcMessage.ID) | |||||
} else { | } else { | ||||
log.Infof("msg: {} is not found in futures.", rpcMessage.ID) | |||||
log.Infof("msg: {} is not found in msgFutures.", rpcMessage.ID) | |||||
} | } | ||||
} | } |
@@ -26,7 +26,7 @@ var ( | |||||
type gettyClientHandler struct { | type gettyClientHandler struct { | ||||
conf *config.ClientConfig | conf *config.ClientConfig | ||||
idGenerator *atomic.Uint32 | idGenerator *atomic.Uint32 | ||||
futures *sync.Map | |||||
msgFutures *sync.Map | |||||
mergeMsgMap *sync.Map | mergeMsgMap *sync.Map | ||||
processorTable map[protocol.MessageType]processor.RemotingProcessor | processorTable map[protocol.MessageType]processor.RemotingProcessor | ||||
} | } | ||||
@@ -37,7 +37,7 @@ func GetGettyClientHandlerInstance() *gettyClientHandler { | |||||
clientHandler = &gettyClientHandler{ | clientHandler = &gettyClientHandler{ | ||||
conf: config.GetDefaultClientConfig("seata-go"), | conf: config.GetDefaultClientConfig("seata-go"), | ||||
idGenerator: &atomic.Uint32{}, | idGenerator: &atomic.Uint32{}, | ||||
futures: &sync.Map{}, | |||||
msgFutures: &sync.Map{}, | |||||
mergeMsgMap: &sync.Map{}, | mergeMsgMap: &sync.Map{}, | ||||
processorTable: make(map[protocol.MessageType]processor.RemotingProcessor, 0), | processorTable: make(map[protocol.MessageType]processor.RemotingProcessor, 0), | ||||
} | } | ||||
@@ -46,41 +46,37 @@ func GetGettyClientHandlerInstance() *gettyClientHandler { | |||||
return clientHandler | return clientHandler | ||||
} | } | ||||
// OnOpen ... | |||||
func (client *gettyClientHandler) OnOpen(session getty.Session) error { | func (client *gettyClientHandler) OnOpen(session getty.Session) error { | ||||
clientSessionManager.RegisterGettySession(session) | clientSessionManager.RegisterGettySession(session) | ||||
go func() { | |||||
request := protocol.RegisterTMRequest{AbstractIdentifyRequest: protocol.AbstractIdentifyRequest{ | |||||
Version: client.conf.SeataVersion, | |||||
ApplicationId: client.conf.ApplicationID, | |||||
TransactionServiceGroup: client.conf.TransactionServiceGroup, | |||||
}} | |||||
err := GetGettyRemotingClient().SendAsyncRequest(request) | |||||
//client.sendAsyncRequestWithResponse(session, request, RPC_REQUEST_TIMEOUT) | |||||
if err != nil { | |||||
log.Error("OnOpen error: {%#v}", err.Error()) | |||||
clientSessionManager.ReleaseGettySession(session) | |||||
return | |||||
} | |||||
//todo | |||||
//client.GettySessionOnOpenChannel <- session.RemoteAddr() | |||||
}() | |||||
//go func() { | |||||
// request := protocol.RegisterTMRequest{AbstractIdentifyRequest: protocol.AbstractIdentifyRequest{ | |||||
// Version: client.conf.SeataVersion, | |||||
// ApplicationId: client.conf.ApplicationID, | |||||
// TransactionServiceGroup: client.conf.TransactionServiceGroup, | |||||
// }} | |||||
// err := GetGettyRemotingClient().SendAsyncRequest(request) | |||||
// //client.sendAsyncRequestWithResponse(session, request, RPC_REQUEST_TIMEOUT) | |||||
// if err != nil { | |||||
// log.Error("OnOpen error: {%#v}", err.Error()) | |||||
// clientSessionManager.ReleaseGettySession(session) | |||||
// return | |||||
// } | |||||
// | |||||
// //todo | |||||
// //client.GettySessionOnOpenChannel <- session.RemoteAddr() | |||||
//}() | |||||
return nil | return nil | ||||
} | } | ||||
// OnError ... | |||||
func (client *gettyClientHandler) OnError(session getty.Session, err error) { | func (client *gettyClientHandler) OnError(session getty.Session, err error) { | ||||
clientSessionManager.ReleaseGettySession(session) | clientSessionManager.ReleaseGettySession(session) | ||||
} | } | ||||
// OnClose ... | |||||
func (client *gettyClientHandler) OnClose(session getty.Session) { | func (client *gettyClientHandler) OnClose(session getty.Session) { | ||||
clientSessionManager.ReleaseGettySession(session) | clientSessionManager.ReleaseGettySession(session) | ||||
} | } | ||||
// OnMessage ... | |||||
func (client *gettyClientHandler) OnMessage(session getty.Session, pkg interface{}) { | func (client *gettyClientHandler) OnMessage(session getty.Session, pkg interface{}) { | ||||
// TODO 需要把session里面的关键信息存储到context中,以方便在后面流程中获取使用。比如,XID等等 | // TODO 需要把session里面的关键信息存储到context中,以方便在后面流程中获取使用。比如,XID等等 | ||||
ctx := context.Background() | ctx := context.Background() | ||||
@@ -104,9 +100,8 @@ func (client *gettyClientHandler) OnMessage(session getty.Session, pkg interface | |||||
} | } | ||||
} | } | ||||
// OnCron ... | |||||
func (client *gettyClientHandler) OnCron(session getty.Session) { | func (client *gettyClientHandler) OnCron(session getty.Session) { | ||||
//GetGettyRemotingClient().SendAsyncRequest(protocol.HeartBeatMessagePing) | |||||
// todo 发送心跳消息 | |||||
} | } | ||||
func (client *gettyClientHandler) RegisterProcessor(msgType protocol.MessageType, processor processor.RemotingProcessor) { | func (client *gettyClientHandler) RegisterProcessor(msgType protocol.MessageType, processor processor.RemotingProcessor) { | ||||
@@ -50,6 +50,7 @@ var ( | |||||
rpcPkgHandler = &RpcPackageHandler{} | rpcPkgHandler = &RpcPackageHandler{} | ||||
) | ) | ||||
// TODO 待重构 | |||||
var ( | var ( | ||||
ErrNotEnoughStream = errors.New("packet stream is not enough") | ErrNotEnoughStream = errors.New("packet stream is not enough") | ||||
ErrTooLargePackage = errors.New("package length is exceed the getty package's legal maximum length.") | ErrTooLargePackage = errors.New("package length is exceed the getty package's legal maximum length.") | ||||
@@ -20,8 +20,7 @@ import ( | |||||
type RpcClient struct { | type RpcClient struct { | ||||
conf *config.ClientConfig | conf *config.ClientConfig | ||||
gettyClients []getty.Client | gettyClients []getty.Client | ||||
//rpcHandler RpcRemoteClient | |||||
futures *sync.Map | |||||
futures *sync.Map | |||||
} | } | ||||
func init() { | func init() { | ||||
@@ -32,7 +31,6 @@ func newRpcClient() *RpcClient { | |||||
rpcClient := &RpcClient{ | rpcClient := &RpcClient{ | ||||
conf: config.GetClientConfig(), | conf: config.GetClientConfig(), | ||||
gettyClients: make([]getty.Client, 0), | gettyClients: make([]getty.Client, 0), | ||||
//rpcHandler: *InitRpcRemoteClient(), | |||||
} | } | ||||
rpcClient.init() | rpcClient.init() | ||||
return rpcClient | return rpcClient | ||||
@@ -57,17 +55,6 @@ func (c *RpcClient) init() { | |||||
// todo mock | // todo mock | ||||
func getAvailServerList(config *config.ClientConfig) []string { | func getAvailServerList(config *config.ClientConfig) []string { | ||||
//reg, err := extension.GetRegistry(config.RegistryConfig.Mode) | |||||
//if err != nil { | |||||
// logger.Errorf("Registry can not connect success, program is going to panic.Error message is %s", err.Error()) | |||||
// panic(err.Error()) | |||||
//} | |||||
//addrs, err := reg.Lookup() | |||||
//if err != nil { | |||||
// logger.Errorf("no hava valid server list", err.Error()) | |||||
// return nil | |||||
//} | |||||
//return addrs | |||||
return []string{"127.0.0.1:8091"} | return []string{"127.0.0.1:8091"} | ||||
} | } | ||||
@@ -1,10 +0,0 @@ | |||||
package getty | |||||
import ( | |||||
"github.com/seata/seata-go/pkg/protocol" | |||||
) | |||||
type RpcRMMessage struct { | |||||
RpcMessage protocol.RpcMessage | |||||
ServerAddress string | |||||
} |
@@ -17,6 +17,8 @@ func init() { | |||||
getty.GetGettyClientHandlerInstance().RegisterProcessor(protocol.MessageTypeBranchStatusReportResult, clientOnResponseProcessor) | getty.GetGettyClientHandlerInstance().RegisterProcessor(protocol.MessageTypeBranchStatusReportResult, clientOnResponseProcessor) | ||||
getty.GetGettyClientHandlerInstance().RegisterProcessor(protocol.MessageTypeGlobalLockQueryResult, clientOnResponseProcessor) | getty.GetGettyClientHandlerInstance().RegisterProcessor(protocol.MessageTypeGlobalLockQueryResult, clientOnResponseProcessor) | ||||
getty.GetGettyClientHandlerInstance().RegisterProcessor(protocol.MessageTypeRegRmResult, clientOnResponseProcessor) | getty.GetGettyClientHandlerInstance().RegisterProcessor(protocol.MessageTypeRegRmResult, clientOnResponseProcessor) | ||||
getty.GetGettyClientHandlerInstance().RegisterProcessor(protocol.MessageTypeGlobalBeginResult, clientOnResponseProcessor) | |||||
getty.GetGettyClientHandlerInstance().RegisterProcessor(protocol.MessageTypeGlobalCommitResult, clientOnResponseProcessor) | |||||
} | } | ||||
type clientOnResponseProcessor struct { | type clientOnResponseProcessor struct { | ||||
@@ -44,7 +46,7 @@ func (f *clientOnResponseProcessor) Process(ctx context.Context, rpcMessage prot | |||||
// 如果是请求消息,做处理逻辑 | // 如果是请求消息,做处理逻辑 | ||||
msgFuture := getty.GetGettyRemotingInstance().GetMessageFuture(rpcMessage.ID) | msgFuture := getty.GetGettyRemotingInstance().GetMessageFuture(rpcMessage.ID) | ||||
if msgFuture != nil { | if msgFuture != nil { | ||||
getty.GetGettyRemotingInstance().NotifytRpcMessageResponse(rpcMessage) | |||||
getty.GetGettyRemotingInstance().NotifyRpcMessageResponse(rpcMessage) | |||||
getty.GetGettyRemotingInstance().RemoveMessageFuture(rpcMessage.ID) | getty.GetGettyRemotingInstance().RemoveMessageFuture(rpcMessage.ID) | ||||
} else { | } else { | ||||
if _, ok := rpcMessage.Body.(protocol.AbstractResultMessage); ok { | if _, ok := rpcMessage.Body.(protocol.AbstractResultMessage); ok { | ||||
@@ -0,0 +1,48 @@ | |||||
package client | |||||
import ( | |||||
"context" | |||||
"github.com/seata/seata-go/pkg/protocol" | |||||
"github.com/seata/seata-go/pkg/rm" | |||||
"github.com/seata/seata-go/pkg/rpc/getty" | |||||
"github.com/seata/seata-go/pkg/utils/log" | |||||
) | |||||
func init() { | |||||
rmBranchCommitProcessor := &rmBranchCommitProcessor{} | |||||
getty.GetGettyClientHandlerInstance().RegisterProcessor(protocol.MessageTypeBranchCommit, rmBranchCommitProcessor) | |||||
} | |||||
type rmBranchCommitProcessor struct { | |||||
} | |||||
func (f *rmBranchCommitProcessor) Process(ctx context.Context, rpcMessage protocol.RpcMessage) error { | |||||
log.Infof("rm client handle branch commit process %v", rpcMessage) | |||||
request := rpcMessage.Body.(protocol.BranchCommitRequest) | |||||
xid := request.Xid | |||||
branchID := request.BranchId | |||||
resourceID := request.ResourceId | |||||
applicationData := request.ApplicationData | |||||
log.Infof("Branch committing: xid %s, branchID %s, resourceID %s, applicationData %s", xid, branchID, resourceID, applicationData) | |||||
status, err := rm.GetResourceManagerFacadeInstance().GetResourceManager(request.BranchType).BranchCommit(ctx, request.BranchType, xid, branchID, resourceID, applicationData) | |||||
if err != nil { | |||||
log.Infof("Branch commit error: %s", err.Error()) | |||||
return err | |||||
} | |||||
// reply commit response to tc server | |||||
response := protocol.BranchCommitResponse{ | |||||
AbstractBranchEndResponse: protocol.AbstractBranchEndResponse{ | |||||
Xid: xid, | |||||
BranchId: branchID, | |||||
BranchStatus: status, | |||||
}, | |||||
} | |||||
err = getty.GetGettyRemotingClient().SendAsyncResponse(response) | |||||
if err != nil { | |||||
log.Error("BranchCommitResponse error: {%#v}", err.Error()) | |||||
return err | |||||
} | |||||
return nil | |||||
} |
@@ -0,0 +1,48 @@ | |||||
package client | |||||
import ( | |||||
"context" | |||||
"github.com/seata/seata-go/pkg/protocol" | |||||
"github.com/seata/seata-go/pkg/rm" | |||||
"github.com/seata/seata-go/pkg/rpc/getty" | |||||
"github.com/seata/seata-go/pkg/utils/log" | |||||
) | |||||
func init() { | |||||
rmBranchCommitProcessor := &rmBranchCommitProcessor{} | |||||
getty.GetGettyClientHandlerInstance().RegisterProcessor(protocol.MessageTypeBranchCommit, rmBranchCommitProcessor) | |||||
} | |||||
type rmBranchRollbackProcessor struct { | |||||
} | |||||
func (f *rmBranchRollbackProcessor) Process(ctx context.Context, rpcMessage protocol.RpcMessage) error { | |||||
log.Infof("rm client handle branch commit process %v", rpcMessage) | |||||
request := rpcMessage.Body.(protocol.BranchCommitRequest) | |||||
xid := request.Xid | |||||
branchID := request.BranchId | |||||
resourceID := request.ResourceId | |||||
applicationData := request.ApplicationData | |||||
log.Infof("Branch committing: xid %s, branchID %s, resourceID %s, applicationData %s", xid, branchID, resourceID, applicationData) | |||||
status, err := rm.GetResourceManagerFacadeInstance().GetResourceManager(request.BranchType).BranchCommit(ctx, request.BranchType, xid, branchID, resourceID, applicationData) | |||||
if err != nil { | |||||
log.Infof("Branch commit error: %s", err.Error()) | |||||
return err | |||||
} | |||||
// reply commit response to tc server | |||||
response := protocol.BranchCommitResponse{ | |||||
AbstractBranchEndResponse: protocol.AbstractBranchEndResponse{ | |||||
Xid: xid, | |||||
BranchId: branchID, | |||||
BranchStatus: status, | |||||
}, | |||||
} | |||||
err = getty.GetGettyRemotingClient().SendAsyncResponse(response) | |||||
if err != nil { | |||||
log.Error("BranchCommitResponse error: {%#v}", err.Error()) | |||||
return err | |||||
} | |||||
return nil | |||||
} |
@@ -1,45 +0,0 @@ | |||||
package api | |||||
import ( | |||||
"github.com/seata/seata-go/pkg/common/model" | |||||
) | |||||
type GlobalTransactionRole int8 | |||||
const ( | |||||
LAUNCHER GlobalTransactionRole = 0 | |||||
PARTICIPANT GlobalTransactionRole = 1 | |||||
) | |||||
type GlobalTransaction interface { | |||||
// Begin a new global transaction with given timeout and given name. | |||||
begin(timeout int64, name string) error | |||||
// Commit the global transaction. | |||||
commit() error | |||||
// Rollback the global transaction. | |||||
rollback() error | |||||
// Suspend the global transaction. | |||||
suspend() (SuspendedResourcesHolder, error) | |||||
// Resume the global transaction. | |||||
resume(suspendedResourcesHolder SuspendedResourcesHolder) error | |||||
// Ask TC for current status of the corresponding global transaction. | |||||
getStatus() (model.GlobalStatus, error) | |||||
// Get XID. | |||||
getXid() string | |||||
// report the global transaction status. | |||||
globalReport(globalStatus model.GlobalStatus) error | |||||
// local status of the global transaction. | |||||
getLocalStatus() model.GlobalStatus | |||||
// get global transaction role. | |||||
getGlobalTransactionRole() GlobalTransactionRole | |||||
} |
@@ -0,0 +1,154 @@ | |||||
package tm | |||||
import ( | |||||
"context" | |||||
"fmt" | |||||
"github.com/pkg/errors" | |||||
"github.com/seata/seata-go/pkg/common/model" | |||||
"github.com/seata/seata-go/pkg/protocol" | |||||
"github.com/seata/seata-go/pkg/rpc/getty" | |||||
"github.com/seata/seata-go/pkg/tm/api" | |||||
"github.com/seata/seata-go/pkg/utils/log" | |||||
"sync" | |||||
) | |||||
type GlobalTransaction struct { | |||||
Xid string | |||||
Status model.GlobalStatus | |||||
Role model.GlobalTransactionRole | |||||
} | |||||
var ( | |||||
// singletone ResourceManagerFacade | |||||
globalTransactionManager *GlobalTransactionManager | |||||
onceGlobalTransactionManager = &sync.Once{} | |||||
) | |||||
func GetGlobalTransactionManager() *GlobalTransactionManager { | |||||
if globalTransactionManager == nil { | |||||
onceGlobalTransactionManager.Do(func() { | |||||
globalTransactionManager = &GlobalTransactionManager{} | |||||
}) | |||||
} | |||||
return globalTransactionManager | |||||
} | |||||
type GlobalTransactionManager struct { | |||||
} | |||||
// Begin a new global transaction with given timeout and given name. | |||||
func (g *GlobalTransactionManager) Begin(ctx context.Context, transaction *GlobalTransaction, timeout int32, name string) error { | |||||
if transaction.Role != model.LAUNCHER { | |||||
log.Infof("Ignore Begin(): just involved in global transaction %s", transaction.Xid) | |||||
return nil | |||||
} | |||||
if transaction.Xid != "" { | |||||
return errors.New(fmt.Sprintf("Global transaction already exists,can't begin a new global transaction, currentXid = %s ", transaction.Xid)) | |||||
} | |||||
req := protocol.GlobalBeginRequest{ | |||||
TransactionName: name, | |||||
Timeout: timeout, | |||||
} | |||||
res, err := getty.GetGettyRemotingClient().SendSyncRequest(req) | |||||
if err != nil { | |||||
log.Errorf("GlobalBeginRequest error, xid %s, error %v", transaction.Xid, err) | |||||
return err | |||||
} | |||||
if res == nil || res.(protocol.GlobalBeginResponse).ResultCode == protocol.ResultCodeFailed { | |||||
log.Errorf("GlobalBeginRequest error, xid %s, res %v", transaction.Xid, res) | |||||
return err | |||||
} | |||||
log.Infof("GlobalBeginRequest success, xid %s, res %v", transaction.Xid, res) | |||||
transaction.Status = model.Begin | |||||
transaction.Xid = res.(protocol.GlobalBeginResponse).Xid | |||||
model.SetXID(ctx, res.(protocol.GlobalBeginResponse).Xid) | |||||
return nil | |||||
} | |||||
// Commit the global transaction. | |||||
func (g *GlobalTransactionManager) Commit(ctx context.Context, transaction *GlobalTransaction) error { | |||||
if transaction.Role != model.LAUNCHER { | |||||
log.Infof("Ignore Commit(): just involved in global transaction [{}]", transaction.Xid) | |||||
return nil | |||||
} | |||||
if transaction.Xid == "" { | |||||
return errors.New("Commit xid should not be empty") | |||||
} | |||||
// todo: replace retry with config | |||||
var ( | |||||
err error | |||||
res interface{} | |||||
) | |||||
for retry := 5; retry > 0; retry-- { | |||||
req := protocol.GlobalCommitRequest{ | |||||
AbstractGlobalEndRequest: protocol.AbstractGlobalEndRequest{ | |||||
Xid: transaction.Xid, | |||||
}, | |||||
} | |||||
res, err = getty.GetGettyRemotingClient().SendSyncRequest(req) | |||||
if err != nil { | |||||
log.Errorf("GlobalCommitRequest error, xid %s, error %v", transaction.Xid, err) | |||||
} else { | |||||
break | |||||
} | |||||
} | |||||
if err == nil && res != nil { | |||||
transaction.Status = res.(protocol.GlobalCommitResponse).GlobalStatus | |||||
} | |||||
model.UnbindXid(ctx) | |||||
log.Infof("GlobalCommitRequest commit success, xid %s", transaction.Xid) | |||||
return err | |||||
} | |||||
// Rollback the global transaction. | |||||
func (g *GlobalTransactionManager) Rollback(ctx context.Context, transaction *GlobalTransaction) error { | |||||
if transaction.Role != model.LAUNCHER { | |||||
log.Infof("Ignore Commit(): just involved in global transaction [{}]", transaction.Xid) | |||||
return nil | |||||
} | |||||
if transaction.Xid == "" { | |||||
return errors.New("Commit xid should not be empty") | |||||
} | |||||
// todo: replace retry with config | |||||
var ( | |||||
err error | |||||
res interface{} | |||||
) | |||||
for retry := 5; retry > 0; retry-- { | |||||
req := protocol.GlobalRollbackRequest{ | |||||
AbstractGlobalEndRequest: protocol.AbstractGlobalEndRequest{ | |||||
Xid: transaction.Xid, | |||||
}, | |||||
} | |||||
res, err = getty.GetGettyRemotingClient().SendSyncRequest(req) | |||||
if err != nil { | |||||
log.Errorf("GlobalRollbackRequest error, xid %s, error %v", transaction.Xid, err) | |||||
} else { | |||||
break | |||||
} | |||||
} | |||||
if err == nil && res != nil { | |||||
transaction.Status = res.(protocol.GlobalRollbackResponse).GlobalStatus | |||||
} | |||||
model.UnbindXid(ctx) | |||||
return err | |||||
} | |||||
// Suspend the global transaction. | |||||
func (g *GlobalTransactionManager) Suspend() (api.SuspendedResourcesHolder, error) { | |||||
panic("implement me") | |||||
} | |||||
// Resume the global transaction. | |||||
func (g *GlobalTransactionManager) Resume(suspendedResourcesHolder api.SuspendedResourcesHolder) error { | |||||
panic("implement me") | |||||
} | |||||
// report the global transaction status. | |||||
func (g *GlobalTransactionManager) GlobalReport(globalStatus model.GlobalStatus) error { | |||||
panic("implement me") | |||||
} |
@@ -0,0 +1,7 @@ | |||||
package error | |||||
type ErrorCode int32 | |||||
const ( | |||||
ErrorCode_IllegalState ErrorCode = 40001 | |||||
) |
@@ -0,0 +1,6 @@ | |||||
package utils | |||||
func GetLocalIp() string { | |||||
// todo | |||||
return "127.0.0.1" | |||||
} |
@@ -2,14 +2,11 @@ package xid_utils | |||||
import ( | import ( | ||||
"context" | "context" | ||||
) | |||||
import ( | |||||
"github.com/seata/seata-go/pkg/common" | |||||
"github.com/seata/seata-go/pkg/common/model" | |||||
) | ) | ||||
func GetXID(ctx context.Context) string { | func GetXID(ctx context.Context) string { | ||||
xid := ctx.Value(common.XID) | |||||
xid := ctx.Value(model.XID) | |||||
if xid == nil { | if xid == nil { | ||||
return "" | return "" | ||||
} | } | ||||
@@ -21,5 +18,5 @@ func HasXID(ctx context.Context) bool { | |||||
} | } | ||||
func SetXID(ctx context.Context, xid string) context.Context { | func SetXID(ctx context.Context, xid string) context.Context { | ||||
return context.WithValue(ctx, common.XID, xid) | |||||
return context.WithValue(ctx, model.XID, xid) | |||||
} | } |
@@ -0,0 +1,49 @@ | |||||
package test | |||||
import ( | |||||
"context" | |||||
"github.com/seata/seata-go/pkg/common/model" | |||||
_ "github.com/seata/seata-go/pkg/imports" | |||||
"github.com/seata/seata-go/pkg/rm/tcc" | |||||
"github.com/seata/seata-go/pkg/rm/tcc/api" | |||||
"github.com/seata/seata-go/pkg/rm/tcc/remoting" | |||||
"github.com/seata/seata-go/pkg/utils/log" | |||||
"testing" | |||||
) | |||||
type TestTCCServiceBusiness struct { | |||||
} | |||||
func (T TestTCCServiceBusiness) Prepare(ctx context.Context, params interface{}) error { | |||||
log.Infof("TestTCCServiceBusiness Prepare, param %v", params) | |||||
return nil | |||||
} | |||||
func (T TestTCCServiceBusiness) Commit(ctx context.Context, businessActionContext api.BusinessActionContext) error { | |||||
log.Infof("TestTCCServiceBusiness Commit, param %v", businessActionContext) | |||||
return nil | |||||
} | |||||
func (T TestTCCServiceBusiness) Rollback(ctx context.Context, businessActionContext api.BusinessActionContext) error { | |||||
log.Infof("TestTCCServiceBusiness Rollback, param %v", businessActionContext) | |||||
return nil | |||||
} | |||||
func (T TestTCCServiceBusiness) GetActionName() string { | |||||
return "TestTCCServiceBusiness" | |||||
} | |||||
func (T TestTCCServiceBusiness) GetRemoteType() remoting.RemoteType { | |||||
return remoting.RemoteTypeLocalService | |||||
} | |||||
func (T TestTCCServiceBusiness) GetServiceType() remoting.ServiceType { | |||||
return remoting.ServiceTypeProvider | |||||
} | |||||
func TestNew(test *testing.T) { | |||||
tccService := tcc.NewTCCServiceProxy(TestTCCServiceBusiness{}) | |||||
tccService.Prepare(model.InitSeataContext(context.Background()), 1) | |||||
//time.Sleep(time.Second * 1000) | |||||
} |
@@ -3,10 +3,10 @@ package mock | |||||
import ( | import ( | ||||
"context" | "context" | ||||
"fmt" | "fmt" | ||||
"github.com/seata/seata-go/pkg/rm/tcc/api" | |||||
) | ) | ||||
import ( | import ( | ||||
"github.com/seata/seata-go/pkg/rm/api" | |||||
"github.com/seata/seata-go/pkg/rm/tcc/remoting" | "github.com/seata/seata-go/pkg/rm/tcc/remoting" | ||||
_ "github.com/seata/seata-go/pkg/utils/xid" | _ "github.com/seata/seata-go/pkg/utils/xid" | ||||
xid_utils "github.com/seata/seata-go/pkg/utils/xid" | xid_utils "github.com/seata/seata-go/pkg/utils/xid" | ||||