@@ -0,0 +1,8 @@ | |||
# Default ignored files | |||
/shelf/ | |||
/workspace.xml | |||
# Editor-based HTTP Client requests | |||
/httpRequests/ | |||
# Datasource local storage ignored files | |||
/dataSources/ | |||
/dataSources.local.xml |
@@ -0,0 +1,15 @@ | |||
<?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> |
@@ -0,0 +1,8 @@ | |||
<?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> |
@@ -0,0 +1,9 @@ | |||
<?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> |
@@ -0,0 +1,6 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<project version="4"> | |||
<component name="VcsDirectoryMappings"> | |||
<mapping directory="$PROJECT_DIR$" vcs="Git" /> | |||
</component> | |||
</project> |
@@ -0,0 +1,5 @@ | |||
package main | |||
func main() { | |||
// start the server | |||
} |
@@ -0,0 +1,37 @@ | |||
# 配置类;io.seata.spring.boot.autoconfigure.StarterConstants | |||
seata: | |||
transport: | |||
type: "TCP" | |||
#NIO NATIVE | |||
server: "NIO" | |||
#enable heartbeat | |||
heartbeat: true | |||
# the client batch send request enable | |||
enableClientBatchSendRequest: true | |||
compressor: nome | |||
service: | |||
client: | |||
rm: | |||
asyncCommitBufferLimit: 10000 | |||
reportRetryCount: 5 | |||
tableMetaCheckEnable: false | |||
reportSuccessEnable: false | |||
sagaBranchRegisterEnable: 10000 | |||
sagaJsonParser: fastjson | |||
sagaRetryPersistModeUpdate: false | |||
sagaCompensatePersistModeUpdate: false | |||
tm: | |||
commitRetryCount: 5 | |||
rollbackRetryCount: 5 | |||
defaultGlobalTransactionTimeout: 60000 | |||
degradeCheck: false | |||
degradeCheckAllowTimes: 10 | |||
degradeCheckPeriod: 2000 | |||
undo: | |||
dataValidation: true | |||
logSerialization: jackson | |||
logTable: undo_log | |||
onlyCareUpdateColumns: true |
@@ -0,0 +1,4 @@ | |||
mode: atomic | |||
github.com/seata/seata-go/pkg/rm/tcc/tcc_resource.go:20.51,22.2 1 0 | |||
github.com/seata/seata-go/pkg/rm/tcc/tcc_resource.go:24.46,26.2 1 0 | |||
github.com/seata/seata-go/pkg/rm/tcc/tcc_resource.go:28.56,30.2 1 0 |
@@ -1,3 +1,18 @@ | |||
module github.com/seata/seata-go | |||
go 1.16 | |||
require ( | |||
github.com/BurntSushi/toml v1.1.0 // indirect | |||
github.com/apache/dubbo-getty v1.4.8 | |||
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/natefinch/lumberjack v2.0.0+incompatible | |||
github.com/pkg/errors v0.9.1 | |||
go.uber.org/atomic v1.9.0 | |||
go.uber.org/zap v1.19.1 | |||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect | |||
vimagination.zapto.org/byteio v0.0.0-20200222190125-d27cba0f0b10 | |||
vimagination.zapto.org/memio v0.0.0-20200222190306-588ebc67b97d // indirect | |||
) |
@@ -0,0 +1,58 @@ | |||
VERSION=$(shell cat "./VERSION" 2> /dev/null) | |||
GO_FLAGS := -ldflags "-X main.Branch=$(GIT_BRANCH) -X main.Revision=$(GIT_REVISION) -X main.Version=$(VERSION) -extldflags \"-static\" -s -w" -tags netgo | |||
GO = go | |||
GO_PATH = $(shell $(GO) env GOPATH) | |||
GO_OS = $(shell $(GO) env GOOS) | |||
ifeq ($(GO_OS), darwin) | |||
GO_OS = mac | |||
endif | |||
# License environment | |||
GO_LICENSE_CHECKER_DIR = license-header-checker-$(GO_OS) | |||
GO_LICENSE_CHECKER = $(GO_PATH)/bin/license-header-checker | |||
LICENSE_DIR = /tmp/tools/license | |||
# format import code | |||
format-import: | |||
go get -d github.com/dubbogo/tools/cmd/imports-formatter | |||
imports-formatter -path . -module github.com/seata/seata-go -bl false | |||
unit-test: | |||
go test ./pkg/... -coverprofile=coverage.txt -covermode=atomic | |||
# Generate binaries for a Cortex release | |||
dist dist/seatago-linux-amd64 dist/seatago-darwin-amd64 dist/seatago-linux-amd64-sha-256 dist/seatago-darwin-amd64-sha-256: | |||
rm -fr ./dist | |||
mkdir -p ./dist | |||
GOOS="linux" GOARCH="amd64" CGO_ENABLED=0 go build $(GO_FLAGS) -o ./dist/seatago-linux-amd64 ./cmd | |||
GOOS="darwin" GOARCH="amd64" CGO_ENABLED=0 go build $(GO_FLAGS) -o ./dist/seatago-darwin-amd64 ./cmd | |||
sha256sum ./dist/seatago-darwin-amd64 | cut -d ' ' -f 1 > ./dist/seatago-darwin-amd64-sha-256 | |||
sha256sum ./dist/seatago-linux-amd64 | cut -d ' ' -f 1 > ./dist/seatago-linux-amd64-sha-256 | |||
# Generate binaries for a Cortex release | |||
build dist/seatago dist/seatago-sha-256: | |||
rm -fr ./dist | |||
mkdir -p ./dist | |||
CGO_ENABLED=0 go build $(GO_FLAGS) -o ./dist/seatago ./cmd | |||
sha256sum ./dist/seatago | cut -d ' ' -f 1 > ./dist/seatago-sha-256 | |||
#docker-build: | |||
# docker build -t seatago/seatago:latest . | |||
integration-test: | |||
@go clean -testcache | |||
go test -tags integration -v ./test/... | |||
clean: | |||
@rm -rf coverage.txt | |||
@rm -rf dist | |||
prepareLic: | |||
echo 'The makefile is for ci test and has dependencies. Do not run it locally. If you want to run the unit tests, run command `go test ./...` directly.' | |||
$(GO_LICENSE_CHECKER) -version || (wget https://github.com/lsm-dev/license-header-checker/releases/download/v1.2.0/$(GO_LICENSE_CHECKER_DIR).zip -O $(GO_LICENSE_CHECKER_DIR).zip && unzip -o $(GO_LICENSE_CHECKER_DIR).zip && mkdir -p $(GO_PATH)/bin/ && cp $(GO_LICENSE_CHECKER_DIR)/64bit/license-header-checker $(GO_PATH)/bin/) | |||
ls /tmp/tools/license/license.txt || wget -P $(LICENSE_DIR) https://github.com/dubbogo/resources/raw/master/tools/license/license.txt | |||
.PHONY: license | |||
license: prepareLic | |||
$(GO_LICENSE_CHECKER) -v -a -r -i vendor $(LICENSE_DIR)/license.txt . go && [[ -z `git status -s` ]] |
@@ -0,0 +1,51 @@ | |||
package config | |||
import ( | |||
"time" | |||
) | |||
var clientConfig *ClientConfig | |||
type ClientConfig struct { | |||
ApplicationID string `yaml:"application_id" json:"application_id,omitempty"` | |||
TransactionServiceGroup string `yaml:"transaction_service_group" json:"transaction_service_group,omitempty"` | |||
EnableClientBatchSendRequest bool `yaml:"enable-rpc_client-batch-send-request" json:"enable-rpc_client-batch-send-request,omitempty"` | |||
SeataVersion string `yaml:"seata_version" json:"seata_version,omitempty"` | |||
GettyConfig GettyConfig `yaml:"getty" json:"getty,omitempty"` | |||
TMConfig TMConfig `yaml:"tm" json:"tm,omitempty"` | |||
ATConfig struct { | |||
DSN string `yaml:"dsn" json:"dsn,omitempty"` | |||
ReportRetryCount int `default:"5" yaml:"report_retry_count" json:"report_retry_count,omitempty"` | |||
ReportSuccessEnable bool `default:"false" yaml:"report_success_enable" json:"report_success_enable,omitempty"` | |||
LockRetryInterval time.Duration `default:"10ms" yaml:"lock_retry_interval" json:"lock_retry_interval,omitempty"` | |||
LockRetryTimes int `default:"30" yaml:"lock_retry_times" json:"lock_retry_times,omitempty"` | |||
} `yaml:"at" json:"at,omitempty"` | |||
RegistryConfig RegistryConfig `yaml:"registry_config" json:"registry_config,omitempty"` //注册中心配置信息 | |||
ConfigCenterConfig ConfigCenterConfig `yaml:"config_center" json:"config_center,omitempty"` //配置中心配置信息 | |||
} | |||
func GetClientConfig() *ClientConfig { | |||
// todo mock data | |||
//return clientConfig | |||
return &ClientConfig{ | |||
GettyConfig: GetDefaultGettyConfig(), | |||
} | |||
} | |||
func GetTMConfig() TMConfig { | |||
return clientConfig.TMConfig | |||
} | |||
func GetDefaultClientConfig(applicationID string) ClientConfig { | |||
return ClientConfig{ | |||
ApplicationID: applicationID, | |||
TransactionServiceGroup: "127.0.0.1:8091", | |||
EnableClientBatchSendRequest: false, | |||
SeataVersion: "1.1.0", | |||
GettyConfig: GetDefaultGettyConfig(), | |||
TMConfig: GetDefaultTmConfig(), | |||
} | |||
} |
@@ -0,0 +1,31 @@ | |||
package config | |||
import ( | |||
"time" | |||
) | |||
// ConfigCenterConfig config center config | |||
type ConfigCenterConfig struct { | |||
Mode string `yaml:"type" json:"type,omitempty"` //类型 | |||
NacosConfig NacosConfigCenter `yaml:"nacos" json:"nacos,omitempty"` | |||
ETCDConfig EtcdConfigCenter `yaml:"etcdv3" json:"etcdv3,omitempty"` | |||
} | |||
// NacosConfigCenter nacos config center | |||
type NacosConfigCenter struct { | |||
ServerAddr string `yaml:"server_addr" json:"server_addr,omitempty"` | |||
Group string `default:"SEATA_GROUP" yaml:"group" json:"group,omitempty"` | |||
Namespace string `yaml:"namespace" json:"namespace,omitempty"` | |||
Cluster string `yaml:"cluster" json:"cluster,omitempty"` | |||
UserName string `yaml:"username" json:"username,omitempty"` | |||
Password string `yaml:"password" json:"password,omitempty"` | |||
DataID string `default:"seata" yaml:"data_id" json:"data_id,omitempty"` | |||
} | |||
type EtcdConfigCenter struct { | |||
Name string `default:"seata-config-center" yaml:"name" json:"name"` | |||
ConfigKey string `default:"config-seata" yaml:"config_key" json:"config_key,omitempty"` | |||
Endpoints string `yaml:"endpoints" json:"endpoints,omitempty"` | |||
Heartbeats int `yaml:"heartbeats" json:"heartbeats"` | |||
Timeout time.Duration `yaml:"timeout" json:"timeout"` | |||
} |
@@ -0,0 +1,56 @@ | |||
package config | |||
import ( | |||
"time" | |||
) | |||
// GettyConfig | |||
//Config holds supported types by the multiconfig package | |||
type GettyConfig struct { | |||
ReconnectInterval int `default:"0" yaml:"reconnect_interval" json:"reconnect_interval,omitempty"` | |||
// getty_session pool | |||
ConnectionNum int `default:"16" yaml:"connection_number" json:"connection_number,omitempty"` | |||
// heartbeat | |||
HeartbeatPeriod time.Duration `default:"15s" yaml:"heartbeat_period" json:"heartbeat_period,omitempty"` | |||
// getty_session tcp parameters | |||
GettySessionParam GettySessionParam `required:"true" yaml:"getty_session_param" json:"getty_session_param,omitempty"` | |||
} | |||
// GetDefaultGettyConfig ... | |||
func GetDefaultGettyConfig() GettyConfig { | |||
return GettyConfig{ | |||
ReconnectInterval: 0, | |||
ConnectionNum: 2, | |||
HeartbeatPeriod: 10 * time.Second, | |||
GettySessionParam: GettySessionParam{ | |||
CompressEncoding: false, | |||
TCPNoDelay: true, | |||
TCPKeepAlive: true, | |||
KeepAlivePeriod: 180 * time.Second, | |||
TCPRBufSize: 262144, | |||
TCPWBufSize: 65536, | |||
TCPReadTimeout: time.Second, | |||
TCPWriteTimeout: 5 * time.Second, | |||
WaitTimeout: time.Second, | |||
MaxMsgLen: 4096, | |||
SessionName: "rpc_client", | |||
}, | |||
} | |||
} | |||
// GettySessionParam getty session param | |||
type GettySessionParam struct { | |||
CompressEncoding bool `default:"false" yaml:"compress_encoding" json:"compress_encoding,omitempty"` | |||
TCPNoDelay bool `default:"true" yaml:"tcp_no_delay" json:"tcp_no_delay,omitempty"` | |||
TCPKeepAlive bool `default:"true" yaml:"tcp_keep_alive" json:"tcp_keep_alive,omitempty"` | |||
KeepAlivePeriod time.Duration `default:"180s" yaml:"keep_alive_period" json:"keep_alive_period,omitempty"` | |||
TCPRBufSize int `default:"262144" yaml:"tcp_r_buf_size" json:"tcp_r_buf_size,omitempty"` | |||
TCPWBufSize int `default:"65536" yaml:"tcp_w_buf_size" json:"tcp_w_buf_size,omitempty"` | |||
TCPReadTimeout time.Duration `default:"1s" yaml:"tcp_read_timeout" json:"tcp_read_timeout,omitempty"` | |||
TCPWriteTimeout time.Duration `default:"5s" yaml:"tcp_write_timeout" json:"tcp_write_timeout,omitempty"` | |||
WaitTimeout time.Duration `default:"7s" yaml:"wait_timeout" json:"wait_timeout,omitempty"` | |||
MaxMsgLen int `default:"4096" yaml:"max_msg_len" json:"max_msg_len,omitempty"` | |||
SessionName string `default:"rpc" yaml:"session_name" json:"session_name,omitempty"` | |||
} |
@@ -0,0 +1,42 @@ | |||
package config | |||
import ( | |||
"time" | |||
) | |||
var config *RegistryConfig | |||
// RegistryConfig registry config | |||
type RegistryConfig struct { | |||
Mode string `yaml:"type" json:"type,omitempty"` //类型 | |||
NacosConfig NacosConfig `yaml:"nacos" json:"nacos,omitempty"` | |||
EtcdConfig EtcdConfig `yaml:"etcdv3" json:"etcdv3"` | |||
} | |||
// NacosConfig nacos config | |||
type NacosConfig struct { | |||
Application string `yaml:"application" json:"application,omitempty"` | |||
ServerAddr string `yaml:"server_addr" json:"server_addr,omitempty"` | |||
Group string `default:"SEATA_GROUP" yaml:"group" json:"group,omitempty"` | |||
Namespace string `yaml:"namespace" json:"namespace,omitempty"` | |||
Cluster string `yaml:"cluster" json:"cluster,omitempty"` | |||
UserName string `yaml:"username" json:"username,omitempty"` | |||
Password string `yaml:"password" json:"password,omitempty"` | |||
} | |||
// InitRegistryConfig init registry config | |||
func InitRegistryConfig(registryConfig *RegistryConfig) { | |||
config = registryConfig | |||
} | |||
// GetRegistryConfig get registry config | |||
func GetRegistryConfig() *RegistryConfig { | |||
return config | |||
} | |||
type EtcdConfig struct { | |||
ClusterName string `default:"seata-golang-etcdv3" yaml:"cluster_name" json:"cluster_name,omitempty"` | |||
Endpoints string `yaml:"endpoints" json:"endpoints,omitempty"` | |||
Heartbeats int `yaml:"heartbeats" json:"heartbeats"` | |||
Timeout time.Duration `yaml:"timeout" json:"timeout"` | |||
} |
@@ -0,0 +1,13 @@ | |||
package config | |||
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"` | |||
} | |||
func GetDefaultTmConfig() TMConfig { | |||
return TMConfig{ | |||
CommitRetryCount: 5, | |||
RollbackRetryCount: 5, | |||
} | |||
} |
@@ -0,0 +1,112 @@ | |||
package model | |||
import ( | |||
"fmt" | |||
) | |||
type BranchType int8 | |||
type BranchStatus int8 | |||
const ( | |||
AT BranchType = 0 | |||
TCC BranchType = 1 | |||
SAGA BranchType = 2 | |||
XA BranchType = 3 | |||
) | |||
const ( | |||
/** | |||
* The BranchStatus_Unknown. | |||
* description:BranchStatus_Unknown branch status. | |||
*/ | |||
BranchStatusUnknown BranchStatus = iota | |||
/** | |||
* The BranchStatus_Registered. | |||
* description:BranchStatus_Registered to TC. | |||
*/ | |||
BranchStatusRegistered | |||
/** | |||
* The Phase one done. | |||
* description:Branch logic is successfully done at phase one. | |||
*/ | |||
BranchStatusPhaseoneDone | |||
/** | |||
* The Phase one failed. | |||
* description:Branch logic is failed at phase one. | |||
*/ | |||
BranchStatusPhaseoneFailed | |||
/** | |||
* The Phase one timeout. | |||
* description:Branch logic is NOT reported for a timeout. | |||
*/ | |||
BranchStatusPhaseoneTimeout | |||
/** | |||
* The Phase two committed. | |||
* description:Commit logic is successfully done at phase two. | |||
*/ | |||
BranchStatusPhasetwoCommitted | |||
/** | |||
* The Phase two commit failed retryable. | |||
* description:Commit logic is failed but retryable. | |||
*/ | |||
BranchStatusPhasetwoCommitFailedRetryable | |||
/** | |||
* The Phase two commit failed unretryable. | |||
* description:Commit logic is failed and NOT retryable. | |||
*/ | |||
BranchStatusPhasetwoCommitFailedUnretryable | |||
/** | |||
* The Phase two rollbacked. | |||
* description:Rollback logic is successfully done at phase two. | |||
*/ | |||
BranchStatusPhasetwoRollbacked | |||
/** | |||
* The Phase two rollback failed retryable. | |||
* description:Rollback logic is failed but retryable. | |||
*/ | |||
BranchStatusPhasetwoRollbackFailedRetryable | |||
/** | |||
* The Phase two rollback failed unretryable. | |||
* description:Rollback logic is failed but NOT retryable. | |||
*/ | |||
BranchStatusPhasetwoRollbackFailedUnretryable | |||
) | |||
func (s BranchStatus) String() string { | |||
switch s { | |||
case BranchStatusUnknown: | |||
return "Unknown" | |||
case BranchStatusRegistered: | |||
return "Registered" | |||
case BranchStatusPhaseoneDone: | |||
return "PhaseoneDone" | |||
case BranchStatusPhaseoneFailed: | |||
return "PhaseoneFailed" | |||
case BranchStatusPhaseoneTimeout: | |||
return "PhaseoneTimeout" | |||
case BranchStatusPhasetwoCommitted: | |||
return "PhasetwoCommitted" | |||
case BranchStatusPhasetwoCommitFailedRetryable: | |||
return "PhasetwoCommitFailedRetryable" | |||
case BranchStatusPhasetwoCommitFailedUnretryable: | |||
return "CommitFailedUnretryable" | |||
case BranchStatusPhasetwoRollbacked: | |||
return "PhasetwoRollbacked" | |||
case BranchStatusPhasetwoRollbackFailedRetryable: | |||
return "RollbackFailedRetryable" | |||
case BranchStatusPhasetwoRollbackFailedUnretryable: | |||
return "RollbackFailedUnretryable" | |||
default: | |||
return fmt.Sprintf("%d", s) | |||
} | |||
} |
@@ -0,0 +1,102 @@ | |||
package model | |||
type GlobalStatus int64 | |||
const ( | |||
/** | |||
* Un known global status. | |||
*/ | |||
// Unknown | |||
UnKnown GlobalStatus = 0 | |||
/** | |||
* The Begin. | |||
*/ | |||
// PHASE 1: can accept new branch registering. | |||
Begin GlobalStatus = 1 | |||
/** | |||
* PHASE 2: Running Status: may be changed any time. | |||
*/ | |||
// Committing. | |||
Committing GlobalStatus = 2 | |||
/** | |||
* The Commit retrying. | |||
*/ | |||
// Retrying commit after a recoverable failure. | |||
CommitRetrying GlobalStatus = 3 | |||
/** | |||
* Rollbacking global status. | |||
*/ | |||
// Rollbacking | |||
Rollbacking GlobalStatus = 4 | |||
/** | |||
* The Rollback retrying. | |||
*/ | |||
// Retrying rollback after a recoverable failure. | |||
RollbackRetrying GlobalStatus = 5 | |||
/** | |||
* The Timeout rollbacking. | |||
*/ | |||
// Rollbacking since timeout | |||
TimeoutRollbacking GlobalStatus = 6 | |||
/** | |||
* The Timeout rollback retrying. | |||
*/ | |||
// Retrying rollback GlobalStatus = since timeout) after a recoverable failure. | |||
TimeoutRollbackRetrying GlobalStatus = 7 | |||
/** | |||
* All branches can be async committed. The committing is NOT done yet, but it can be seen as committed for TM/RM | |||
* client. | |||
*/ | |||
AsyncCommitting GlobalStatus = 8 | |||
/** | |||
* PHASE 2: Final Status: will NOT change any more. | |||
*/ | |||
// Finally: global transaction is successfully committed. | |||
Committed GlobalStatus = 9 | |||
/** | |||
* The Commit failed. | |||
*/ | |||
// Finally: failed to commit | |||
CommitFailed GlobalStatus = 10 | |||
/** | |||
* The Rollbacked. | |||
*/ | |||
// Finally: global transaction is successfully rollbacked. | |||
Rollbacked GlobalStatus = 11 | |||
/** | |||
* The Rollback failed. | |||
*/ | |||
// Finally: failed to rollback | |||
RollbackFailed GlobalStatus = 12 | |||
/** | |||
* The Timeout rollbacked. | |||
*/ | |||
// Finally: global transaction is successfully rollbacked since timeout. | |||
TimeoutRollbacked GlobalStatus = 13 | |||
/** | |||
* The Timeout rollback failed. | |||
*/ | |||
// Finally: failed to rollback since timeout | |||
TimeoutRollbackFailed GlobalStatus = 14 | |||
/** | |||
* The Finished. | |||
*/ | |||
// Not managed in session MAP any more | |||
Finished GlobalStatus = 15 | |||
) |
@@ -0,0 +1,41 @@ | |||
package model | |||
// Resource that can be managed by Resource Manager and involved into global transaction | |||
type Resource interface { | |||
GetResourceGroupId() string | |||
GetResourceId() string | |||
GetBranchType() BranchType | |||
} | |||
// Control a branch transaction commit or rollback | |||
type ResourceManagerInbound interface { | |||
// Commit a branch transaction | |||
BranchCommit(branchType BranchType, xid, branchId int64, resourceId, applicationData string) (BranchStatus, error) | |||
// Rollback a branch transaction | |||
BranchRollback(branchType BranchType, xid string, branchId int64, resourceId, applicationData string) (BranchStatus, error) | |||
} | |||
// Resource Manager: send outbound request to TC | |||
type ResourceManagerOutbound interface { | |||
// Branch register long | |||
BranchRegister(branchType BranchType, resourceId, clientId, xid, applicationData, lockKeys string) (int64, error) | |||
// Branch report | |||
BranchReport(branchType BranchType, xid string, branchId int64, status BranchStatus, applicationData string) error | |||
// Lock query boolean | |||
LockQuery(branchType BranchType, resourceId, xid, lockKeys string) (bool, error) | |||
} | |||
// Resource Manager: common behaviors | |||
type ResourceManager interface { | |||
ResourceManagerInbound | |||
ResourceManagerOutbound | |||
// Register a Resource to be managed by Resource Manager | |||
RegisterResource(resource Resource) error | |||
// Unregister a Resource from the Resource Manager | |||
UnregisterResource(resource Resource) error | |||
// Get all resources managed by this manager | |||
GetManagedResources() map[string]Resource | |||
// Get the BranchType | |||
GetBranchType() BranchType | |||
} |
@@ -0,0 +1,110 @@ | |||
package model | |||
type TransactionExceptionCode byte | |||
const ( | |||
/** | |||
* Unknown transaction exception code. | |||
*/ | |||
TransactionExceptionCodeUnknown TransactionExceptionCode = iota | |||
/** | |||
* BeginFailed | |||
*/ | |||
TransactionExceptionCodeBeginFailed | |||
/** | |||
* Lock key conflict transaction exception code. | |||
*/ | |||
TransactionExceptionCodeLockKeyConflict | |||
/** | |||
* Io transaction exception code. | |||
*/ | |||
IO | |||
/** | |||
* Branch rollback failed retriable transaction exception code. | |||
*/ | |||
TransactionExceptionCodeBranchRollbackFailedRetriable | |||
/** | |||
* Branch rollback failed unretriable transaction exception code. | |||
*/ | |||
TransactionExceptionCodeBranchRollbackFailedUnretriable | |||
/** | |||
* Branch register failed transaction exception code. | |||
*/ | |||
TransactionExceptionCodeBranchRegisterFailed | |||
/** | |||
* Branch report failed transaction exception code. | |||
*/ | |||
TransactionExceptionCodeBranchReportFailed | |||
/** | |||
* Lockable check failed transaction exception code. | |||
*/ | |||
TransactionExceptionCodeLockableCheckFailed | |||
/** | |||
* Branch transaction not exist transaction exception code. | |||
*/ | |||
TransactionExceptionCodeBranchTransactionNotExist | |||
/** | |||
* Global transaction not exist transaction exception code. | |||
*/ | |||
TransactionExceptionCodeGlobalTransactionNotExist | |||
/** | |||
* Global transaction not active transaction exception code. | |||
*/ | |||
TransactionExceptionCodeGlobalTransactionNotActive | |||
/** | |||
* Global transaction status invalid transaction exception code. | |||
*/ | |||
TransactionExceptionCodeGlobalTransactionStatusInvalid | |||
/** | |||
* Failed to send branch commit request transaction exception code. | |||
*/ | |||
TransactionExceptionCodeFailedToSendBranchCommitRequest | |||
/** | |||
* Failed to send branch rollback request transaction exception code. | |||
*/ | |||
TransactionExceptionCodeFailedToSendBranchRollbackRequest | |||
/** | |||
* Failed to add branch transaction exception code. | |||
*/ | |||
TransactionExceptionCodeFailedToAddBranch | |||
/** | |||
* Failed to lock global transaction exception code. | |||
*/ | |||
TransactionExceptionCodeFailedLockGlobalTranscation | |||
/** | |||
* FailedWriteSession | |||
*/ | |||
TransactionExceptionCodeFailedWriteSession | |||
/** | |||
* Failed to holder exception code | |||
*/ | |||
FailedStore | |||
) | |||
type TransactionException struct { | |||
Code TransactionExceptionCode | |||
Message string | |||
} | |||
//Error 隐式继承 builtin.error 接口 | |||
func (e TransactionException) Error() string { | |||
return "TransactionException: " + e.Message | |||
} |
@@ -0,0 +1,18 @@ | |||
package model | |||
type TransactionManager interface { | |||
// Begin a new global transaction. | |||
Begin(applicationId, transactionServiceGroup, name string, timeout int64) (string, error) | |||
// Global commit. | |||
Commit(xid string) (GlobalStatus, error) | |||
//Global rollback. | |||
Rollback(xid string) (GlobalStatus, error) | |||
// Get current status of the give transaction. | |||
GetStatus(xid string) (GlobalStatus, error) | |||
// Global report. | |||
GlobalReport(xid string, globalStatus GlobalStatus) (GlobalStatus, error) | |||
} |
@@ -0,0 +1,247 @@ | |||
package codec | |||
import ( | |||
"bytes" | |||
) | |||
import ( | |||
"vimagination.zapto.org/byteio" | |||
) | |||
import ( | |||
"github.com/seata/seata-go/pkg/protocol" | |||
log "github.com/seata/seata-go/pkg/util/log" | |||
) | |||
type SerializerType byte | |||
const ( | |||
SEATA = byte(0x1) | |||
PROTOBUF = byte(0x2) | |||
KRYO = byte(0x4) | |||
FST = byte(0x8) | |||
) | |||
type Encoder func(in interface{}) []byte | |||
type Decoder func(in []byte) (interface{}, int) | |||
func MessageEncoder(codecType byte, in interface{}) []byte { | |||
switch codecType { | |||
case SEATA: | |||
return SeataEncoder(in) | |||
default: | |||
log.Errorf("not support codecType, %s", codecType) | |||
return nil | |||
} | |||
} | |||
func MessageDecoder(codecType byte, in []byte) (interface{}, int) { | |||
switch codecType { | |||
case SEATA: | |||
return SeataDecoder(in) | |||
default: | |||
log.Errorf("not support codecType, %s", codecType) | |||
return nil, 0 | |||
} | |||
} | |||
func SeataEncoder(in interface{}) []byte { | |||
var result = make([]byte, 0) | |||
msg := in.(protocol.MessageTypeAware) | |||
typeCode := msg.GetTypeCode() | |||
encoder := getMessageEncoder(typeCode) | |||
typeC := uint16(typeCode) | |||
if encoder != nil { | |||
body := encoder(in) | |||
result = append(result, []byte{byte(typeC >> 8), byte(typeC)}...) | |||
result = append(result, body...) | |||
} | |||
return result | |||
} | |||
func SeataDecoder(in []byte) (interface{}, int) { | |||
r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||
typeCode, _, _ := r.ReadInt16() | |||
decoder := getMessageDecoder(typeCode) | |||
if decoder != nil { | |||
return decoder(in[2:]) | |||
} | |||
return nil, 0 | |||
} | |||
func getMessageEncoder(typeCode int16) Encoder { | |||
switch typeCode { | |||
case protocol.TypeSeataMerge: | |||
return MergedWarpMessageEncoder | |||
case protocol.TypeSeataMergeResult: | |||
return MergeResultMessageEncoder | |||
case protocol.TypeRegClt: | |||
return RegisterTMRequestEncoder | |||
case protocol.TypeRegCltResult: | |||
return RegisterTMResponseEncoder | |||
case protocol.TypeRegRm: | |||
return RegisterRMRequestEncoder | |||
case protocol.TypeRegRmResult: | |||
return RegisterRMResponseEncoder | |||
case protocol.TypeBranchCommit: | |||
return BranchCommitRequestEncoder | |||
case protocol.TypeBranchRollback: | |||
return BranchRollbackRequestEncoder | |||
case protocol.TypeGlobalReport: | |||
return GlobalReportRequestEncoder | |||
default: | |||
var encoder Encoder | |||
encoder = getMergeRequestMessageEncoder(typeCode) | |||
if encoder != nil { | |||
return encoder | |||
} | |||
encoder = getMergeResponseMessageEncoder(typeCode) | |||
if encoder != nil { | |||
return encoder | |||
} | |||
log.Errorf("not support typeCode, %d", typeCode) | |||
return nil | |||
} | |||
} | |||
func getMergeRequestMessageEncoder(typeCode int16) Encoder { | |||
switch typeCode { | |||
case protocol.TypeGlobalBegin: | |||
return GlobalBeginRequestEncoder | |||
case protocol.TypeGlobalCommit: | |||
return GlobalCommitRequestEncoder | |||
case protocol.TypeGlobalRollback: | |||
return GlobalRollbackRequestEncoder | |||
case protocol.TypeGlobalStatus: | |||
return GlobalStatusRequestEncoder | |||
case protocol.TypeGlobalLockQuery: | |||
return GlobalLockQueryRequestEncoder | |||
case protocol.TypeBranchRegister: | |||
return BranchRegisterRequestEncoder | |||
case protocol.TypeBranchStatusReport: | |||
return BranchReportRequestEncoder | |||
case protocol.TypeGlobalReport: | |||
return GlobalReportRequestEncoder | |||
default: | |||
break | |||
} | |||
return nil | |||
} | |||
func getMergeResponseMessageEncoder(typeCode int16) Encoder { | |||
switch typeCode { | |||
case protocol.TypeGlobalBeginResult: | |||
return GlobalBeginResponseEncoder | |||
case protocol.TypeGlobalCommitResult: | |||
return GlobalCommitResponseEncoder | |||
case protocol.TypeGlobalRollbackResult: | |||
return GlobalRollbackResponseEncoder | |||
case protocol.TypeGlobalStatusResult: | |||
return GlobalStatusResponseEncoder | |||
case protocol.TypeGlobalLockQueryResult: | |||
return GlobalLockQueryResponseEncoder | |||
case protocol.TypeBranchRegisterResult: | |||
return BranchRegisterResponseEncoder | |||
case protocol.TypeBranchStatusReportResult: | |||
return BranchReportResponseEncoder | |||
case protocol.TypeBranchCommitResult: | |||
return BranchCommitResponseEncoder | |||
case protocol.TypeBranchRollbackResult: | |||
return BranchRollbackResponseEncoder | |||
case protocol.TypeGlobalReportResult: | |||
return GlobalReportResponseEncoder | |||
default: | |||
break | |||
} | |||
return nil | |||
} | |||
func getMessageDecoder(typeCode int16) Decoder { | |||
switch typeCode { | |||
case protocol.TypeSeataMerge: | |||
return MergedWarpMessageDecoder | |||
case protocol.TypeSeataMergeResult: | |||
return MergeResultMessageDecoder | |||
case protocol.TypeRegClt: | |||
return RegisterTMRequestDecoder | |||
case protocol.TypeRegCltResult: | |||
return RegisterTMResponseDecoder | |||
case protocol.TypeRegRm: | |||
return RegisterRMRequestDecoder | |||
case protocol.TypeRegRmResult: | |||
return RegisterRMResponseDecoder | |||
case protocol.TypeBranchCommit: | |||
return BranchCommitRequestDecoder | |||
case protocol.TypeBranchRollback: | |||
return BranchRollbackRequestDecoder | |||
case protocol.TypeGlobalReport: | |||
return GlobalReportRequestDecoder | |||
default: | |||
var Decoder Decoder | |||
Decoder = getMergeRequestMessageDecoder(typeCode) | |||
if Decoder != nil { | |||
return Decoder | |||
} | |||
Decoder = getMergeResponseMessageDecoder(typeCode) | |||
if Decoder != nil { | |||
return Decoder | |||
} | |||
log.Errorf("not support typeCode, %d", typeCode) | |||
return nil | |||
} | |||
} | |||
func getMergeRequestMessageDecoder(typeCode int16) Decoder { | |||
switch typeCode { | |||
case protocol.TypeGlobalBegin: | |||
return GlobalBeginRequestDecoder | |||
case protocol.TypeGlobalCommit: | |||
return GlobalCommitRequestDecoder | |||
case protocol.TypeGlobalRollback: | |||
return GlobalRollbackRequestDecoder | |||
case protocol.TypeGlobalStatus: | |||
return GlobalStatusRequestDecoder | |||
case protocol.TypeGlobalLockQuery: | |||
return GlobalLockQueryRequestDecoder | |||
case protocol.TypeBranchRegister: | |||
return BranchRegisterRequestDecoder | |||
case protocol.TypeBranchStatusReport: | |||
return BranchReportRequestDecoder | |||
case protocol.TypeGlobalReport: | |||
return GlobalReportRequestDecoder | |||
default: | |||
break | |||
} | |||
return nil | |||
} | |||
func getMergeResponseMessageDecoder(typeCode int16) Decoder { | |||
switch typeCode { | |||
case protocol.TypeGlobalBeginResult: | |||
return GlobalBeginResponseDecoder | |||
case protocol.TypeGlobalCommitResult: | |||
return GlobalCommitResponseDecoder | |||
case protocol.TypeGlobalRollbackResult: | |||
return GlobalRollbackResponseDecoder | |||
case protocol.TypeGlobalStatusResult: | |||
return GlobalStatusResponseDecoder | |||
case protocol.TypeGlobalLockQueryResult: | |||
return GlobalLockQueryResponseDecoder | |||
case protocol.TypeBranchRegisterResult: | |||
return BranchRegisterResponseDecoder | |||
case protocol.TypeBranchStatusReportResult: | |||
return BranchReportResponseDecoder | |||
case protocol.TypeBranchCommitResult: | |||
return BranchCommitResponseDecoder | |||
case protocol.TypeBranchRollbackResult: | |||
return BranchRollbackResponseDecoder | |||
case protocol.TypeGlobalReportResult: | |||
return GlobalReportResponseDecoder | |||
default: | |||
break | |||
} | |||
return nil | |||
} |
@@ -0,0 +1,779 @@ | |||
package codec | |||
import ( | |||
"bytes" | |||
) | |||
import ( | |||
"vimagination.zapto.org/byteio" | |||
) | |||
import ( | |||
"github.com/seata/seata-go/pkg/model" | |||
"github.com/seata/seata-go/pkg/protocol" | |||
) | |||
func AbstractResultMessageDecoder(in []byte) (interface{}, int) { | |||
var ( | |||
length16 uint16 = 0 | |||
readN = 0 | |||
totalReadN = 0 | |||
) | |||
msg := protocol.AbstractResultMessage{} | |||
r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||
resultCode, _ := r.ReadByte() | |||
msg.ResultCode = protocol.ResultCode(resultCode) | |||
totalReadN += 1 | |||
if msg.ResultCode == protocol.ResultCodeFailed { | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.Msg, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
} | |||
return msg, totalReadN | |||
} | |||
func MergedWarpMessageDecoder(in []byte) (interface{}, int) { | |||
var ( | |||
size16 int16 = 0 | |||
readN = 0 | |||
totalReadN = 0 | |||
) | |||
result := protocol.MergedWarpMessage{} | |||
r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||
r.ReadInt32() | |||
totalReadN += 4 | |||
size16, readN, _ = r.ReadInt16() | |||
totalReadN += readN | |||
result.Msgs = make([]protocol.MessageTypeAware, 0) | |||
for index := 0; index < int(size16); index++ { | |||
typeCode, _, _ := r.ReadInt16() | |||
totalReadN += 2 | |||
decoder := getMessageDecoder(typeCode) | |||
if decoder != nil { | |||
msg, readN := decoder(in[totalReadN:]) | |||
totalReadN += readN | |||
result.Msgs = append(result.Msgs, msg.(protocol.MessageTypeAware)) | |||
} | |||
} | |||
return result, totalReadN | |||
} | |||
func MergeResultMessageDecoder(in []byte) (interface{}, int) { | |||
var ( | |||
size16 int16 = 0 | |||
readN = 0 | |||
totalReadN = 0 | |||
) | |||
result := protocol.MergeResultMessage{} | |||
r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||
r.ReadInt32() | |||
totalReadN += 4 | |||
size16, readN, _ = r.ReadInt16() | |||
totalReadN += readN | |||
result.Msgs = make([]protocol.MessageTypeAware, 0) | |||
for index := 0; index < int(size16); index++ { | |||
typeCode, _, _ := r.ReadInt16() | |||
totalReadN += 2 | |||
decoder := getMessageDecoder(typeCode) | |||
if decoder != nil { | |||
msg, readN := decoder(in[totalReadN:]) | |||
totalReadN += readN | |||
result.Msgs = append(result.Msgs, msg.(protocol.MessageTypeAware)) | |||
} | |||
} | |||
return result, totalReadN | |||
} | |||
func AbstractIdentifyRequestDecoder(in []byte) (interface{}, int) { | |||
var ( | |||
length16 uint16 = 0 | |||
readN = 0 | |||
totalReadN = 0 | |||
) | |||
msg := protocol.AbstractIdentifyRequest{} | |||
r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.Version, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.ApplicationId, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.TransactionServiceGroup, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.ExtraData = make([]byte, int(length16)) | |||
readN, _ := r.Read(msg.ExtraData) | |||
totalReadN += readN | |||
} | |||
return msg, totalReadN | |||
} | |||
func AbstractIdentifyResponseDecoder(in []byte) (interface{}, int) { | |||
var ( | |||
length16 uint16 = 0 | |||
readN = 0 | |||
totalReadN = 0 | |||
) | |||
msg := protocol.AbstractIdentifyResponse{} | |||
r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||
identified, _ := r.ReadByte() | |||
totalReadN += 1 | |||
if identified == byte(1) { | |||
msg.Identified = true | |||
} else if identified == byte(0) { | |||
msg.Identified = false | |||
} | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.Version, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
return msg, totalReadN | |||
} | |||
func RegisterRMRequestDecoder(in []byte) (interface{}, int) { | |||
var ( | |||
length32 uint32 = 0 | |||
length16 uint16 = 0 | |||
readN = 0 | |||
totalReadN = 0 | |||
) | |||
msg := protocol.RegisterRMRequest{} | |||
r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.Version, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.ApplicationId, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.TransactionServiceGroup, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.ExtraData = make([]byte, int(length16)) | |||
readN, _ := r.Read(msg.ExtraData) | |||
totalReadN += readN | |||
} | |||
length32, readN, _ = r.ReadUint32() | |||
totalReadN += readN | |||
if length32 > 0 { | |||
msg.ResourceIds, readN, _ = r.ReadString(int(length32)) | |||
totalReadN += readN | |||
} | |||
return msg, totalReadN | |||
} | |||
func RegisterRMResponseDecoder(in []byte) (interface{}, int) { | |||
resp, totalReadN := AbstractIdentifyResponseDecoder(in) | |||
abstractIdentifyResponse := resp.(protocol.AbstractIdentifyResponse) | |||
msg := protocol.RegisterRMResponse{AbstractIdentifyResponse: abstractIdentifyResponse} | |||
return msg, totalReadN | |||
} | |||
func RegisterTMRequestDecoder(in []byte) (interface{}, int) { | |||
req, totalReadN := AbstractIdentifyRequestDecoder(in) | |||
abstractIdentifyRequest := req.(protocol.AbstractIdentifyRequest) | |||
msg := protocol.RegisterTMRequest{AbstractIdentifyRequest: abstractIdentifyRequest} | |||
return msg, totalReadN | |||
} | |||
func RegisterTMResponseDecoder(in []byte) (interface{}, int) { | |||
resp, totalReadN := AbstractIdentifyResponseDecoder(in) | |||
abstractIdentifyResponse := resp.(protocol.AbstractIdentifyResponse) | |||
msg := protocol.RegisterRMResponse{AbstractIdentifyResponse: abstractIdentifyResponse} | |||
return msg, totalReadN | |||
} | |||
func AbstractTransactionResponseDecoder(in []byte) (interface{}, int) { | |||
var ( | |||
length16 uint16 = 0 | |||
readN = 0 | |||
totalReadN = 0 | |||
) | |||
msg := protocol.AbstractTransactionResponse{} | |||
r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||
resultCode, _ := r.ReadByte() | |||
totalReadN += 1 | |||
msg.ResultCode = protocol.ResultCode(resultCode) | |||
if msg.ResultCode == protocol.ResultCodeFailed { | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.Msg, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
} | |||
exceptionCode, _ := r.ReadByte() | |||
totalReadN += 1 | |||
msg.TransactionExceptionCode = model.TransactionExceptionCode(exceptionCode) | |||
return msg, totalReadN | |||
} | |||
func AbstractBranchEndRequestDecoder(in []byte) (interface{}, int) { | |||
var ( | |||
length16 uint16 = 0 | |||
readN = 0 | |||
totalReadN = 0 | |||
) | |||
msg := protocol.AbstractBranchEndRequest{} | |||
r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.Xid, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
msg.BranchId, _, _ = r.ReadInt64() | |||
totalReadN += 8 | |||
branchType, _ := r.ReadByte() | |||
totalReadN += 1 | |||
msg.BranchType = model.BranchType(branchType) | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.ResourceId, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.ApplicationData = make([]byte, int(length16)) | |||
readN, _ := r.Read(msg.ApplicationData) | |||
totalReadN += readN | |||
} | |||
return msg, totalReadN | |||
} | |||
func AbstractBranchEndResponseDecoder(in []byte) (interface{}, int) { | |||
var ( | |||
length16 uint16 = 0 | |||
readN = 0 | |||
totalReadN = 0 | |||
) | |||
msg := protocol.AbstractBranchEndResponse{} | |||
r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||
resultCode, _ := r.ReadByte() | |||
totalReadN += 1 | |||
msg.ResultCode = protocol.ResultCode(resultCode) | |||
if msg.ResultCode == protocol.ResultCodeFailed { | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.Msg, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
} | |||
exceptionCode, _ := r.ReadByte() | |||
totalReadN += 1 | |||
msg.TransactionExceptionCode = model.TransactionExceptionCode(exceptionCode) | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.Xid, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
msg.BranchId, _, _ = r.ReadInt64() | |||
totalReadN += 8 | |||
branchStatus, _ := r.ReadByte() | |||
totalReadN += 1 | |||
msg.BranchStatus = model.BranchStatus(branchStatus) | |||
return msg, totalReadN | |||
} | |||
func AbstractGlobalEndRequestDecoder(in []byte) (interface{}, int) { | |||
var ( | |||
length16 uint16 = 0 | |||
readN = 0 | |||
totalReadN = 0 | |||
) | |||
msg := protocol.AbstractGlobalEndRequest{} | |||
r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.Xid, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.ExtraData = make([]byte, int(length16)) | |||
readN, _ := r.Read(msg.ExtraData) | |||
totalReadN += readN | |||
} | |||
return msg, totalReadN | |||
} | |||
func AbstractGlobalEndResponseDecoder(in []byte) (interface{}, int) { | |||
var ( | |||
length16 uint16 = 0 | |||
readN = 0 | |||
totalReadN = 0 | |||
) | |||
msg := protocol.AbstractGlobalEndResponse{} | |||
r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||
resultCode, _ := r.ReadByte() | |||
totalReadN += 1 | |||
msg.ResultCode = protocol.ResultCode(resultCode) | |||
if msg.ResultCode == protocol.ResultCodeFailed { | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.Msg, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
} | |||
exceptionCode, _ := r.ReadByte() | |||
totalReadN += 1 | |||
msg.TransactionExceptionCode = model.TransactionExceptionCode(exceptionCode) | |||
globalStatus, _ := r.ReadByte() | |||
totalReadN += 1 | |||
msg.GlobalStatus = model.GlobalStatus(globalStatus) | |||
return msg, totalReadN | |||
} | |||
func BranchCommitRequestDecoder(in []byte) (interface{}, int) { | |||
req, totalReadN := AbstractBranchEndRequestDecoder(in) | |||
abstractBranchEndRequest := req.(protocol.AbstractBranchEndRequest) | |||
msg := protocol.BranchCommitRequest{AbstractBranchEndRequest: abstractBranchEndRequest} | |||
return msg, totalReadN | |||
} | |||
func BranchCommitResponseDecoder(in []byte) (interface{}, int) { | |||
resp, totalReadN := AbstractBranchEndResponseDecoder(in) | |||
abstractBranchEndResponse := resp.(protocol.AbstractBranchEndResponse) | |||
msg := protocol.BranchCommitResponse{AbstractBranchEndResponse: abstractBranchEndResponse} | |||
return msg, totalReadN | |||
} | |||
func BranchRegisterRequestDecoder(in []byte) (interface{}, int) { | |||
var ( | |||
length32 uint32 = 0 | |||
length16 uint16 = 0 | |||
readN = 0 | |||
totalReadN = 0 | |||
) | |||
msg := protocol.BranchRegisterRequest{} | |||
r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.Xid, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
branchType, _ := r.ReadByte() | |||
totalReadN += 1 | |||
msg.BranchType = model.BranchType(branchType) | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.ResourceId, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
length32, readN, _ = r.ReadUint32() | |||
totalReadN += readN | |||
if length32 > 0 { | |||
msg.LockKey, readN, _ = r.ReadString(int(length32)) | |||
totalReadN += readN | |||
} | |||
length32, readN, _ = r.ReadUint32() | |||
totalReadN += readN | |||
if length32 > 0 { | |||
msg.ApplicationData = make([]byte, int(length32)) | |||
readN, _ := r.Read(msg.ApplicationData) | |||
totalReadN += readN | |||
} | |||
return msg, totalReadN | |||
} | |||
func BranchRegisterResponseDecoder(in []byte) (interface{}, int) { | |||
var ( | |||
length16 uint16 = 0 | |||
readN = 0 | |||
totalReadN = 0 | |||
) | |||
msg := protocol.BranchRegisterResponse{} | |||
r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||
resultCode, _ := r.ReadByte() | |||
totalReadN += 1 | |||
msg.ResultCode = protocol.ResultCode(resultCode) | |||
if msg.ResultCode == protocol.ResultCodeFailed { | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.Msg, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
} | |||
exceptionCode, _ := r.ReadByte() | |||
totalReadN += 1 | |||
msg.TransactionExceptionCode = model.TransactionExceptionCode(exceptionCode) | |||
msg.BranchId, readN, _ = r.ReadInt64() | |||
totalReadN += readN | |||
return msg, totalReadN | |||
} | |||
func BranchReportRequestDecoder(in []byte) (interface{}, int) { | |||
var ( | |||
length16 uint16 = 0 | |||
readN = 0 | |||
totalReadN = 0 | |||
) | |||
msg := protocol.BranchReportRequest{} | |||
r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.Xid, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
msg.BranchId, _, _ = r.ReadInt64() | |||
branchStatus, _ := r.ReadByte() | |||
msg.Status = model.BranchStatus(branchStatus) | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.ResourceId, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.ApplicationData = make([]byte, int(length16)) | |||
readN, _ := r.Read(msg.ApplicationData) | |||
totalReadN += readN | |||
} | |||
branchType, _ := r.ReadByte() | |||
totalReadN += 1 | |||
msg.BranchType = model.BranchType(branchType) | |||
return msg, totalReadN | |||
} | |||
func BranchReportResponseDecoder(in []byte) (interface{}, int) { | |||
resp, totalReadN := AbstractTransactionResponseDecoder(in) | |||
abstractTransactionResponse := resp.(protocol.AbstractTransactionResponse) | |||
msg := protocol.BranchReportResponse{AbstractTransactionResponse: abstractTransactionResponse} | |||
return msg, totalReadN | |||
} | |||
func BranchRollbackRequestDecoder(in []byte) (interface{}, int) { | |||
req, totalReadN := AbstractBranchEndRequestDecoder(in) | |||
abstractBranchEndRequest := req.(protocol.AbstractBranchEndRequest) | |||
msg := protocol.BranchRollbackRequest{AbstractBranchEndRequest: abstractBranchEndRequest} | |||
return msg, totalReadN | |||
} | |||
func BranchRollbackResponseDecoder(in []byte) (interface{}, int) { | |||
resp, totalReadN := AbstractBranchEndResponseDecoder(in) | |||
abstractBranchEndResponse := resp.(protocol.AbstractBranchEndResponse) | |||
msg := protocol.BranchRollbackResponse{AbstractBranchEndResponse: abstractBranchEndResponse} | |||
return msg, totalReadN | |||
} | |||
func GlobalBeginRequestDecoder(in []byte) (interface{}, int) { | |||
var ( | |||
length16 uint16 = 0 | |||
readN = 0 | |||
totalReadN = 0 | |||
) | |||
msg := protocol.GlobalBeginRequest{} | |||
r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||
timeout, readN, _ := r.ReadInt32() | |||
totalReadN += readN | |||
msg.Timeout = timeout | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.TransactionName, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
return msg, totalReadN | |||
} | |||
func GlobalBeginResponseDecoder(in []byte) (interface{}, int) { | |||
var ( | |||
length16 uint16 = 0 | |||
readN = 0 | |||
totalReadN = 0 | |||
) | |||
msg := protocol.GlobalBeginResponse{} | |||
r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||
resultCode, _ := r.ReadByte() | |||
totalReadN += 1 | |||
msg.ResultCode = protocol.ResultCode(resultCode) | |||
if msg.ResultCode == protocol.ResultCodeFailed { | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.Msg, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
} | |||
exceptionCode, _ := r.ReadByte() | |||
totalReadN += 1 | |||
msg.TransactionExceptionCode = model.TransactionExceptionCode(exceptionCode) | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.Xid, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.ExtraData = make([]byte, int(length16)) | |||
readN, _ := r.Read(msg.ExtraData) | |||
totalReadN += readN | |||
} | |||
return msg, totalReadN | |||
} | |||
func GlobalCommitRequestDecoder(in []byte) (interface{}, int) { | |||
req, totalReadN := AbstractGlobalEndRequestDecoder(in) | |||
abstractGlobalEndRequest := req.(protocol.AbstractGlobalEndRequest) | |||
msg := protocol.GlobalCommitRequest{AbstractGlobalEndRequest: abstractGlobalEndRequest} | |||
return msg, totalReadN | |||
} | |||
func GlobalCommitResponseDecoder(in []byte) (interface{}, int) { | |||
resp, totalReadN := AbstractGlobalEndResponseDecoder(in) | |||
abstractGlobalEndResponse := resp.(protocol.AbstractGlobalEndResponse) | |||
msg := protocol.GlobalCommitResponse{AbstractGlobalEndResponse: abstractGlobalEndResponse} | |||
return msg, totalReadN | |||
} | |||
func GlobalLockQueryRequestDecoder(in []byte) (interface{}, int) { | |||
req, totalReadN := BranchRegisterRequestDecoder(in) | |||
branchRegisterRequest := req.(protocol.BranchRegisterRequest) | |||
msg := protocol.GlobalLockQueryRequest{BranchRegisterRequest: branchRegisterRequest} | |||
return msg, totalReadN | |||
} | |||
func GlobalLockQueryResponseDecoder(in []byte) (interface{}, int) { | |||
var ( | |||
length16 uint16 = 0 | |||
readN = 0 | |||
totalReadN = 0 | |||
) | |||
msg := protocol.GlobalLockQueryResponse{} | |||
r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||
resultCode, _ := r.ReadByte() | |||
totalReadN += 1 | |||
msg.ResultCode = protocol.ResultCode(resultCode) | |||
if msg.ResultCode == protocol.ResultCodeFailed { | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.Msg, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
} | |||
exceptionCode, _ := r.ReadByte() | |||
totalReadN += 1 | |||
msg.TransactionExceptionCode = model.TransactionExceptionCode(exceptionCode) | |||
lockable, readN, _ := r.ReadUint16() | |||
totalReadN += readN | |||
if lockable == uint16(1) { | |||
msg.Lockable = true | |||
} else if lockable == uint16(0) { | |||
msg.Lockable = false | |||
} | |||
return msg, totalReadN | |||
} | |||
func GlobalReportRequestDecoder(in []byte) (interface{}, int) { | |||
var ( | |||
length16 uint16 = 0 | |||
readN = 0 | |||
totalReadN = 0 | |||
) | |||
msg := protocol.GlobalReportRequest{} | |||
r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.Xid, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.ExtraData = make([]byte, int(length16)) | |||
readN, _ := r.Read(msg.ExtraData) | |||
totalReadN += readN | |||
} | |||
globalStatus, _ := r.ReadByte() | |||
totalReadN += 1 | |||
msg.GlobalStatus = model.GlobalStatus(globalStatus) | |||
return msg, totalReadN | |||
} | |||
func GlobalReportResponseDecoder(in []byte) (interface{}, int) { | |||
resp, totalReadN := AbstractGlobalEndResponseDecoder(in) | |||
abstractGlobalEndResponse := resp.(protocol.AbstractGlobalEndResponse) | |||
msg := protocol.GlobalReportResponse{AbstractGlobalEndResponse: abstractGlobalEndResponse} | |||
return msg, totalReadN | |||
} | |||
func GlobalRollbackRequestDecoder(in []byte) (interface{}, int) { | |||
req, totalReadN := AbstractGlobalEndRequestDecoder(in) | |||
abstractGlobalEndRequest := req.(protocol.AbstractGlobalEndRequest) | |||
msg := protocol.GlobalRollbackRequest{AbstractGlobalEndRequest: abstractGlobalEndRequest} | |||
return msg, totalReadN | |||
} | |||
func GlobalRollbackResponseDecoder(in []byte) (interface{}, int) { | |||
resp, totalReadN := AbstractGlobalEndResponseDecoder(in) | |||
abstractGlobalEndResponse := resp.(protocol.AbstractGlobalEndResponse) | |||
msg := protocol.GlobalRollbackResponse{AbstractGlobalEndResponse: abstractGlobalEndResponse} | |||
return msg, totalReadN | |||
} | |||
func GlobalStatusRequestDecoder(in []byte) (interface{}, int) { | |||
req, totalReadN := AbstractGlobalEndRequestDecoder(in) | |||
abstractGlobalEndRequest := req.(protocol.AbstractGlobalEndRequest) | |||
msg := protocol.GlobalStatusRequest{AbstractGlobalEndRequest: abstractGlobalEndRequest} | |||
return msg, totalReadN | |||
} | |||
func GlobalStatusResponseDecoder(in []byte) (interface{}, int) { | |||
resp, totalReadN := AbstractGlobalEndResponseDecoder(in) | |||
abstractGlobalEndResponse := resp.(protocol.AbstractGlobalEndResponse) | |||
msg := protocol.GlobalStatusResponse{AbstractGlobalEndResponse: abstractGlobalEndResponse} | |||
return msg, totalReadN | |||
} | |||
func UndoLogDeleteRequestDecoder(in []byte) (interface{}, int) { | |||
var ( | |||
length16 uint16 = 0 | |||
readN = 0 | |||
totalReadN = 0 | |||
) | |||
msg := protocol.UndoLogDeleteRequest{} | |||
r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||
branchType, _ := r.ReadByte() | |||
totalReadN += 1 | |||
msg.BranchType = model.BranchType(branchType) | |||
length16, readN, _ = r.ReadUint16() | |||
totalReadN += readN | |||
if length16 > 0 { | |||
msg.ResourceId, readN, _ = r.ReadString(int(length16)) | |||
totalReadN += readN | |||
} | |||
msg.SaveDays, readN, _ = r.ReadInt16() | |||
totalReadN += readN | |||
return msg, totalReadN | |||
} |
@@ -0,0 +1,565 @@ | |||
package codec | |||
import ( | |||
"bytes" | |||
) | |||
import ( | |||
"vimagination.zapto.org/byteio" | |||
) | |||
import ( | |||
"github.com/seata/seata-go/pkg/protocol" | |||
log "github.com/seata/seata-go/pkg/util/log" | |||
) | |||
func AbstractResultMessageEncoder(in interface{}) []byte { | |||
var ( | |||
zero16 int16 = 0 | |||
b bytes.Buffer | |||
) | |||
w := byteio.BigEndianWriter{Writer: &b} | |||
message := in.(protocol.AbstractResultMessage) | |||
w.WriteByte(byte(message.ResultCode)) | |||
if message.ResultCode == protocol.ResultCodeFailed { | |||
var msg string | |||
if message.Msg != "" { | |||
if len(message.Msg) > 128 { | |||
msg = message.Msg[:128] | |||
} else { | |||
msg = message.Msg | |||
} | |||
// 暂时不考虑 message.Msg 包含中文的情况,这样字符串的长度就是 byte 数组的长度 | |||
w.WriteInt16(int16(len(msg))) | |||
w.WriteString(msg) | |||
} else { | |||
w.WriteInt16(zero16) | |||
} | |||
} | |||
return b.Bytes() | |||
} | |||
func MergedWarpMessageEncoder(in interface{}) []byte { | |||
var ( | |||
b bytes.Buffer | |||
result = make([]byte, 0) | |||
) | |||
w := byteio.BigEndianWriter{Writer: &b} | |||
req, _ := in.(protocol.MergedWarpMessage) | |||
w.WriteInt16(int16(len(req.Msgs))) | |||
for _, msg := range req.Msgs { | |||
encoder := getMessageEncoder(msg.GetTypeCode()) | |||
if encoder != nil { | |||
data := encoder(msg) | |||
w.WriteInt16(msg.GetTypeCode()) | |||
w.Write(data) | |||
} | |||
} | |||
size := uint32(b.Len()) | |||
result = append(result, []byte{byte(size >> 24), byte(size >> 16), byte(size >> 8), byte(size)}...) | |||
result = append(result, b.Bytes()...) | |||
if len(req.Msgs) > 20 { | |||
log.Debugf("msg in one packet: %s ,buffer size: %s", len(req.Msgs), size) | |||
} | |||
return result | |||
} | |||
func MergeResultMessageEncoder(in interface{}) []byte { | |||
var ( | |||
b bytes.Buffer | |||
result = make([]byte, 0) | |||
) | |||
w := byteio.BigEndianWriter{Writer: &b} | |||
req, _ := in.(protocol.MergeResultMessage) | |||
w.WriteInt16(int16(len(req.Msgs))) | |||
for _, msg := range req.Msgs { | |||
encoder := getMessageEncoder(msg.GetTypeCode()) | |||
if encoder != nil { | |||
data := encoder(msg) | |||
w.WriteInt16(msg.GetTypeCode()) | |||
w.Write(data) | |||
} | |||
} | |||
size := uint32(b.Len()) | |||
result = append(result, []byte{byte(size >> 24), byte(size >> 16), byte(size >> 8), byte(size)}...) | |||
result = append(result, b.Bytes()...) | |||
if len(req.Msgs) > 20 { | |||
log.Debugf("msg in one packet: %s ,buffer size: %s", len(req.Msgs), size) | |||
} | |||
return result | |||
} | |||
func AbstractIdentifyRequestEncoder(in interface{}) []byte { | |||
var ( | |||
zero16 int16 = 0 | |||
b bytes.Buffer | |||
) | |||
w := byteio.BigEndianWriter{Writer: &b} | |||
req := in.(protocol.AbstractIdentifyRequest) | |||
if req.Version != "" { | |||
w.WriteInt16(int16(len(req.Version))) | |||
w.WriteString(req.Version) | |||
} else { | |||
w.WriteInt16(zero16) | |||
} | |||
if req.ApplicationId != "" { | |||
w.WriteInt16(int16(len(req.ApplicationId))) | |||
w.WriteString(req.ApplicationId) | |||
} else { | |||
w.WriteInt16(zero16) | |||
} | |||
if req.TransactionServiceGroup != "" { | |||
w.WriteInt16(int16(len(req.TransactionServiceGroup))) | |||
w.WriteString(req.TransactionServiceGroup) | |||
} else { | |||
w.WriteInt16(zero16) | |||
} | |||
if req.ExtraData != nil { | |||
w.WriteUint16(uint16(len(req.ExtraData))) | |||
w.Write(req.ExtraData) | |||
} else { | |||
w.WriteInt16(zero16) | |||
} | |||
return b.Bytes() | |||
} | |||
func AbstractIdentifyResponseEncoder(in interface{}) []byte { | |||
resp := in.(protocol.AbstractIdentifyResponse) | |||
var ( | |||
zero16 int16 = 0 | |||
b bytes.Buffer | |||
) | |||
w := byteio.BigEndianWriter{Writer: &b} | |||
if resp.Identified { | |||
w.WriteByte(byte(1)) | |||
} else { | |||
w.WriteByte(byte(0)) | |||
} | |||
if resp.Version != "" { | |||
w.WriteInt16(int16(len(resp.Version))) | |||
w.WriteString(resp.Version) | |||
} else { | |||
w.WriteInt16(zero16) | |||
} | |||
return b.Bytes() | |||
} | |||
func RegisterRMRequestEncoder(in interface{}) []byte { | |||
req := in.(protocol.RegisterRMRequest) | |||
data := AbstractIdentifyRequestEncoder(req.AbstractIdentifyRequest) | |||
var ( | |||
zero32 int32 = 0 | |||
b bytes.Buffer | |||
) | |||
w := byteio.BigEndianWriter{Writer: &b} | |||
if req.ResourceIds != "" { | |||
w.WriteInt32(int32(len(req.ResourceIds))) | |||
w.WriteString(req.ResourceIds) | |||
} else { | |||
w.WriteInt32(zero32) | |||
} | |||
result := append(data, b.Bytes()...) | |||
return result | |||
} | |||
func RegisterRMResponseEncoder(in interface{}) []byte { | |||
resp := in.(protocol.RegisterRMResponse) | |||
return AbstractIdentifyResponseEncoder(resp.AbstractIdentifyResponse) | |||
} | |||
func RegisterTMRequestEncoder(in interface{}) []byte { | |||
req := in.(protocol.RegisterTMRequest) | |||
return AbstractIdentifyRequestEncoder(req.AbstractIdentifyRequest) | |||
} | |||
func RegisterTMResponseEncoder(in interface{}) []byte { | |||
resp := in.(protocol.RegisterTMResponse) | |||
return AbstractIdentifyResponseEncoder(resp.AbstractIdentifyResponse) | |||
} | |||
func AbstractTransactionResponseEncoder(in interface{}) []byte { | |||
resp := in.(protocol.AbstractTransactionResponse) | |||
data := AbstractResultMessageEncoder(resp.AbstractResultMessage) | |||
result := append(data, byte(resp.TransactionExceptionCode)) | |||
return result | |||
} | |||
func AbstractBranchEndRequestEncoder(in interface{}) []byte { | |||
var ( | |||
zero32 int32 = 0 | |||
zero16 int16 = 0 | |||
b bytes.Buffer | |||
) | |||
w := byteio.BigEndianWriter{Writer: &b} | |||
req, _ := in.(protocol.AbstractBranchEndRequest) | |||
if req.Xid != "" { | |||
w.WriteInt16(int16(len(req.Xid))) | |||
w.WriteString(req.Xid) | |||
} else { | |||
w.WriteInt16(zero16) | |||
} | |||
w.WriteInt64(req.BranchId) | |||
w.WriteByte(byte(req.BranchType)) | |||
if req.ResourceId != "" { | |||
w.WriteInt16(int16(len(req.ResourceId))) | |||
w.WriteString(req.ResourceId) | |||
} else { | |||
w.WriteInt16(zero16) | |||
} | |||
if req.ApplicationData != nil { | |||
w.WriteUint32(uint32(len(req.ApplicationData))) | |||
w.Write(req.ApplicationData) | |||
} else { | |||
w.WriteInt32(zero32) | |||
} | |||
return b.Bytes() | |||
} | |||
func AbstractBranchEndResponseEncoder(in interface{}) []byte { | |||
resp, _ := in.(protocol.AbstractBranchEndResponse) | |||
data := AbstractTransactionResponseEncoder(resp.AbstractTransactionResponse) | |||
var ( | |||
zero16 int16 = 0 | |||
b bytes.Buffer | |||
) | |||
w := byteio.BigEndianWriter{Writer: &b} | |||
if resp.Xid != "" { | |||
w.WriteInt16(int16(len(resp.Xid))) | |||
w.WriteString(resp.Xid) | |||
} else { | |||
w.WriteInt16(zero16) | |||
} | |||
w.WriteInt64(resp.BranchId) | |||
w.WriteByte(byte(resp.BranchStatus)) | |||
result := append(data, b.Bytes()...) | |||
return result | |||
} | |||
func AbstractGlobalEndRequestEncoder(in interface{}) []byte { | |||
var ( | |||
zero16 int16 = 0 | |||
b bytes.Buffer | |||
) | |||
w := byteio.BigEndianWriter{Writer: &b} | |||
req, _ := in.(protocol.AbstractGlobalEndRequest) | |||
if req.Xid != "" { | |||
w.WriteInt16(int16(len(req.Xid))) | |||
w.WriteString(req.Xid) | |||
} else { | |||
w.WriteInt16(zero16) | |||
} | |||
if req.ExtraData != nil { | |||
w.WriteUint16(uint16(len(req.ExtraData))) | |||
w.Write(req.ExtraData) | |||
} else { | |||
w.WriteInt16(zero16) | |||
} | |||
return b.Bytes() | |||
} | |||
func AbstractGlobalEndResponseEncoder(in interface{}) []byte { | |||
resp := in.(protocol.AbstractGlobalEndResponse) | |||
data := AbstractTransactionResponseEncoder(resp.AbstractTransactionResponse) | |||
result := append(data, byte(resp.GlobalStatus)) | |||
return result | |||
} | |||
func BranchCommitRequestEncoder(in interface{}) []byte { | |||
req := in.(protocol.BranchCommitRequest) | |||
return AbstractBranchEndRequestEncoder(req.AbstractBranchEndRequest) | |||
} | |||
func BranchCommitResponseEncoder(in interface{}) []byte { | |||
resp := in.(protocol.BranchCommitResponse) | |||
return AbstractBranchEndResponseEncoder(resp.AbstractBranchEndResponse) | |||
} | |||
func BranchRegisterRequestEncoder(in interface{}) []byte { | |||
var ( | |||
zero32 int32 = 0 | |||
zero16 int16 = 0 | |||
b bytes.Buffer | |||
) | |||
w := byteio.BigEndianWriter{Writer: &b} | |||
req, _ := in.(protocol.BranchRegisterRequest) | |||
if req.Xid != "" { | |||
w.WriteInt16(int16(len(req.Xid))) | |||
w.WriteString(req.Xid) | |||
} else { | |||
w.WriteInt16(zero16) | |||
} | |||
w.WriteByte(byte(req.BranchType)) | |||
if req.ResourceId != "" { | |||
w.WriteInt16(int16(len(req.ResourceId))) | |||
w.WriteString(req.ResourceId) | |||
} else { | |||
w.WriteInt16(zero16) | |||
} | |||
if req.LockKey != "" { | |||
w.WriteInt32(int32(len(req.LockKey))) | |||
w.WriteString(req.LockKey) | |||
} else { | |||
w.WriteInt32(zero32) | |||
} | |||
if req.ApplicationData != nil { | |||
w.WriteUint32(uint32(len(req.ApplicationData))) | |||
w.Write(req.ApplicationData) | |||
} else { | |||
w.WriteInt32(zero32) | |||
} | |||
return b.Bytes() | |||
} | |||
func BranchRegisterResponseEncoder(in interface{}) []byte { | |||
resp := in.(protocol.BranchRegisterResponse) | |||
data := AbstractTransactionResponseEncoder(resp.AbstractTransactionResponse) | |||
c := uint64(resp.BranchId) | |||
branchIdBytes := []byte{ | |||
byte(c >> 56), | |||
byte(c >> 48), | |||
byte(c >> 40), | |||
byte(c >> 32), | |||
byte(c >> 24), | |||
byte(c >> 16), | |||
byte(c >> 8), | |||
byte(c), | |||
} | |||
result := append(data, branchIdBytes...) | |||
return result | |||
} | |||
func BranchReportRequestEncoder(in interface{}) []byte { | |||
var ( | |||
zero32 int32 = 0 | |||
zero16 int16 = 0 | |||
b bytes.Buffer | |||
) | |||
w := byteio.BigEndianWriter{Writer: &b} | |||
req, _ := in.(protocol.BranchReportRequest) | |||
if req.Xid != "" { | |||
w.WriteInt16(int16(len(req.Xid))) | |||
w.WriteString(req.Xid) | |||
} else { | |||
w.WriteInt16(zero16) | |||
} | |||
w.WriteInt64(req.BranchId) | |||
w.WriteByte(byte(req.Status)) | |||
if req.ResourceId != "" { | |||
w.WriteInt16(int16(len(req.ResourceId))) | |||
w.WriteString(req.ResourceId) | |||
} else { | |||
w.WriteInt16(zero16) | |||
} | |||
if req.ApplicationData != nil { | |||
w.WriteUint32(uint32(len(req.ApplicationData))) | |||
w.Write(req.ApplicationData) | |||
} else { | |||
w.WriteInt32(zero32) | |||
} | |||
w.WriteByte(byte(req.BranchType)) | |||
return b.Bytes() | |||
} | |||
func BranchReportResponseEncoder(in interface{}) []byte { | |||
resp := in.(protocol.BranchReportResponse) | |||
return AbstractTransactionResponseEncoder(resp.AbstractTransactionResponse) | |||
} | |||
func BranchRollbackRequestEncoder(in interface{}) []byte { | |||
req := in.(protocol.BranchRollbackRequest) | |||
return AbstractBranchEndRequestEncoder(req.AbstractBranchEndRequest) | |||
} | |||
func BranchRollbackResponseEncoder(in interface{}) []byte { | |||
resp := in.(protocol.BranchRollbackResponse) | |||
return AbstractBranchEndResponseEncoder(resp.AbstractBranchEndResponse) | |||
} | |||
func GlobalBeginRequestEncoder(in interface{}) []byte { | |||
var ( | |||
zero16 int16 = 0 | |||
b bytes.Buffer | |||
) | |||
w := byteio.BigEndianWriter{Writer: &b} | |||
req, _ := in.(protocol.GlobalBeginRequest) | |||
w.WriteInt32(req.Timeout) | |||
if req.TransactionName != "" { | |||
w.WriteInt16(int16(len(req.TransactionName))) | |||
w.WriteString(req.TransactionName) | |||
} else { | |||
w.WriteInt16(zero16) | |||
} | |||
return b.Bytes() | |||
} | |||
func GlobalBeginResponseEncoder(in interface{}) []byte { | |||
resp := in.(protocol.GlobalBeginResponse) | |||
data := AbstractTransactionResponseEncoder(resp.AbstractTransactionResponse) | |||
var ( | |||
zero16 int16 = 0 | |||
b bytes.Buffer | |||
) | |||
w := byteio.BigEndianWriter{Writer: &b} | |||
if resp.Xid != "" { | |||
w.WriteInt16(int16(len(resp.Xid))) | |||
w.WriteString(resp.Xid) | |||
} else { | |||
w.WriteInt16(zero16) | |||
} | |||
if resp.ExtraData != nil { | |||
w.WriteUint16(uint16(len(resp.ExtraData))) | |||
w.Write(resp.ExtraData) | |||
} else { | |||
w.WriteInt16(zero16) | |||
} | |||
result := append(data, b.Bytes()...) | |||
return result | |||
} | |||
func GlobalCommitRequestEncoder(in interface{}) []byte { | |||
req := in.(protocol.GlobalCommitRequest) | |||
return AbstractGlobalEndRequestEncoder(req.AbstractGlobalEndRequest) | |||
} | |||
func GlobalCommitResponseEncoder(in interface{}) []byte { | |||
resp := in.(protocol.GlobalCommitResponse) | |||
return AbstractGlobalEndResponseEncoder(resp.AbstractGlobalEndResponse) | |||
} | |||
func GlobalLockQueryRequestEncoder(in interface{}) []byte { | |||
return BranchRegisterRequestEncoder(in) | |||
} | |||
func GlobalLockQueryResponseEncoder(in interface{}) []byte { | |||
resp, _ := in.(protocol.GlobalLockQueryResponse) | |||
data := AbstractTransactionResponseEncoder(resp.AbstractTransactionResponse) | |||
var result []byte | |||
if resp.Lockable { | |||
result = append(data, byte(0), byte(1)) | |||
} else { | |||
result = append(data, byte(0), byte(0)) | |||
} | |||
return result | |||
} | |||
func GlobalReportRequestEncoder(in interface{}) []byte { | |||
req, _ := in.(protocol.GlobalReportRequest) | |||
data := AbstractGlobalEndRequestEncoder(req.AbstractGlobalEndRequest) | |||
result := append(data, byte(req.GlobalStatus)) | |||
return result | |||
} | |||
func GlobalReportResponseEncoder(in interface{}) []byte { | |||
resp := in.(protocol.GlobalReportResponse) | |||
return AbstractGlobalEndResponseEncoder(resp.AbstractGlobalEndResponse) | |||
} | |||
func GlobalRollbackRequestEncoder(in interface{}) []byte { | |||
req := in.(protocol.GlobalRollbackRequest) | |||
return AbstractGlobalEndRequestEncoder(req.AbstractGlobalEndRequest) | |||
} | |||
func GlobalRollbackResponseEncoder(in interface{}) []byte { | |||
resp := in.(protocol.GlobalRollbackResponse) | |||
return AbstractGlobalEndResponseEncoder(resp.AbstractGlobalEndResponse) | |||
} | |||
func GlobalStatusRequestEncoder(in interface{}) []byte { | |||
req := in.(protocol.GlobalStatusRequest) | |||
return AbstractGlobalEndRequestEncoder(req.AbstractGlobalEndRequest) | |||
} | |||
func GlobalStatusResponseEncoder(in interface{}) []byte { | |||
resp := in.(protocol.GlobalStatusResponse) | |||
return AbstractGlobalEndResponseEncoder(resp.AbstractGlobalEndResponse) | |||
} | |||
func UndoLogDeleteRequestEncoder(in interface{}) []byte { | |||
var ( | |||
zero16 int16 = 0 | |||
b bytes.Buffer | |||
) | |||
w := byteio.BigEndianWriter{Writer: &b} | |||
req, _ := in.(protocol.UndoLogDeleteRequest) | |||
w.WriteByte(byte(req.BranchType)) | |||
if req.ResourceId != "" { | |||
w.WriteInt16(int16(len(req.ResourceId))) | |||
w.WriteString(req.ResourceId) | |||
} else { | |||
w.WriteInt16(zero16) | |||
} | |||
w.WriteInt16(req.SaveDays) | |||
return b.Bytes() | |||
} |
@@ -0,0 +1,28 @@ | |||
package protocol | |||
var MAGIC_CODE_BYTES = [2]byte{0xda, 0xda} | |||
const ( | |||
VERSION = 1 | |||
// MaxFrameLength max frame length | |||
MaxFrameLength = 8 * 1024 * 1024 | |||
// V1HeadLength v1 head length | |||
V1HeadLength = 16 | |||
// MSGTypeRequest request message type | |||
MSGTypeRequest = 0 | |||
// MSGTypeResponse response message type | |||
MSGTypeResponse = 1 | |||
// MSGTypeRequestOneway request one way | |||
MSGTypeRequestOneway = 2 | |||
// MSGTypeHeartbeatRequest heart beat request | |||
MSGTypeHeartbeatRequest = 3 | |||
// MSGTypeHeartbeatResponse heart beat response | |||
MSGTypeHeartbeatResponse = 4 | |||
) |
@@ -0,0 +1,16 @@ | |||
package protocol | |||
type HeartBeatMessage struct { | |||
Ping bool | |||
} | |||
var HeartBeatMessagePing = HeartBeatMessage{true} | |||
var HeartBeatMessagePong = HeartBeatMessage{false} | |||
func (msg HeartBeatMessage) ToString() string { | |||
if msg.Ping { | |||
return "services ping" | |||
} else { | |||
return "services pong" | |||
} | |||
} |
@@ -0,0 +1,26 @@ | |||
package protocol | |||
type AbstractResultMessage struct { | |||
ResultCode ResultCode | |||
Msg string | |||
} | |||
type AbstractIdentifyRequest struct { | |||
Version string | |||
ApplicationId string `json:"applicationId"` | |||
TransactionServiceGroup string | |||
ExtraData []byte | |||
} | |||
type AbstractIdentifyResponse struct { | |||
AbstractResultMessage | |||
Version string | |||
ExtraData []byte | |||
Identified bool | |||
} |
@@ -0,0 +1,18 @@ | |||
package protocol | |||
type MergedWarpMessage struct { | |||
Msgs []MessageTypeAware | |||
MsgIds []int32 | |||
} | |||
func (req MergedWarpMessage) GetTypeCode() int16 { | |||
return TypeSeataMerge | |||
} | |||
type MergeResultMessage struct { | |||
Msgs []MessageTypeAware | |||
} | |||
func (resp MergeResultMessage) GetTypeCode() int16 { | |||
return TypeSeataMergeResult | |||
} |
@@ -0,0 +1,17 @@ | |||
package protocol | |||
// MessageFuture ... | |||
type MessageFuture struct { | |||
ID int32 | |||
Err error | |||
Response interface{} | |||
Done chan bool | |||
} | |||
// NewMessageFuture ... | |||
func NewMessageFuture(message RpcMessage) *MessageFuture { | |||
return &MessageFuture{ | |||
ID: message.ID, | |||
Done: make(chan bool), | |||
} | |||
} |
@@ -0,0 +1,119 @@ | |||
package protocol | |||
const ( | |||
/** | |||
* The constant TYPE_GLOBAL_BEGIN. | |||
*/ | |||
TypeGlobalBegin = 1 | |||
/** | |||
* The constant TYPE_GLOBAL_BEGIN_RESULT. | |||
*/ | |||
TypeGlobalBeginResult = 2 | |||
/** | |||
* The constant TYPE_GLOBAL_COMMIT. | |||
*/ | |||
TypeGlobalCommit = 7 | |||
/** | |||
* The constant TYPE_GLOBAL_COMMIT_RESULT. | |||
*/ | |||
TypeGlobalCommitResult = 8 | |||
/** | |||
* The constant TYPE_GLOBAL_ROLLBACK. | |||
*/ | |||
TypeGlobalRollback = 9 | |||
/** | |||
* The constant TYPE_GLOBAL_ROLLBACK_RESULT. | |||
*/ | |||
TypeGlobalRollbackResult = 10 | |||
/** | |||
* The constant TYPE_GLOBAL_STATUS. | |||
*/ | |||
TypeGlobalStatus = 15 | |||
/** | |||
* The constant TYPE_GLOBAL_STATUS_RESULT. | |||
*/ | |||
TypeGlobalStatusResult = 16 | |||
/** | |||
* The constant TYPE_GLOBAL_REPORT. | |||
*/ | |||
TypeGlobalReport = 17 | |||
/** | |||
* The constant TYPE_GLOBAL_REPORT_RESULT. | |||
*/ | |||
TypeGlobalReportResult = 18 | |||
/** | |||
* The constant TYPE_GLOBAL_LOCK_QUERY. | |||
*/ | |||
TypeGlobalLockQuery = 21 | |||
/** | |||
* The constant TYPE_GLOBAL_LOCK_QUERY_RESULT. | |||
*/ | |||
TypeGlobalLockQueryResult = 22 | |||
/** | |||
* The constant TYPE_BRANCH_COMMIT. | |||
*/ | |||
TypeBranchCommit = 3 | |||
/** | |||
* The constant TYPE_BRANCH_COMMIT_RESULT. | |||
*/ | |||
TypeBranchCommitResult = 4 | |||
/** | |||
* The constant TYPE_BRANCH_ROLLBACK. | |||
*/ | |||
TypeBranchRollback = 5 | |||
/** | |||
* The constant TYPE_BRANCH_ROLLBACK_RESULT. | |||
*/ | |||
TypeBranchRollbackResult = 6 | |||
/** | |||
* The constant TYPE_BRANCH_REGISTER. | |||
*/ | |||
TypeBranchRegister = 11 | |||
/** | |||
* The constant TYPE_BRANCH_REGISTER_RESULT. | |||
*/ | |||
TypeBranchRegisterResult = 12 | |||
/** | |||
* The constant TYPE_BRANCH_STATUS_REPORT. | |||
*/ | |||
TypeBranchStatusReport = 13 | |||
/** | |||
* The constant TYPE_BRANCH_STATUS_REPORT_RESULT. | |||
*/ | |||
TypeBranchStatusReportResult = 14 | |||
/** | |||
* The constant TYPE_SEATA_MERGE. | |||
*/ | |||
TypeSeataMerge = 59 | |||
/** | |||
* The constant TYPE_SEATA_MERGE_RESULT. | |||
*/ | |||
TypeSeataMergeResult = 60 | |||
/** | |||
* The constant TYPE_REG_CLT. | |||
*/ | |||
TypeRegClt = 101 | |||
/** | |||
* The constant TYPE_REG_CLT_RESULT. | |||
*/ | |||
TypeRegCltResult = 102 | |||
/** | |||
* The constant TYPE_REG_RM. | |||
*/ | |||
TypeRegRm = 103 | |||
/** | |||
* The constant TYPE_REG_RM_RESULT. | |||
*/ | |||
TypeRegRmResult = 104 | |||
/** | |||
* The constant TYPE_RM_DELETE_UNDOLOG. | |||
*/ | |||
TypeRmDeleteUndolog = 111 | |||
/** | |||
* the constant TYPE_HEARTBEAT_MSG | |||
*/ | |||
TypeHeartbeatMsg = 120 | |||
) |
@@ -0,0 +1,5 @@ | |||
package protocol | |||
type MessageTypeAware interface { | |||
GetTypeCode() int16 | |||
} |
@@ -0,0 +1,18 @@ | |||
package protocol | |||
type ResultCode byte | |||
const ( | |||
/** | |||
* ResultCodeFailed result code. | |||
*/ | |||
// ResultCodeFailed | |||
ResultCodeFailed ResultCode = iota | |||
/** | |||
* Success result code. | |||
*/ | |||
// Success | |||
ResultCodeSuccess | |||
) |
@@ -0,0 +1,18 @@ | |||
package protocol | |||
type RegisterRMRequest struct { | |||
AbstractIdentifyRequest | |||
ResourceIds string | |||
} | |||
func (req RegisterRMRequest) GetTypeCode() int16 { | |||
return TypeRegRm | |||
} | |||
type RegisterRMResponse struct { | |||
AbstractIdentifyResponse | |||
} | |||
func (resp RegisterRMResponse) GetTypeCode() int16 { | |||
return TypeRegRmResult | |||
} |
@@ -0,0 +1,10 @@ | |||
package protocol | |||
type RpcMessage struct { | |||
ID int32 | |||
MessageType byte | |||
Codec byte | |||
Compressor byte | |||
HeadMap map[string]string | |||
Body interface{} | |||
} |
@@ -0,0 +1,17 @@ | |||
package protocol | |||
type RegisterTMRequest struct { | |||
AbstractIdentifyRequest | |||
} | |||
func (req RegisterTMRequest) GetTypeCode() int16 { | |||
return TypeRegClt | |||
} | |||
type RegisterTMResponse struct { | |||
AbstractIdentifyResponse | |||
} | |||
func (resp RegisterTMResponse) GetTypeCode() int16 { | |||
return TypeRegCltResult | |||
} |
@@ -0,0 +1,226 @@ | |||
package protocol | |||
import ( | |||
"github.com/seata/seata-go/pkg/model" | |||
) | |||
type AbstractTransactionResponse struct { | |||
AbstractResultMessage | |||
TransactionExceptionCode model.TransactionExceptionCode | |||
} | |||
type AbstractBranchEndRequest struct { | |||
Xid string | |||
BranchId int64 | |||
BranchType model.BranchType | |||
ResourceId string | |||
ApplicationData []byte | |||
} | |||
type AbstractBranchEndResponse struct { | |||
AbstractTransactionResponse | |||
Xid string | |||
BranchId int64 | |||
BranchStatus model.BranchStatus | |||
} | |||
type AbstractGlobalEndRequest struct { | |||
Xid string | |||
ExtraData []byte | |||
} | |||
type AbstractGlobalEndResponse struct { | |||
AbstractTransactionResponse | |||
GlobalStatus model.GlobalStatus | |||
} | |||
type BranchRegisterRequest struct { | |||
Xid string | |||
BranchType model.BranchType | |||
ResourceId string | |||
LockKey string | |||
ApplicationData []byte | |||
} | |||
func (req BranchRegisterRequest) GetTypeCode() int16 { | |||
return TypeBranchRegister | |||
} | |||
type BranchRegisterResponse struct { | |||
AbstractTransactionResponse | |||
BranchId int64 | |||
} | |||
func (resp BranchRegisterResponse) GetTypeCode() int16 { | |||
return TypeBranchRegisterResult | |||
} | |||
type BranchReportRequest struct { | |||
Xid string | |||
BranchId int64 | |||
ResourceId string | |||
Status model.BranchStatus | |||
ApplicationData []byte | |||
BranchType model.BranchType | |||
} | |||
func (req BranchReportRequest) GetTypeCode() int16 { | |||
return TypeBranchStatusReport | |||
} | |||
type BranchReportResponse struct { | |||
AbstractTransactionResponse | |||
} | |||
func (resp BranchReportResponse) GetTypeCode() int16 { | |||
return TypeBranchStatusReportResult | |||
} | |||
type BranchCommitRequest struct { | |||
AbstractBranchEndRequest | |||
} | |||
func (req BranchCommitRequest) GetTypeCode() int16 { | |||
return TypeBranchCommit | |||
} | |||
type BranchCommitResponse struct { | |||
AbstractBranchEndResponse | |||
} | |||
func (resp BranchCommitResponse) GetTypeCode() int16 { | |||
return TypeBranchCommitResult | |||
} | |||
type BranchRollbackRequest struct { | |||
AbstractBranchEndRequest | |||
} | |||
func (req BranchRollbackRequest) GetTypeCode() int16 { | |||
return TypeBranchRollback | |||
} | |||
type BranchRollbackResponse struct { | |||
AbstractBranchEndResponse | |||
} | |||
func (resp BranchRollbackResponse) GetTypeCode() int16 { | |||
return TypeGlobalRollbackResult | |||
} | |||
type GlobalBeginRequest struct { | |||
Timeout int32 | |||
TransactionName string | |||
} | |||
func (req GlobalBeginRequest) GetTypeCode() int16 { | |||
return TypeGlobalBegin | |||
} | |||
type GlobalBeginResponse struct { | |||
AbstractTransactionResponse | |||
Xid string | |||
ExtraData []byte | |||
} | |||
func (resp GlobalBeginResponse) GetTypeCode() int16 { | |||
return TypeGlobalBeginResult | |||
} | |||
type GlobalStatusRequest struct { | |||
AbstractGlobalEndRequest | |||
} | |||
func (req GlobalStatusRequest) GetTypeCode() int16 { | |||
return TypeGlobalStatus | |||
} | |||
type GlobalStatusResponse struct { | |||
AbstractGlobalEndResponse | |||
} | |||
func (resp GlobalStatusResponse) GetTypeCode() int16 { | |||
return TypeGlobalStatusResult | |||
} | |||
type GlobalLockQueryRequest struct { | |||
BranchRegisterRequest | |||
} | |||
func (req GlobalLockQueryRequest) GetTypeCode() int16 { | |||
return TypeGlobalLockQuery | |||
} | |||
type GlobalLockQueryResponse struct { | |||
AbstractTransactionResponse | |||
Lockable bool | |||
} | |||
func (resp GlobalLockQueryResponse) GetTypeCode() int16 { | |||
return TypeGlobalLockQueryResult | |||
} | |||
type GlobalReportRequest struct { | |||
AbstractGlobalEndRequest | |||
GlobalStatus model.GlobalStatus | |||
} | |||
func (req GlobalReportRequest) GetTypeCode() int16 { | |||
return TypeGlobalStatus | |||
} | |||
type GlobalReportResponse struct { | |||
AbstractGlobalEndResponse | |||
} | |||
func (resp GlobalReportResponse) GetTypeCode() int16 { | |||
return TypeGlobalStatusResult | |||
} | |||
type GlobalCommitRequest struct { | |||
AbstractGlobalEndRequest | |||
} | |||
func (req GlobalCommitRequest) GetTypeCode() int16 { | |||
return TypeGlobalCommit | |||
} | |||
type GlobalCommitResponse struct { | |||
AbstractGlobalEndResponse | |||
} | |||
func (resp GlobalCommitResponse) GetTypeCode() int16 { | |||
return TypeGlobalCommitResult | |||
} | |||
type GlobalRollbackRequest struct { | |||
AbstractGlobalEndRequest | |||
} | |||
func (req GlobalRollbackRequest) GetTypeCode() int16 { | |||
return TypeGlobalRollback | |||
} | |||
type GlobalRollbackResponse struct { | |||
AbstractGlobalEndResponse | |||
} | |||
func (resp GlobalRollbackResponse) GetTypeCode() int16 { | |||
return TypeGlobalRollbackResult | |||
} | |||
type UndoLogDeleteRequest struct { | |||
ResourceId string | |||
SaveDays int16 | |||
BranchType model.BranchType | |||
} | |||
func (req UndoLogDeleteRequest) GetTypeCode() int16 { | |||
return TypeRmDeleteUndolog | |||
} |
@@ -0,0 +1,8 @@ | |||
package api | |||
type BusinessActionContext struct { | |||
Xid string | |||
BranchId string | |||
ActionName string | |||
ActionContext map[string]interface{} | |||
} |
@@ -0,0 +1,94 @@ | |||
package rm | |||
import ( | |||
"fmt" | |||
"sync" | |||
) | |||
import ( | |||
"github.com/seata/seata-go/pkg/model" | |||
) | |||
var ( | |||
// BranchType -> ResourceManager | |||
resourceManagerMap sync.Map | |||
// singletone defaultResourceManager | |||
defaultRM *defaultResourceManager | |||
) | |||
func init() { | |||
defaultRM = &defaultResourceManager{} | |||
} | |||
type defaultResourceManager struct { | |||
} | |||
// 将事务管理器注册到这里 | |||
func RegisterResource(resourceManager model.ResourceManager) { | |||
resourceManagerMap.Store(resourceManager.GetBranchType(), resourceManager) | |||
} | |||
func GetDefaultResourceManager() model.ResourceManager { | |||
return defaultRM | |||
} | |||
func (*defaultResourceManager) getResourceManager(branchType model.BranchType) model.ResourceManager { | |||
rm, ok := resourceManagerMap.Load(branchType) | |||
if !ok { | |||
panic(fmt.Sprintf("No ResourceManager for BranchType: %v", branchType)) | |||
} | |||
return rm.(model.ResourceManager) | |||
} | |||
// Commit a branch transaction | |||
func (d *defaultResourceManager) BranchCommit(branchType model.BranchType, xid, branchId int64, resourceId, applicationData string) (model.BranchStatus, error) { | |||
return d.getResourceManager(branchType).BranchCommit(branchType, xid, branchId, resourceId, applicationData) | |||
} | |||
// Rollback a branch transaction | |||
func (d *defaultResourceManager) BranchRollback(branchType model.BranchType, xid string, branchId int64, resourceId, applicationData string) (model.BranchStatus, error) { | |||
return d.getResourceManager(branchType).BranchRollback(branchType, xid, branchId, resourceId, applicationData) | |||
} | |||
// Branch register long | |||
func (d *defaultResourceManager) BranchRegister(branchType model.BranchType, resourceId, clientId, xid, applicationData, lockKeys string) (int64, error) { | |||
return d.getResourceManager(branchType).BranchRegister(branchType, resourceId, clientId, xid, applicationData, lockKeys) | |||
} | |||
// Branch report | |||
func (d *defaultResourceManager) BranchReport(branchType model.BranchType, xid string, branchId int64, status model.BranchStatus, applicationData string) error { | |||
return d.getResourceManager(branchType).BranchReport(branchType, xid, branchId, status, applicationData) | |||
} | |||
// Lock query boolean | |||
func (d *defaultResourceManager) LockQuery(branchType model.BranchType, resourceId, xid, lockKeys string) (bool, error) { | |||
return d.getResourceManager(branchType).LockQuery(branchType, resourceId, xid, lockKeys) | |||
} | |||
// Register a model.Resource to be managed by model.Resource Manager | |||
func (d *defaultResourceManager) RegisterResource(resource model.Resource) error { | |||
return d.getResourceManager(resource.GetBranchType()).RegisterResource(resource) | |||
} | |||
// Unregister a model.Resource from the model.Resource Manager | |||
func (d *defaultResourceManager) UnregisterResource(resource model.Resource) error { | |||
return d.getResourceManager(resource.GetBranchType()).UnregisterResource(resource) | |||
} | |||
// Get all resources managed by this manager | |||
func (d *defaultResourceManager) GetManagedResources() map[string]model.Resource { | |||
var allResource map[string]model.Resource | |||
resourceManagerMap.Range(func(branchType, resourceManager interface{}) bool { | |||
rs := resourceManager.(model.ResourceManager).GetManagedResources() | |||
for k, v := range rs { | |||
rs[k] = v | |||
} | |||
return true | |||
}) | |||
return allResource | |||
} | |||
// Get the model.BranchType | |||
func (d *defaultResourceManager) GetBranchType() model.BranchType { | |||
panic("DefaultResourceManager isn't a real ResourceManager") | |||
} |
@@ -0,0 +1,33 @@ | |||
package tcc | |||
import ( | |||
"reflect" | |||
) | |||
import ( | |||
"github.com/seata/seata-go/pkg/model" | |||
) | |||
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 | |||
} | |||
func (t *TCCResource) GetResourceGroupId() string { | |||
return t.ResourceGroupId | |||
} | |||
func (t *TCCResource) GetResourceId() string { | |||
return t.ActionName | |||
} | |||
func (t *TCCResource) GetBranchType() model.BranchType { | |||
return model.TCC | |||
} |
@@ -0,0 +1,15 @@ | |||
package tcc | |||
import ( | |||
"context" | |||
) | |||
import ( | |||
"github.com/seata/seata-go/pkg/rm/api" | |||
) | |||
type TCCService interface { | |||
Prepare(ctx context.Context, param interface{}) error | |||
Commit(ctx context.Context, businessActionContext api.BusinessActionContext) error | |||
Rollback(ctx context.Context, businessActionContext api.BusinessActionContext) error | |||
} |
@@ -0,0 +1 @@ | |||
package tcc |
@@ -0,0 +1,279 @@ | |||
package rpc11 | |||
import ( | |||
"bytes" | |||
"encoding/binary" | |||
) | |||
import ( | |||
getty "github.com/apache/dubbo-getty" | |||
"github.com/pkg/errors" | |||
"vimagination.zapto.org/byteio" | |||
) | |||
import ( | |||
"github.com/seata/seata-go/pkg/protocol" | |||
"github.com/seata/seata-go/pkg/protocol/codec" | |||
) | |||
/** | |||
* <pre> | |||
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |||
* +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | |||
* | magic |Proto| Full length | Head | Msg |Seria|Compr| RequestID | | |||
* | code |colVer| (head+body) | Length |Type |lizer|ess | | | |||
* +-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+ | |||
* | | | |||
* | Head Map [Optional] | | |||
* +-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+ | |||
* | | | |||
* | body | | |||
* | | | |||
* | ... ... | | |||
* +-----------------------------------------------------------------------------------------------+ | |||
* </pre> | |||
* <p> | |||
* <li>Full Length: include all data </li> | |||
* <li>Head Length: include head data from magic code to head map. </li> | |||
* <li>Body Length: Full Length - Head Length</li> | |||
* </p> | |||
* https://github.com/seata/seata/issues/893 | |||
*/ | |||
const ( | |||
SeataV1PackageHeaderReservedLength = 16 | |||
) | |||
var ( | |||
// RpcPkgHandler | |||
rpcPkgHandler = &RpcPackageHandler{} | |||
) | |||
var ( | |||
ErrNotEnoughStream = errors.New("packet stream is not enough") | |||
ErrTooLargePackage = errors.New("package length is exceed the getty package's legal maximum length.") | |||
ErrInvalidPackage = errors.New("invalid rpc package") | |||
ErrIllegalMagic = errors.New("package magic is not right.") | |||
) | |||
type RpcPackageHandler struct{} | |||
type SeataV1PackageHeader struct { | |||
Magic0 byte | |||
Magic1 byte | |||
Version byte | |||
TotalLength uint32 | |||
HeadLength uint16 | |||
MessageType byte | |||
CodecType byte | |||
CompressType byte | |||
ID uint32 | |||
Meta map[string]string | |||
BodyLength uint32 | |||
} | |||
func (h *SeataV1PackageHeader) Unmarshal(buf *bytes.Buffer) (int, error) { | |||
bufLen := buf.Len() | |||
if bufLen < SeataV1PackageHeaderReservedLength { | |||
return 0, ErrNotEnoughStream | |||
} | |||
// magic | |||
if err := binary.Read(buf, binary.BigEndian, &(h.Magic0)); err != nil { | |||
return 0, err | |||
} | |||
if err := binary.Read(buf, binary.BigEndian, &(h.Magic1)); err != nil { | |||
return 0, err | |||
} | |||
if h.Magic0 != protocol.MAGIC_CODE_BYTES[0] || h.Magic1 != protocol.MAGIC_CODE_BYTES[1] { | |||
return 0, ErrIllegalMagic | |||
} | |||
// version | |||
if err := binary.Read(buf, binary.BigEndian, &(h.Version)); err != nil { | |||
return 0, err | |||
} | |||
// TODO check version compatible here | |||
// total length | |||
if err := binary.Read(buf, binary.BigEndian, &(h.TotalLength)); err != nil { | |||
return 0, err | |||
} | |||
// head length | |||
if err := binary.Read(buf, binary.BigEndian, &(h.HeadLength)); err != nil { | |||
return 0, err | |||
} | |||
// message type | |||
if err := binary.Read(buf, binary.BigEndian, &(h.MessageType)); err != nil { | |||
return 0, err | |||
} | |||
// codec type | |||
if err := binary.Read(buf, binary.BigEndian, &(h.CodecType)); err != nil { | |||
return 0, err | |||
} | |||
// compress type | |||
if err := binary.Read(buf, binary.BigEndian, &(h.CompressType)); err != nil { | |||
return 0, err | |||
} | |||
// id | |||
if err := binary.Read(buf, binary.BigEndian, &(h.ID)); err != nil { | |||
return 0, err | |||
} | |||
// todo meta map | |||
if h.HeadLength > SeataV1PackageHeaderReservedLength { | |||
headMapLength := h.HeadLength - SeataV1PackageHeaderReservedLength | |||
h.Meta = headMapDecode(buf.Bytes()[:headMapLength]) | |||
} | |||
h.BodyLength = h.TotalLength - uint32(h.HeadLength) | |||
return int(h.TotalLength), nil | |||
} | |||
// Read read binary data from to rpc message | |||
func (p *RpcPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { | |||
var header SeataV1PackageHeader | |||
buf := bytes.NewBuffer(data) | |||
_, err := header.Unmarshal(buf) | |||
if err != nil { | |||
if err == ErrNotEnoughStream { | |||
// getty case2 | |||
return nil, 0, nil | |||
} | |||
// getty case1 | |||
return nil, 0, err | |||
} | |||
if uint32(len(data)) < header.TotalLength { | |||
// get case3 | |||
return nil, int(header.TotalLength), nil | |||
} | |||
//r := byteio.BigEndianReader{Reader: bytes.NewReader(data)} | |||
rpcMessage := protocol.RpcMessage{ | |||
Codec: header.CodecType, | |||
ID: int32(header.ID), | |||
Compressor: header.CompressType, | |||
MessageType: header.MessageType, | |||
HeadMap: header.Meta, | |||
} | |||
if header.MessageType == protocol.MSGTypeHeartbeatRequest { | |||
rpcMessage.Body = protocol.HeartBeatMessagePing | |||
} else if header.MessageType == protocol.MSGTypeHeartbeatResponse { | |||
rpcMessage.Body = protocol.HeartBeatMessagePong | |||
} else { | |||
if header.BodyLength > 0 { | |||
//todo compress | |||
msg, _ := codec.MessageDecoder(header.CodecType, data[header.HeadLength:]) | |||
rpcMessage.Body = msg | |||
} | |||
} | |||
return rpcMessage, int(header.TotalLength), nil | |||
} | |||
// Write write rpc message to binary data | |||
func (p *RpcPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) { | |||
msg, ok := pkg.(protocol.RpcMessage) | |||
if !ok { | |||
return nil, ErrInvalidPackage | |||
} | |||
fullLength := protocol.V1HeadLength | |||
headLength := protocol.V1HeadLength | |||
var result = make([]byte, 0, fullLength) | |||
var b bytes.Buffer | |||
w := byteio.BigEndianWriter{Writer: &b} | |||
result = append(result, protocol.MAGIC_CODE_BYTES[:2]...) | |||
result = append(result, protocol.VERSION) | |||
w.WriteByte(msg.MessageType) | |||
w.WriteByte(msg.Codec) | |||
w.WriteByte(msg.Compressor) | |||
w.WriteInt32(msg.ID) | |||
if msg.HeadMap != nil && len(msg.HeadMap) > 0 { | |||
headMapBytes, headMapLength := headMapEncode(msg.HeadMap) | |||
headLength += headMapLength | |||
fullLength += headMapLength | |||
w.Write(headMapBytes) | |||
} | |||
if msg.MessageType != protocol.MSGTypeHeartbeatRequest && | |||
msg.MessageType != protocol.MSGTypeHeartbeatResponse { | |||
bodyBytes := codec.MessageEncoder(msg.Codec, msg.Body) | |||
fullLength += len(bodyBytes) | |||
w.Write(bodyBytes) | |||
} | |||
fullLen := int32(fullLength) | |||
headLen := int16(headLength) | |||
result = append(result, []byte{byte(fullLen >> 24), byte(fullLen >> 16), byte(fullLen >> 8), byte(fullLen)}...) | |||
result = append(result, []byte{byte(headLen >> 8), byte(headLen)}...) | |||
result = append(result, b.Bytes()...) | |||
return result, nil | |||
} | |||
func headMapDecode(data []byte) map[string]string { | |||
size := len(data) | |||
if size == 0 { | |||
return nil | |||
} | |||
mp := make(map[string]string) | |||
r := byteio.BigEndianReader{Reader: bytes.NewReader(data)} | |||
readLength := 0 | |||
for readLength < size { | |||
var key, value string | |||
lengthK, _, _ := r.ReadUint16() | |||
if lengthK < 0 { | |||
break | |||
} else if lengthK == 0 { | |||
key = "" | |||
} else { | |||
key, _, _ = r.ReadString(int(lengthK)) | |||
} | |||
lengthV, _, _ := r.ReadUint16() | |||
if lengthV < 0 { | |||
break | |||
} else if lengthV == 0 { | |||
value = "" | |||
} else { | |||
value, _, _ = r.ReadString(int(lengthV)) | |||
} | |||
mp[key] = value | |||
readLength += int(lengthK + lengthV) | |||
} | |||
return mp | |||
} | |||
func headMapEncode(data map[string]string) ([]byte, int) { | |||
var b bytes.Buffer | |||
w := byteio.BigEndianWriter{Writer: &b} | |||
for k, v := range data { | |||
if k == "" { | |||
w.WriteUint16(0) | |||
} else { | |||
w.WriteUint16(uint16(len(k))) | |||
w.WriteString(k) | |||
} | |||
if v == "" { | |||
w.WriteUint16(0) | |||
} else { | |||
w.WriteUint16(uint16(len(v))) | |||
w.WriteString(v) | |||
} | |||
} | |||
return b.Bytes(), b.Len() | |||
} |
@@ -0,0 +1,159 @@ | |||
package rpc11 | |||
import ( | |||
"fmt" | |||
"net" | |||
"sync" | |||
"sync/atomic" | |||
) | |||
import ( | |||
getty "github.com/apache/dubbo-getty" | |||
gxsync "github.com/dubbogo/gost/sync" | |||
) | |||
import ( | |||
"github.com/seata/seata-go/pkg/config" | |||
log "github.com/seata/seata-go/pkg/util/log" | |||
) | |||
var ( | |||
rpcClient *RpcClient | |||
MAX_CHECK_ALIVE_RETRY = 600 | |||
CHECK_ALIVE_INTERNAL = 100 | |||
) | |||
func init() { | |||
rpcClient = newRpcClient() | |||
} | |||
type RpcClient struct { | |||
lock sync.RWMutex | |||
conf *config.ClientConfig | |||
gettyClients []getty.Client | |||
listener getty.EventListener | |||
// serverAddress -> rpc_client.Session -> bool | |||
serverSessionsMap sync.Map | |||
sessionSize int32 | |||
} | |||
func newRpcClient() *RpcClient { | |||
rpcClient := &RpcClient{ | |||
conf: config.GetClientConfig(), | |||
gettyClients: make([]getty.Client, 0), | |||
listener: NewClientEventHandler(), | |||
} | |||
rpcClient.init() | |||
return rpcClient | |||
} | |||
func (r *RpcClient) init() { | |||
//todo 暂时写死地址,待改为配置 | |||
//addressList := []string{"127.0.0.1:8091"} | |||
addressList := []string{"127.0.0.1:8090"} | |||
if len(addressList) == 0 { | |||
log.Warn("no have valid seata server list") | |||
} | |||
for _, address := range addressList { | |||
gettyClient := getty.NewTCPClient( | |||
getty.WithServerAddress(address), | |||
getty.WithConnectionNumber((int)(r.conf.GettyConfig.ConnectionNum)), | |||
getty.WithReconnectInterval(r.conf.GettyConfig.ReconnectInterval), | |||
getty.WithClientTaskPool(gxsync.NewTaskPoolSimple(2)), | |||
) | |||
gettyClient.RunEventLoop(r.newSession) | |||
r.gettyClients = append(r.gettyClients, gettyClient) | |||
} | |||
} | |||
func (r *RpcClient) newSession(session getty.Session) error { | |||
var ( | |||
ok bool | |||
tcpConn *net.TCPConn | |||
) | |||
if r.conf.GettyConfig.GettySessionParam.CompressEncoding { | |||
session.SetCompressType(getty.CompressZip) | |||
} | |||
if tcpConn, ok = session.Conn().(*net.TCPConn); !ok { | |||
panic(fmt.Sprintf("%s, session.conn{%#v} is not tcp connection\n", session.Stat(), session.Conn())) | |||
} | |||
tcpConn.SetNoDelay(r.conf.GettyConfig.GettySessionParam.TCPNoDelay) | |||
tcpConn.SetKeepAlive(r.conf.GettyConfig.GettySessionParam.TCPKeepAlive) | |||
if r.conf.GettyConfig.GettySessionParam.TCPKeepAlive { | |||
tcpConn.SetKeepAlivePeriod(r.conf.GettyConfig.GettySessionParam.KeepAlivePeriod) | |||
} | |||
tcpConn.SetReadBuffer(r.conf.GettyConfig.GettySessionParam.TCPRBufSize) | |||
tcpConn.SetWriteBuffer(r.conf.GettyConfig.GettySessionParam.TCPWBufSize) | |||
session.SetName(r.conf.GettyConfig.GettySessionParam.SessionName) | |||
session.SetMaxMsgLen(r.conf.GettyConfig.GettySessionParam.MaxMsgLen) | |||
session.SetPkgHandler(rpcPkgHandler) | |||
session.SetEventListener(r.listener) | |||
session.SetReadTimeout(r.conf.GettyConfig.GettySessionParam.TCPReadTimeout) | |||
session.SetWriteTimeout(r.conf.GettyConfig.GettySessionParam.TCPWriteTimeout) | |||
session.SetCronPeriod((int)(r.conf.GettyConfig.HeartbeatPeriod.Nanoseconds() / 1e6)) | |||
session.SetWaitTime(r.conf.GettyConfig.GettySessionParam.WaitTimeout) | |||
log.Debugf("rpc_client new session:%s", session.Stat()) | |||
return nil | |||
} | |||
func (r *RpcClient) AcquireGettySession() getty.Session { | |||
// map 遍历是随机的 | |||
var session getty.Session | |||
r.serverSessionsMap.Range(func(key, value interface{}) bool { | |||
session = key.(getty.Session) | |||
if session.IsClosed() { | |||
r.ReleaseGettySession(session) | |||
} else { | |||
return false | |||
} | |||
return true | |||
}) | |||
if session != nil { | |||
return session | |||
} | |||
//if sessionSize == 0 { | |||
// ticker := time.NewTicker(time.Duration(CHECK_ALIVE_INTERNAL) * time.Millisecond) | |||
// defer ticker.Stop() | |||
// for i := 0; i < MAX_CHECK_ALIVE_RETRY; i++ { | |||
// <-ticker.C | |||
// allSessions.Range(func(key, value interface{}) bool { | |||
// session = key.(getty.Session) | |||
// if session.IsClosed() { | |||
// sessionManager.ReleaseGettySession(session) | |||
// } else { | |||
// return false | |||
// } | |||
// return true | |||
// }) | |||
// if session != nil { | |||
// return session | |||
// } | |||
// } | |||
//} | |||
return nil | |||
} | |||
func (r *RpcClient) RegisterGettySession(session getty.Session) { | |||
//r.serverSessionsMap.Store(session, true) | |||
m, _ := r.serverSessionsMap.LoadOrStore(session.RemoteAddr(), &sync.Map{}) | |||
sMap := m.(*sync.Map) | |||
sMap.Store(session, true) | |||
atomic.AddInt32(&r.sessionSize, 1) | |||
} | |||
func (r *RpcClient) ReleaseGettySession(session getty.Session) { | |||
r.serverSessionsMap.Delete(session) | |||
if !session.IsClosed() { | |||
m, _ := r.serverSessionsMap.LoadOrStore(session.RemoteAddr(), &sync.Map{}) | |||
sMap := m.(*sync.Map) | |||
sMap.Delete(session) | |||
session.Close() | |||
} | |||
atomic.AddInt32(&r.sessionSize, -1) | |||
} |
@@ -0,0 +1,93 @@ | |||
package rpc11 | |||
import ( | |||
"time" | |||
) | |||
import ( | |||
getty "github.com/apache/dubbo-getty" | |||
"github.com/pkg/errors" | |||
"go.uber.org/atomic" | |||
) | |||
import ( | |||
"github.com/seata/seata-go/pkg/protocol" | |||
"github.com/seata/seata-go/pkg/protocol/codec" | |||
log "github.com/seata/seata-go/pkg/util/log" | |||
) | |||
type ClientEventHandler struct { | |||
} | |||
func NewClientEventHandler() *ClientEventHandler { | |||
return &ClientEventHandler{} | |||
} | |||
func (h *ClientEventHandler) OnOpen(session getty.Session) error { | |||
log.Infof("OnOpen session{%s} open", session.Stat()) | |||
rpcClient.RegisterGettySession(session) | |||
return nil | |||
} | |||
func (h *ClientEventHandler) OnError(session getty.Session, err error) { | |||
log.Infof("OnError session{%s} got error{%v}, will be closed.", session.Stat(), err) | |||
} | |||
func (h *ClientEventHandler) OnClose(session getty.Session) { | |||
log.Infof("OnClose session{%s} is closing......", session.Stat()) | |||
} | |||
func (h *ClientEventHandler) OnMessage(session getty.Session, pkg interface{}) { | |||
s, ok := pkg.(string) | |||
if !ok { | |||
log.Infof("illegal packge{%#v}", pkg) | |||
return | |||
} | |||
log.Infof("OnMessage: %s", s) | |||
} | |||
func (h *ClientEventHandler) OnCron(session getty.Session) { | |||
active := session.GetActive() | |||
if 20*time.Second.Nanoseconds() < time.Since(active).Nanoseconds() { | |||
log.Infof("OnCorn session{%s} timeout{%s}", session.Stat(), time.Since(active).String()) | |||
session.Close() | |||
} | |||
} | |||
func (client *ClientEventHandler) sendMergedMessage(mergedMessage protocol.MergedWarpMessage) { | |||
ss := rpcClient.AcquireGettySession() | |||
err := client.sendAsync(ss, mergedMessage) | |||
if err != nil { | |||
log.Errorf("error sendMergedMessage") | |||
} | |||
} | |||
func (client *ClientEventHandler) sendAsync(session getty.Session, msg interface{}) error { | |||
var err error | |||
if session == nil || session.IsClosed() { | |||
log.Warn("sendAsyncRequestWithResponse nothing, caused by null channel.") | |||
} | |||
idGenerator := atomic.Uint32{} | |||
rpcMessage := protocol.RpcMessage{ | |||
ID: int32(idGenerator.Inc()), | |||
MessageType: protocol.MSGTypeRequestOneway, | |||
Codec: codec.SEATA, | |||
Compressor: 0, | |||
Body: msg, | |||
} | |||
log.Infof("store message, id %d : %#v", rpcMessage.ID, msg) | |||
//client.mergeMsgMap.Store(rpcMessage.ID, msg) | |||
//config timeout | |||
pkgLen, sendLen, err := session.WritePkg(rpcMessage, time.Duration(0)) | |||
if err != nil || (pkgLen != 0 && pkgLen != sendLen) { | |||
log.Warnf("start to close the session because %d of %d bytes data is sent success. err:%+v", sendLen, pkgLen, err) | |||
//runtime.GoWithRecover(func() { | |||
// session.Close() | |||
//}, nil) | |||
return errors.Wrap(err, "pkg not send completely!") | |||
} | |||
return nil | |||
} |
@@ -0,0 +1,27 @@ | |||
package rpc11 | |||
import ( | |||
"testing" | |||
"time" | |||
) | |||
import ( | |||
"github.com/seata/seata-go/pkg/protocol" | |||
) | |||
func TestSendMergedMessage(test *testing.T) { | |||
request := protocol.RegisterRMRequest{ | |||
ResourceIds: "1111", | |||
AbstractIdentifyRequest: protocol.AbstractIdentifyRequest{ | |||
ApplicationId: "ApplicationID", | |||
TransactionServiceGroup: "TransactionServiceGroup", | |||
}, | |||
} | |||
mergedMessage := protocol.MergedWarpMessage{ | |||
Msgs: []protocol.MessageTypeAware{request}, | |||
MsgIds: []int32{1212}, | |||
} | |||
handler := NewClientEventHandler() | |||
handler.sendMergedMessage(mergedMessage) | |||
time.Sleep(100000 * time.Second) | |||
} |
@@ -0,0 +1,21 @@ | |||
package rpc_client | |||
import ( | |||
"time" | |||
) | |||
import ( | |||
"github.com/seata/seata-go/pkg/protocol" | |||
) | |||
type ClientMessageSender interface { | |||
// Send msg with response object. | |||
SendMsgWithResponse(msg interface{}) (interface{}, error) | |||
// Send msg with response object. | |||
SendMsgWithResponseAndTimeout(msg interface{}, timeout time.Duration) (interface{}, error) | |||
// Send response. | |||
SendResponse(request protocol.RpcMessage, serverAddress string, msg interface{}) | |||
} |
@@ -0,0 +1,101 @@ | |||
package rpc_client | |||
import ( | |||
"sync" | |||
"sync/atomic" | |||
"time" | |||
) | |||
import ( | |||
getty "github.com/apache/dubbo-getty" | |||
) | |||
var ( | |||
MAX_CHECK_ALIVE_RETRY = 600 | |||
CHECK_ALIVE_INTERNAL = 100 | |||
allSessions = sync.Map{} | |||
// serverAddress -> rpc_client.Session -> bool | |||
serverSessions = sync.Map{} | |||
sessionSize int32 = 0 | |||
clientSessionManager = &GettyClientSessionManager{} | |||
) | |||
type GettyClientSessionManager struct{} | |||
func (sessionManager *GettyClientSessionManager) AcquireGettySession() getty.Session { | |||
// map 遍历是随机的 | |||
var session getty.Session | |||
allSessions.Range(func(key, value interface{}) bool { | |||
session = key.(getty.Session) | |||
if session.IsClosed() { | |||
sessionManager.ReleaseGettySession(session) | |||
} else { | |||
return false | |||
} | |||
return true | |||
}) | |||
if session != nil { | |||
return session | |||
} | |||
if sessionSize == 0 { | |||
ticker := time.NewTicker(time.Duration(CHECK_ALIVE_INTERNAL) * time.Millisecond) | |||
defer ticker.Stop() | |||
for i := 0; i < MAX_CHECK_ALIVE_RETRY; i++ { | |||
<-ticker.C | |||
allSessions.Range(func(key, value interface{}) bool { | |||
session = key.(getty.Session) | |||
if session.IsClosed() { | |||
sessionManager.ReleaseGettySession(session) | |||
} else { | |||
return false | |||
} | |||
return true | |||
}) | |||
if session != nil { | |||
return session | |||
} | |||
} | |||
} | |||
return nil | |||
} | |||
func (sessionManager *GettyClientSessionManager) AcquireGettySessionByServerAddress(serverAddress string) getty.Session { | |||
m, _ := serverSessions.LoadOrStore(serverAddress, &sync.Map{}) | |||
sMap := m.(*sync.Map) | |||
var session getty.Session | |||
sMap.Range(func(key, value interface{}) bool { | |||
session = key.(getty.Session) | |||
if session.IsClosed() { | |||
sessionManager.ReleaseGettySession(session) | |||
} else { | |||
return false | |||
} | |||
return true | |||
}) | |||
return session | |||
} | |||
func (sessionManager *GettyClientSessionManager) ReleaseGettySession(session getty.Session) { | |||
allSessions.Delete(session) | |||
if !session.IsClosed() { | |||
m, _ := serverSessions.LoadOrStore(session.RemoteAddr(), &sync.Map{}) | |||
sMap := m.(*sync.Map) | |||
sMap.Delete(session) | |||
session.Close() | |||
} | |||
atomic.AddInt32(&sessionSize, -1) | |||
} | |||
func (sessionManager *GettyClientSessionManager) RegisterGettySession(session getty.Session) { | |||
allSessions.Store(session, true) | |||
m, _ := serverSessions.LoadOrStore(session.RemoteAddr(), &sync.Map{}) | |||
sMap := m.(*sync.Map) | |||
sMap.Store(session, true) | |||
atomic.AddInt32(&sessionSize, 1) | |||
} |
@@ -0,0 +1,279 @@ | |||
package rpc_client | |||
import ( | |||
"bytes" | |||
"encoding/binary" | |||
) | |||
import ( | |||
getty "github.com/apache/dubbo-getty" | |||
"github.com/pkg/errors" | |||
"vimagination.zapto.org/byteio" | |||
) | |||
import ( | |||
"github.com/seata/seata-go/pkg/protocol" | |||
"github.com/seata/seata-go/pkg/protocol/codec" | |||
) | |||
/** | |||
* <pre> | |||
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |||
* +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | |||
* | magic |Proto| Full length | Head | Msg |Seria|Compr| RequestID | | |||
* | code |colVer| (head+body) | Length |Type |lizer|ess | | | |||
* +-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+ | |||
* | | | |||
* | Head Map [Optional] | | |||
* +-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+ | |||
* | | | |||
* | body | | |||
* | | | |||
* | ... ... | | |||
* +-----------------------------------------------------------------------------------------------+ | |||
* </pre> | |||
* <p> | |||
* <li>Full Length: include all data </li> | |||
* <li>Head Length: include head data from magic code to head map. </li> | |||
* <li>Body Length: Full Length - Head Length</li> | |||
* </p> | |||
* https://github.com/seata/seata/issues/893 | |||
*/ | |||
const ( | |||
SeataV1PackageHeaderReservedLength = 16 | |||
) | |||
var ( | |||
// RpcPkgHandler | |||
rpcPkgHandler = &RpcPackageHandler{} | |||
) | |||
var ( | |||
ErrNotEnoughStream = errors.New("packet stream is not enough") | |||
ErrTooLargePackage = errors.New("package length is exceed the getty package's legal maximum length.") | |||
ErrInvalidPackage = errors.New("invalid rpc package") | |||
ErrIllegalMagic = errors.New("package magic is not right.") | |||
) | |||
type RpcPackageHandler struct{} | |||
type SeataV1PackageHeader struct { | |||
Magic0 byte | |||
Magic1 byte | |||
Version byte | |||
TotalLength uint32 | |||
HeadLength uint16 | |||
MessageType byte | |||
CodecType byte | |||
CompressType byte | |||
ID uint32 | |||
Meta map[string]string | |||
BodyLength uint32 | |||
} | |||
func (h *SeataV1PackageHeader) Unmarshal(buf *bytes.Buffer) (int, error) { | |||
bufLen := buf.Len() | |||
if bufLen < SeataV1PackageHeaderReservedLength { | |||
return 0, ErrNotEnoughStream | |||
} | |||
// magic | |||
if err := binary.Read(buf, binary.BigEndian, &(h.Magic0)); err != nil { | |||
return 0, err | |||
} | |||
if err := binary.Read(buf, binary.BigEndian, &(h.Magic1)); err != nil { | |||
return 0, err | |||
} | |||
if h.Magic0 != protocol.MAGIC_CODE_BYTES[0] || h.Magic1 != protocol.MAGIC_CODE_BYTES[1] { | |||
return 0, ErrIllegalMagic | |||
} | |||
// version | |||
if err := binary.Read(buf, binary.BigEndian, &(h.Version)); err != nil { | |||
return 0, err | |||
} | |||
// TODO check version compatible here | |||
// total length | |||
if err := binary.Read(buf, binary.BigEndian, &(h.TotalLength)); err != nil { | |||
return 0, err | |||
} | |||
// head length | |||
if err := binary.Read(buf, binary.BigEndian, &(h.HeadLength)); err != nil { | |||
return 0, err | |||
} | |||
// message type | |||
if err := binary.Read(buf, binary.BigEndian, &(h.MessageType)); err != nil { | |||
return 0, err | |||
} | |||
// codec type | |||
if err := binary.Read(buf, binary.BigEndian, &(h.CodecType)); err != nil { | |||
return 0, err | |||
} | |||
// compress type | |||
if err := binary.Read(buf, binary.BigEndian, &(h.CompressType)); err != nil { | |||
return 0, err | |||
} | |||
// id | |||
if err := binary.Read(buf, binary.BigEndian, &(h.ID)); err != nil { | |||
return 0, err | |||
} | |||
// todo meta map | |||
if h.HeadLength > SeataV1PackageHeaderReservedLength { | |||
headMapLength := h.HeadLength - SeataV1PackageHeaderReservedLength | |||
h.Meta = headMapDecode(buf.Bytes()[:headMapLength]) | |||
} | |||
h.BodyLength = h.TotalLength - uint32(h.HeadLength) | |||
return int(h.TotalLength), nil | |||
} | |||
// Read read binary data from to rpc message | |||
func (p *RpcPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { | |||
var header SeataV1PackageHeader | |||
buf := bytes.NewBuffer(data) | |||
_, err := header.Unmarshal(buf) | |||
if err != nil { | |||
if err == ErrNotEnoughStream { | |||
// getty case2 | |||
return nil, 0, nil | |||
} | |||
// getty case1 | |||
return nil, 0, err | |||
} | |||
if uint32(len(data)) < header.TotalLength { | |||
// get case3 | |||
return nil, int(header.TotalLength), nil | |||
} | |||
//r := byteio.BigEndianReader{Reader: bytes.NewReader(data)} | |||
rpcMessage := protocol.RpcMessage{ | |||
Codec: header.CodecType, | |||
ID: int32(header.ID), | |||
Compressor: header.CompressType, | |||
MessageType: header.MessageType, | |||
HeadMap: header.Meta, | |||
} | |||
if header.MessageType == protocol.MSGTypeHeartbeatRequest { | |||
rpcMessage.Body = protocol.HeartBeatMessagePing | |||
} else if header.MessageType == protocol.MSGTypeHeartbeatResponse { | |||
rpcMessage.Body = protocol.HeartBeatMessagePong | |||
} else { | |||
if header.BodyLength > 0 { | |||
//todo compress | |||
msg, _ := codec.MessageDecoder(header.CodecType, data[header.HeadLength:]) | |||
rpcMessage.Body = msg | |||
} | |||
} | |||
return rpcMessage, int(header.TotalLength), nil | |||
} | |||
// Write write rpc message to binary data | |||
func (p *RpcPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) { | |||
msg, ok := pkg.(protocol.RpcMessage) | |||
if !ok { | |||
return nil, ErrInvalidPackage | |||
} | |||
fullLength := protocol.V1HeadLength | |||
headLength := protocol.V1HeadLength | |||
var result = make([]byte, 0, fullLength) | |||
var b bytes.Buffer | |||
w := byteio.BigEndianWriter{Writer: &b} | |||
result = append(result, protocol.MAGIC_CODE_BYTES[:2]...) | |||
result = append(result, protocol.VERSION) | |||
w.WriteByte(msg.MessageType) | |||
w.WriteByte(msg.Codec) | |||
w.WriteByte(msg.Compressor) | |||
w.WriteInt32(msg.ID) | |||
if msg.HeadMap != nil && len(msg.HeadMap) > 0 { | |||
headMapBytes, headMapLength := headMapEncode(msg.HeadMap) | |||
headLength += headMapLength | |||
fullLength += headMapLength | |||
w.Write(headMapBytes) | |||
} | |||
if msg.MessageType != protocol.MSGTypeHeartbeatRequest && | |||
msg.MessageType != protocol.MSGTypeHeartbeatResponse { | |||
bodyBytes := codec.MessageEncoder(msg.Codec, msg.Body) | |||
fullLength += len(bodyBytes) | |||
w.Write(bodyBytes) | |||
} | |||
fullLen := int32(fullLength) | |||
headLen := int16(headLength) | |||
result = append(result, []byte{byte(fullLen >> 24), byte(fullLen >> 16), byte(fullLen >> 8), byte(fullLen)}...) | |||
result = append(result, []byte{byte(headLen >> 8), byte(headLen)}...) | |||
result = append(result, b.Bytes()...) | |||
return result, nil | |||
} | |||
func headMapDecode(data []byte) map[string]string { | |||
size := len(data) | |||
if size == 0 { | |||
return nil | |||
} | |||
mp := make(map[string]string) | |||
r := byteio.BigEndianReader{Reader: bytes.NewReader(data)} | |||
readLength := 0 | |||
for readLength < size { | |||
var key, value string | |||
lengthK, _, _ := r.ReadUint16() | |||
if lengthK < 0 { | |||
break | |||
} else if lengthK == 0 { | |||
key = "" | |||
} else { | |||
key, _, _ = r.ReadString(int(lengthK)) | |||
} | |||
lengthV, _, _ := r.ReadUint16() | |||
if lengthV < 0 { | |||
break | |||
} else if lengthV == 0 { | |||
value = "" | |||
} else { | |||
value, _, _ = r.ReadString(int(lengthV)) | |||
} | |||
mp[key] = value | |||
readLength += int(lengthK + lengthV) | |||
} | |||
return mp | |||
} | |||
func headMapEncode(data map[string]string) ([]byte, int) { | |||
var b bytes.Buffer | |||
w := byteio.BigEndianWriter{Writer: &b} | |||
for k, v := range data { | |||
if k == "" { | |||
w.WriteUint16(0) | |||
} else { | |||
w.WriteUint16(uint16(len(k))) | |||
w.WriteString(k) | |||
} | |||
if v == "" { | |||
w.WriteUint16(0) | |||
} else { | |||
w.WriteUint16(uint16(len(v))) | |||
w.WriteString(v) | |||
} | |||
} | |||
return b.Bytes(), b.Len() | |||
} |
@@ -0,0 +1,102 @@ | |||
package rpc_client | |||
import ( | |||
"fmt" | |||
"github.com/seata/seata-go/pkg/config" | |||
"github.com/seata/seata-go/pkg/util/log" | |||
"net" | |||
) | |||
import ( | |||
getty "github.com/apache/dubbo-getty" | |||
gxsync "github.com/dubbogo/gost/sync" | |||
) | |||
type RpcClient struct { | |||
conf *config.ClientConfig | |||
gettyClients []getty.Client | |||
rpcHandler RpcRemoteClient | |||
} | |||
func init() { | |||
newRpcClient() | |||
} | |||
func newRpcClient() *RpcClient { | |||
rpcClient := &RpcClient{ | |||
conf: config.GetClientConfig(), | |||
gettyClients: make([]getty.Client, 0), | |||
rpcHandler: *InitRpcRemoteClient(), | |||
} | |||
rpcClient.init() | |||
return rpcClient | |||
} | |||
func (c *RpcClient) init() { | |||
addressList := getAvailServerList(c.conf) | |||
if len(addressList) == 0 { | |||
log.Warn("no have valid seata server list") | |||
} | |||
for _, address := range addressList { | |||
gettyClient := getty.NewTCPClient( | |||
getty.WithServerAddress(address), | |||
getty.WithConnectionNumber((int)(c.conf.GettyConfig.ConnectionNum)), | |||
getty.WithReconnectInterval(c.conf.GettyConfig.ReconnectInterval), | |||
getty.WithClientTaskPool(gxsync.NewTaskPoolSimple(0)), | |||
) | |||
go gettyClient.RunEventLoop(c.newSession) | |||
// c.gettyClients = append(c.gettyClients, gettyClient) | |||
} | |||
} | |||
// todo mock | |||
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"} | |||
} | |||
func (c *RpcClient) newSession(session getty.Session) error { | |||
var ( | |||
ok bool | |||
tcpConn *net.TCPConn | |||
) | |||
if c.conf.GettyConfig.GettySessionParam.CompressEncoding { | |||
session.SetCompressType(getty.CompressZip) | |||
} | |||
if tcpConn, ok = session.Conn().(*net.TCPConn); !ok { | |||
panic(fmt.Sprintf("%s, session.conn{%#v} is not tcp connection\n", session.Stat(), session.Conn())) | |||
} | |||
tcpConn.SetNoDelay(c.conf.GettyConfig.GettySessionParam.TCPNoDelay) | |||
tcpConn.SetKeepAlive(c.conf.GettyConfig.GettySessionParam.TCPKeepAlive) | |||
if c.conf.GettyConfig.GettySessionParam.TCPKeepAlive { | |||
tcpConn.SetKeepAlivePeriod(c.conf.GettyConfig.GettySessionParam.KeepAlivePeriod) | |||
} | |||
tcpConn.SetReadBuffer(c.conf.GettyConfig.GettySessionParam.TCPRBufSize) | |||
tcpConn.SetWriteBuffer(c.conf.GettyConfig.GettySessionParam.TCPWBufSize) | |||
session.SetName(c.conf.GettyConfig.GettySessionParam.SessionName) | |||
session.SetMaxMsgLen(c.conf.GettyConfig.GettySessionParam.MaxMsgLen) | |||
session.SetPkgHandler(rpcPkgHandler) | |||
session.SetEventListener(&c.rpcHandler) | |||
session.SetReadTimeout(c.conf.GettyConfig.GettySessionParam.TCPReadTimeout) | |||
session.SetWriteTimeout(c.conf.GettyConfig.GettySessionParam.TCPWriteTimeout) | |||
session.SetCronPeriod((int)(c.conf.GettyConfig.HeartbeatPeriod.Nanoseconds() / 1e6)) | |||
session.SetWaitTime(c.conf.GettyConfig.GettySessionParam.WaitTimeout) | |||
log.Debugf("rpc_client new session:%s\n", session.Stat()) | |||
return nil | |||
} |
@@ -0,0 +1,412 @@ | |||
package rpc_client | |||
import ( | |||
"math/rand" | |||
"strings" | |||
"sync" | |||
"time" | |||
) | |||
import ( | |||
getty "github.com/apache/dubbo-getty" | |||
gxtime "github.com/dubbogo/gost/time" | |||
"github.com/pkg/errors" | |||
"go.uber.org/atomic" | |||
) | |||
import ( | |||
"github.com/seata/seata-go/pkg/config" | |||
"github.com/seata/seata-go/pkg/protocol" | |||
"github.com/seata/seata-go/pkg/protocol/codec" | |||
log "github.com/seata/seata-go/pkg/util/log" | |||
"github.com/seata/seata-go/pkg/util/runtime" | |||
) | |||
const ( | |||
RPC_REQUEST_TIMEOUT = 30 * time.Second | |||
) | |||
var rpcRemoteClient *RpcRemoteClient | |||
func InitRpcRemoteClient() *RpcRemoteClient { | |||
rpcRemoteClient = &RpcRemoteClient{ | |||
conf: config.GetClientConfig(), | |||
idGenerator: &atomic.Uint32{}, | |||
futures: &sync.Map{}, | |||
mergeMsgMap: &sync.Map{}, | |||
rpcMessageChannel: make(chan protocol.RpcMessage, 100), | |||
BranchRollbackRequestChannel: make(chan RpcRMMessage), | |||
BranchCommitRequestChannel: make(chan RpcRMMessage), | |||
GettySessionOnOpenChannel: make(chan string), | |||
} | |||
if rpcRemoteClient.conf.EnableClientBatchSendRequest { | |||
go rpcRemoteClient.processMergedMessage() | |||
} | |||
return rpcRemoteClient | |||
} | |||
func GetRpcRemoteClient() *RpcRemoteClient { | |||
return rpcRemoteClient | |||
} | |||
type RpcRemoteClient struct { | |||
conf *config.ClientConfig | |||
idGenerator *atomic.Uint32 | |||
futures *sync.Map | |||
mergeMsgMap *sync.Map | |||
rpcMessageChannel chan protocol.RpcMessage | |||
BranchCommitRequestChannel chan RpcRMMessage | |||
BranchRollbackRequestChannel chan RpcRMMessage | |||
GettySessionOnOpenChannel chan string | |||
} | |||
// OnOpen ... | |||
func (client *RpcRemoteClient) OnOpen(session getty.Session) error { | |||
go func() { | |||
request := protocol.RegisterTMRequest{AbstractIdentifyRequest: protocol.AbstractIdentifyRequest{ | |||
Version: client.conf.SeataVersion, | |||
ApplicationId: client.conf.ApplicationID, | |||
TransactionServiceGroup: client.conf.TransactionServiceGroup, | |||
}} | |||
_, err := client.sendAsyncRequestWithResponse(session, request, RPC_REQUEST_TIMEOUT) | |||
if err == nil { | |||
clientSessionManager.RegisterGettySession(session) | |||
client.GettySessionOnOpenChannel <- session.RemoteAddr() | |||
} | |||
}() | |||
return nil | |||
} | |||
// OnError ... | |||
func (client *RpcRemoteClient) OnError(session getty.Session, err error) { | |||
clientSessionManager.ReleaseGettySession(session) | |||
} | |||
// OnClose ... | |||
func (client *RpcRemoteClient) OnClose(session getty.Session) { | |||
clientSessionManager.ReleaseGettySession(session) | |||
} | |||
// OnMessage ... | |||
func (client *RpcRemoteClient) OnMessage(session getty.Session, pkg interface{}) { | |||
log.Debugf("received message: {%#v}", pkg) | |||
rpcMessage, ok := pkg.(protocol.RpcMessage) | |||
if !ok { | |||
log.Errorf("received message is not protocol.RpcMessage. pkg: %#v", pkg) | |||
return | |||
} | |||
heartBeat, isHeartBeat := rpcMessage.Body.(protocol.HeartBeatMessage) | |||
if isHeartBeat && heartBeat == protocol.HeartBeatMessagePong { | |||
log.Debugf("received PONG from %s", session.RemoteAddr()) | |||
return | |||
} | |||
if rpcMessage.MessageType == protocol.MSGTypeRequest || | |||
rpcMessage.MessageType == protocol.MSGTypeRequestOneway { | |||
log.Debugf("msgID: %d, body: %#v", rpcMessage.ID, rpcMessage.Body) | |||
client.onMessage(rpcMessage, session.RemoteAddr()) | |||
return | |||
} | |||
mergedResult, isMergedResult := rpcMessage.Body.(protocol.MergeResultMessage) | |||
if isMergedResult { | |||
mm, loaded := client.mergeMsgMap.Load(rpcMessage.ID) | |||
if loaded { | |||
mergedMessage := mm.(protocol.MergedWarpMessage) | |||
log.Infof("rpcMessageID: %d, rpcMessage: %#v, result: %#v", rpcMessage.ID, mergedMessage, mergedResult) | |||
for i := 0; i < len(mergedMessage.Msgs); i++ { | |||
msgID := mergedMessage.MsgIds[i] | |||
resp, loaded := client.futures.Load(msgID) | |||
if loaded { | |||
response := resp.(*protocol.MessageFuture) | |||
response.Response = mergedResult.Msgs[i] | |||
response.Done <- true | |||
client.futures.Delete(msgID) | |||
} | |||
} | |||
client.mergeMsgMap.Delete(rpcMessage.ID) | |||
} | |||
} else { | |||
resp, loaded := client.futures.Load(rpcMessage.ID) | |||
if loaded { | |||
response := resp.(*protocol.MessageFuture) | |||
response.Response = rpcMessage.Body | |||
response.Done <- true | |||
client.futures.Delete(rpcMessage.ID) | |||
} | |||
} | |||
} | |||
// OnCron ... | |||
func (client *RpcRemoteClient) OnCron(session getty.Session) { | |||
client.defaultSendRequest(session, protocol.HeartBeatMessagePing) | |||
} | |||
func (client *RpcRemoteClient) onMessage(rpcMessage protocol.RpcMessage, serverAddress string) { | |||
msg := rpcMessage.Body.(protocol.MessageTypeAware) | |||
log.Debugf("onMessage: %#v", msg) | |||
switch msg.GetTypeCode() { | |||
case protocol.TypeBranchCommit: | |||
client.BranchCommitRequestChannel <- RpcRMMessage{ | |||
RpcMessage: rpcMessage, | |||
ServerAddress: serverAddress, | |||
} | |||
case protocol.TypeBranchRollback: | |||
client.BranchRollbackRequestChannel <- RpcRMMessage{ | |||
RpcMessage: rpcMessage, | |||
ServerAddress: serverAddress, | |||
} | |||
case protocol.TypeRmDeleteUndolog: | |||
break | |||
default: | |||
break | |||
} | |||
} | |||
//************************************* | |||
// ClientMessageSender | |||
//************************************* | |||
func (client *RpcRemoteClient) SendMsgWithResponse(msg interface{}) (interface{}, error) { | |||
if client.conf.EnableClientBatchSendRequest { | |||
return client.sendAsyncRequest2(msg, RPC_REQUEST_TIMEOUT) | |||
} | |||
return client.SendMsgWithResponseAndTimeout(msg, RPC_REQUEST_TIMEOUT) | |||
} | |||
func (client *RpcRemoteClient) SendMsgWithResponseAndTimeout(msg interface{}, timeout time.Duration) (interface{}, error) { | |||
ss := clientSessionManager.AcquireGettySession() | |||
return client.sendAsyncRequestWithResponse(ss, msg, timeout) | |||
} | |||
func (client *RpcRemoteClient) SendResponse(request protocol.RpcMessage, serverAddress string, msg interface{}) { | |||
client.defaultSendResponse(request, clientSessionManager.AcquireGettySessionByServerAddress(serverAddress), msg) | |||
} | |||
func (client *RpcRemoteClient) sendAsyncRequestWithResponse(session getty.Session, msg interface{}, timeout time.Duration) (interface{}, error) { | |||
if timeout <= time.Duration(0) { | |||
return nil, errors.New("timeout should more than 0ms") | |||
} | |||
return client.sendAsyncRequest(session, msg, timeout) | |||
} | |||
func (client *RpcRemoteClient) sendAsyncRequestWithoutResponse(session getty.Session, msg interface{}) error { | |||
_, err := client.sendAsyncRequest(session, msg, time.Duration(0)) | |||
return err | |||
} | |||
func (client *RpcRemoteClient) sendAsyncRequest(session getty.Session, msg interface{}, timeout time.Duration) (interface{}, error) { | |||
var err error | |||
if session == nil || session.IsClosed() { | |||
log.Warn("sendAsyncRequestWithResponse nothing, caused by null channel.") | |||
} | |||
rpcMessage := protocol.RpcMessage{ | |||
ID: int32(client.idGenerator.Inc()), | |||
MessageType: protocol.MSGTypeRequestOneway, | |||
Codec: codec.SEATA, | |||
Compressor: 0, | |||
Body: msg, | |||
} | |||
resp := protocol.NewMessageFuture(rpcMessage) | |||
client.futures.Store(rpcMessage.ID, resp) | |||
//config timeout | |||
_, _, err = session.WritePkg(rpcMessage, time.Duration(0)) | |||
if err != nil { | |||
client.futures.Delete(rpcMessage.ID) | |||
log.Errorf("send message: %#v, session: %s", rpcMessage, session.Stat()) | |||
return nil, err | |||
} | |||
log.Debugf("send message: %#v, session: %s", rpcMessage, session.Stat()) | |||
if timeout > time.Duration(0) { | |||
select { | |||
case <-gxtime.GetDefaultTimerWheel().After(timeout): | |||
client.futures.Delete(rpcMessage.ID) | |||
if session != nil { | |||
return nil, errors.Errorf("wait response timeout, ip: %s, request: %#v", session.RemoteAddr(), rpcMessage) | |||
} else { | |||
return nil, errors.Errorf("wait response timeout and session is nil, request: %#v", rpcMessage) | |||
} | |||
case <-resp.Done: | |||
err = resp.Err | |||
return resp.Response, err | |||
} | |||
} | |||
return nil, err | |||
} | |||
func (client *RpcRemoteClient) sendAsyncRequest2(msg interface{}, timeout time.Duration) (interface{}, error) { | |||
var err error | |||
rpcMessage := protocol.RpcMessage{ | |||
ID: int32(client.idGenerator.Inc()), | |||
MessageType: protocol.MSGTypeRequest, | |||
Codec: codec.SEATA, | |||
Compressor: 0, | |||
Body: msg, | |||
} | |||
resp := protocol.NewMessageFuture(rpcMessage) | |||
client.futures.Store(rpcMessage.ID, resp) | |||
client.rpcMessageChannel <- rpcMessage | |||
log.Infof("send message: %#v", rpcMessage) | |||
if timeout > time.Duration(0) { | |||
select { | |||
case <-gxtime.GetDefaultTimerWheel().After(timeout): | |||
client.futures.Delete(rpcMessage.ID) | |||
return nil, errors.Errorf("wait response timeout, request: %#v", rpcMessage) | |||
case <-resp.Done: | |||
err = resp.Err | |||
} | |||
return resp.Response, err | |||
} | |||
return nil, err | |||
} | |||
func (client *RpcRemoteClient) sendAsync(session getty.Session, msg interface{}) error { | |||
var err error | |||
if session == nil || session.IsClosed() { | |||
log.Warn("sendAsyncRequestWithResponse nothing, caused by null channel.") | |||
} | |||
rpcMessage := protocol.RpcMessage{ | |||
ID: int32(client.idGenerator.Inc()), | |||
MessageType: protocol.MSGTypeRequestOneway, | |||
Codec: codec.SEATA, | |||
Compressor: 0, | |||
Body: msg, | |||
} | |||
log.Infof("store message, id %d : %#v", rpcMessage.ID, msg) | |||
client.mergeMsgMap.Store(rpcMessage.ID, msg) | |||
//config timeout | |||
pkgLen, sendLen, err := session.WritePkg(rpcMessage, time.Duration(0)) | |||
if err != nil || (pkgLen != 0 && pkgLen != sendLen) { | |||
log.Warnf("start to close the session because %d of %d bytes data is sent success. err:%+v", sendLen, pkgLen, err) | |||
runtime.GoWithRecover(func() { | |||
session.Close() | |||
}, nil) | |||
return errors.Wrap(err, "pkg not send completely!") | |||
} | |||
return nil | |||
} | |||
func (client *RpcRemoteClient) defaultSendRequest(session getty.Session, msg interface{}) { | |||
rpcMessage := protocol.RpcMessage{ | |||
ID: int32(client.idGenerator.Inc()), | |||
Codec: codec.SEATA, | |||
Compressor: 0, | |||
Body: msg, | |||
} | |||
_, ok := msg.(protocol.HeartBeatMessage) | |||
if ok { | |||
rpcMessage.MessageType = protocol.MSGTypeHeartbeatRequest | |||
} else { | |||
rpcMessage.MessageType = protocol.MSGTypeRequest | |||
} | |||
pkgLen, sendLen, err := session.WritePkg(rpcMessage, client.conf.GettyConfig.GettySessionParam.TCPWriteTimeout) | |||
if err != nil || (pkgLen != 0 && pkgLen != sendLen) { | |||
log.Warnf("start to close the session because %d of %d bytes data is sent success. err:%+v", sendLen, pkgLen, err) | |||
runtime.GoWithRecover(func() { | |||
session.Close() | |||
}, nil) | |||
} | |||
} | |||
func (client *RpcRemoteClient) defaultSendResponse(request protocol.RpcMessage, session getty.Session, msg interface{}) { | |||
resp := protocol.RpcMessage{ | |||
ID: request.ID, | |||
Codec: request.Codec, | |||
Compressor: request.Compressor, | |||
Body: msg, | |||
} | |||
_, ok := msg.(protocol.HeartBeatMessage) | |||
if ok { | |||
resp.MessageType = protocol.MSGTypeHeartbeatResponse | |||
} else { | |||
resp.MessageType = protocol.MSGTypeResponse | |||
} | |||
pkgLen, sendLen, err := session.WritePkg(resp, time.Duration(0)) | |||
if err != nil || (pkgLen != 0 && pkgLen != sendLen) { | |||
log.Warnf("start to close the session because %d of %d bytes data is sent success. err:%+v", sendLen, pkgLen, err) | |||
runtime.GoWithRecover(func() { | |||
session.Close() | |||
}, nil) | |||
} | |||
} | |||
func (client *RpcRemoteClient) 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 *RpcRemoteClient) 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 *RpcRemoteClient) 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) | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,24 @@ | |||
package rpc_client | |||
import ( | |||
"github.com/seata/seata-go/pkg/protocol" | |||
"testing" | |||
"time" | |||
) | |||
func TestSendMsgWithResponse(test *testing.T) { | |||
request := protocol.RegisterRMRequest{ | |||
ResourceIds: "1111", | |||
AbstractIdentifyRequest: protocol.AbstractIdentifyRequest{ | |||
ApplicationId: "ApplicationID", | |||
TransactionServiceGroup: "TransactionServiceGroup", | |||
}, | |||
} | |||
mergedMessage := protocol.MergedWarpMessage{ | |||
Msgs: []protocol.MessageTypeAware{request}, | |||
MsgIds: []int32{1212}, | |||
} | |||
handler := GetRpcRemoteClient() | |||
handler.sendMergedMessage(mergedMessage) | |||
time.Sleep(100000 * time.Second) | |||
} |
@@ -0,0 +1,10 @@ | |||
package rpc_client | |||
import ( | |||
"github.com/seata/seata-go/pkg/protocol" | |||
) | |||
type RpcRMMessage struct { | |||
RpcMessage protocol.RpcMessage | |||
ServerAddress string | |||
} |
@@ -0,0 +1,45 @@ | |||
package api | |||
import ( | |||
"github.com/seata/seata-go/pkg/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,11 @@ | |||
package api | |||
type SuspendedResourcesHolder struct { | |||
Xid string | |||
} | |||
func NewSuspendedResourcesHolder(xid string) SuspendedResourcesHolder { | |||
return SuspendedResourcesHolder{ | |||
Xid: xid, | |||
} | |||
} |
@@ -0,0 +1,218 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package log | |||
import ( | |||
"bytes" | |||
"errors" | |||
"fmt" | |||
) | |||
import ( | |||
"github.com/natefinch/lumberjack" | |||
"go.uber.org/zap" | |||
"go.uber.org/zap/zapcore" | |||
) | |||
// Level represents the level of logging. | |||
type LogLevel int8 | |||
const ( | |||
// DebugLevel logs are typically voluminous, and are usually disabled in | |||
// production. | |||
DebugLevel = LogLevel(zapcore.DebugLevel) | |||
// InfoLevel is the default logging priority. | |||
InfoLevel = LogLevel(zapcore.InfoLevel) | |||
// WarnLevel logs are more important than Info, but don't need individual | |||
// human review. | |||
WarnLevel = LogLevel(zapcore.WarnLevel) | |||
// ErrorLevel logs are high-priority. If an application is running smoothly, | |||
// it shouldn't generate any error-level logs. | |||
ErrorLevel = LogLevel(zapcore.ErrorLevel) | |||
// PanicLevel logs a message, then panics. | |||
PanicLevel = LogLevel(zapcore.PanicLevel) | |||
// FatalLevel logs a message, then calls os.Exit(1). | |||
FatalLevel = LogLevel(zapcore.FatalLevel) | |||
) | |||
func (l *LogLevel) UnmarshalText(text []byte) error { | |||
if l == nil { | |||
return errors.New("can't unmarshal a nil *Level") | |||
} | |||
if !l.unmarshalText(text) && !l.unmarshalText(bytes.ToLower(text)) { | |||
return fmt.Errorf("unrecognized level: %q", text) | |||
} | |||
return nil | |||
} | |||
func (l *LogLevel) unmarshalText(text []byte) bool { | |||
switch string(text) { | |||
case "debug", "DEBUG": | |||
*l = DebugLevel | |||
case "info", "INFO", "": // make the zero value useful | |||
*l = InfoLevel | |||
case "warn", "WARN": | |||
*l = WarnLevel | |||
case "error", "ERROR": | |||
*l = ErrorLevel | |||
case "panic", "PANIC": | |||
*l = PanicLevel | |||
case "fatal", "FATAL": | |||
*l = FatalLevel | |||
default: | |||
return false | |||
} | |||
return true | |||
} | |||
type Logger interface { | |||
Debug(v ...interface{}) | |||
Debugf(format string, v ...interface{}) | |||
Info(v ...interface{}) | |||
Infof(format string, v ...interface{}) | |||
Warn(v ...interface{}) | |||
Warnf(format string, v ...interface{}) | |||
Error(v ...interface{}) | |||
Errorf(format string, v ...interface{}) | |||
Panic(v ...interface{}) | |||
Panicf(format string, v ...interface{}) | |||
Fatal(v ...interface{}) | |||
Fatalf(format string, v ...interface{}) | |||
} | |||
var ( | |||
log Logger | |||
zapLogger *zap.Logger | |||
zapLoggerConfig = zap.NewDevelopmentConfig() | |||
zapLoggerEncoderConfig = zapcore.EncoderConfig{ | |||
TimeKey: "time", | |||
LevelKey: "level", | |||
NameKey: "logger", | |||
CallerKey: "caller", | |||
MessageKey: "message", | |||
StacktraceKey: "stacktrace", | |||
EncodeLevel: zapcore.CapitalColorLevelEncoder, | |||
EncodeTime: zapcore.ISO8601TimeEncoder, | |||
EncodeDuration: zapcore.SecondsDurationEncoder, | |||
EncodeCaller: zapcore.ShortCallerEncoder, | |||
} | |||
) | |||
func init() { | |||
zapLoggerConfig.EncoderConfig = zapLoggerEncoderConfig | |||
zapLogger, _ = zapLoggerConfig.Build(zap.AddCallerSkip(1)) | |||
log = zapLogger.Sugar() | |||
} | |||
func Init(logPath string, level LogLevel) { | |||
lumberJackLogger := &lumberjack.Logger{ | |||
Filename: logPath, | |||
MaxSize: 10, | |||
MaxBackups: 5, | |||
MaxAge: 30, | |||
Compress: false, | |||
} | |||
syncer := zapcore.AddSync(lumberJackLogger) | |||
encoderConfig := zap.NewProductionEncoderConfig() | |||
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder | |||
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder | |||
encoder := zapcore.NewConsoleEncoder(encoderConfig) | |||
core := zapcore.NewCore(encoder, syncer, zap.NewAtomicLevelAt(zapcore.Level(level))) | |||
zapLogger = zap.New(core, zap.AddCaller()) | |||
log = zapLogger.Sugar() | |||
} | |||
// SetLogger: customize yourself logger. | |||
func SetLogger(logger Logger) { | |||
log = logger | |||
} | |||
// GetLogger get logger | |||
func GetLogger() Logger { | |||
return log | |||
} | |||
// Debug ... | |||
func Debug(v ...interface{}) { | |||
log.Debug(v...) | |||
} | |||
// Debugf ... | |||
func Debugf(format string, v ...interface{}) { | |||
log.Debugf(format, v...) | |||
} | |||
// Info ... | |||
func Info(v ...interface{}) { | |||
log.Info(v...) | |||
} | |||
// Infof ... | |||
func Infof(format string, v ...interface{}) { | |||
log.Infof(format, v...) | |||
} | |||
// Warn ... | |||
func Warn(v ...interface{}) { | |||
log.Warn(v...) | |||
} | |||
// Warnf ... | |||
func Warnf(format string, v ...interface{}) { | |||
log.Warnf(format, v...) | |||
} | |||
// Error ... | |||
func Error(v ...interface{}) { | |||
log.Error(v...) | |||
} | |||
// Errorf ... | |||
func Errorf(format string, v ...interface{}) { | |||
log.Errorf(format, v...) | |||
} | |||
// Panic ... | |||
func Panic(v ...interface{}) { | |||
log.Panic(v...) | |||
} | |||
// Panicf ... | |||
func Panicf(format string, v ...interface{}) { | |||
log.Panicf(format, v...) | |||
} | |||
// Fatal ... | |||
func Fatal(v ...interface{}) { | |||
log.Fatal(v...) | |||
} | |||
// Fatalf ... | |||
func Fatalf(format string, v ...interface{}) { | |||
log.Fatalf(format, v...) | |||
} |
@@ -0,0 +1,38 @@ | |||
package runtime | |||
import ( | |||
"fmt" | |||
"os" | |||
"runtime/debug" | |||
"time" | |||
) | |||
var debugIgnoreStdout = false | |||
// GoWithRecover wraps a `go func()` with recover() | |||
func GoWithRecover(handler func(), recoverHandler func(r interface{})) { | |||
go func() { | |||
defer func() { | |||
if r := recover(); r != nil { | |||
// TODO: log | |||
if !debugIgnoreStdout { | |||
fmt.Fprintf(os.Stderr, "%s goroutine panic: %v\n%s\n", | |||
time.Now(), r, string(debug.Stack())) | |||
} | |||
if recoverHandler != nil { | |||
go func() { | |||
defer func() { | |||
if p := recover(); p != nil { | |||
if !debugIgnoreStdout { | |||
fmt.Fprintf(os.Stderr, "recover goroutine panic:%v\n%s\n", p, string(debug.Stack())) | |||
} | |||
} | |||
}() | |||
recoverHandler(r) | |||
}() | |||
} | |||
} | |||
}() | |||
handler() | |||
}() | |||
} |
@@ -0,0 +1,26 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package test | |||
import ( | |||
"testing" | |||
) | |||
func Test1(t *testing.T) { | |||
} |
@@ -0,0 +1,29 @@ | |||
DROP TABLE IF EXISTS `stock_tbl`; | |||
CREATE TABLE `stock_tbl` | |||
( | |||
`id` int(11) NOT NULL AUTO_INCREMENT, | |||
`commodity_code` varchar(255) DEFAULT NULL, | |||
`count` int(11) DEFAULT 0, | |||
PRIMARY KEY (`id`), | |||
UNIQUE KEY (`commodity_code`) | |||
) ENGINE=InnoDB DEFAULT CHARSET=utf8; | |||
DROP TABLE IF EXISTS `order_tbl`; | |||
CREATE TABLE `order_tbl` | |||
( | |||
`id` int(11) NOT NULL AUTO_INCREMENT, | |||
`user_id` varchar(255) DEFAULT NULL, | |||
`commodity_code` varchar(255) DEFAULT NULL, | |||
`count` int(11) DEFAULT 0, | |||
`money` int(11) DEFAULT 0, | |||
PRIMARY KEY (`id`) | |||
) ENGINE=InnoDB DEFAULT CHARSET=utf8; | |||
DROP TABLE IF EXISTS `account_tbl`; | |||
CREATE TABLE `account_tbl` | |||
( | |||
`id` int(11) NOT NULL AUTO_INCREMENT, | |||
`user_id` varchar(255) DEFAULT NULL, | |||
`money` int(11) DEFAULT 0, | |||
PRIMARY KEY (`id`) | |||
) ENGINE=InnoDB DEFAULT CHARSET=utf8; |