goserver_sync/core/transact/transnode.go

447 lines
10 KiB
Go

// transnode
package transact
import (
"time"
"mongo.games.com/goserver/core/basic"
"mongo.games.com/goserver/core/timer"
"sync"
"sync/atomic"
)
const (
///transact execute result
TransResult_Success int = iota
TransResult_Failed
TransResult_TimeOut
TransResult_Max
)
const (
///transact result
TransExeResult_Success TransExeResult = iota
TransExeResult_Failed
TransExeResult_Yield
TransExeResult_NullPointer
TransExeResult_NoStart
TransExeResult_NoSetHandler
TransExeResult_ChildNodeNotExist
TransExeResult_ChildNodeRepeateRet
TransExeResult_AsynFailed
TransExeResult_HadDone
TransExeResult_StartChildFailed
TransExeResult_UnsafeExecuteEnv
)
const (
///transact owner type
TransOwnerType_Invalid TransOwnerType = iota
TransOwnerType_Max
)
const (
///transact command type
TransCmd_Invalid TransCmd = iota
TransCmd_Commit
TransCmd_RollBack
)
const (
///transact
TransRootNodeLevel int = 0
DefaultTransactTimeout time.Duration = 30 * time.Second
)
var (
TransNodeIDNil = TransNodeID(0)
transStats = new(sync.Map)
)
type TransExeResult int
type TransOwnerType int
type TransCmd int
type TransNodeID int64
type TransNodeParam struct {
TId TransNodeID
Tt TransType
Ot TransOwnerType
Tct TransactCommitPolicy
Oid int
SkeletonID int
LevelNo int
AreaID int
TimeOut time.Duration
ExpiresTs int64
}
type TransResult struct {
RetCode int
RetFiels interface{}
}
const (
TransStatsOp_Exe = iota
TransStatsOp_Rollback
TransStatsOp_Commit
TransStatsOp_Yiled
TransStatsOp_Resume
TransStatsOp_Timeout
)
type TransStats struct {
ExecuteTimes int64
RollbackTimes int64
CommitTimes int64
TimeoutTimes int64
YieldTimes int64
ResumeTimes int64
TotalRuningTime int64
MaxRuningTime int64
}
func (stats *TransStats) incStats(op int) {
switch op {
case TransStatsOp_Exe:
atomic.AddInt64(&stats.ExecuteTimes, 1)
case TransStatsOp_Rollback:
atomic.AddInt64(&stats.RollbackTimes, 1)
case TransStatsOp_Commit:
atomic.AddInt64(&stats.CommitTimes, 1)
case TransStatsOp_Yiled:
atomic.AddInt64(&stats.YieldTimes, 1)
case TransStatsOp_Resume:
atomic.AddInt64(&stats.ResumeTimes, 1)
case TransStatsOp_Timeout:
atomic.AddInt64(&stats.TimeoutTimes, 1)
}
}
type TransCallback func(*TransNode)
type TransBrotherNotify func(*TransNode, TransExeResult)
type TransNode struct {
TransEnv *TransCtx
TransRep *TransResult
MyTnp *TransNodeParam
ParentTnp *TransNodeParam
ownerObj *basic.Object
Childs map[TransNodeID]*TransNodeParam
finChild map[TransNodeID]interface{}
timeHandle timer.TimerHandle
handler TransHandler
AsynCallback TransCallback
brothers map[*TransNode]TransBrotherNotify
createTime time.Time
start bool
yield bool
resume bool
done bool
owner *transactCoordinater
ud interface{}
}
func (this *TransNode) incStats(op int) {
if s, exist := transStats.Load(this.MyTnp.Tt); exist {
if stats, ok := s.(*TransStats); ok {
stats.incStats(op)
}
} else {
stats := &TransStats{}
transStats.Store(this.MyTnp.Tt, stats)
stats.incStats(op)
}
}
func (this *TransNode) statsRuningTime() {
if s, exist := transStats.Load(this.MyTnp.Tt); exist {
if stats, ok := s.(*TransStats); ok {
runingTime := int64(time.Now().Sub(this.createTime) / time.Millisecond)
if runingTime > stats.MaxRuningTime {
stats.MaxRuningTime = runingTime
}
stats.TotalRuningTime += runingTime
}
}
}
func (this *TransNode) execute(ud interface{}) TransExeResult {
if this == nil {
return TransExeResult_NullPointer
}
if this.handler == nil {
return TransExeResult_NoSetHandler
}
this.start = true
ret := this.handler.OnExcute(this, ud)
this.incStats(TransStatsOp_Exe)
if ret == TransExeResult_Yield {
return this.Yield()
}
return this.doneExecRet(ret)
}
func (this *TransNode) doneExecRet(ter TransExeResult) TransExeResult {
if this.done {
return TransExeResult_HadDone
}
if ter == TransExeResult_Success {
if len(this.Childs) == len(this.finChild) {
if this.MyTnp.LevelNo <= TransRootNodeLevel {
return this.commit()
} else {
if Config.tcs != nil {
this.TransRep.RetCode = TransResult_Success
Config.tcs.SendTransResult(this.ParentTnp, this.MyTnp, this.TransRep)
}
if this.MyTnp.Tct == TransactCommitPolicy_SelfDecide {
return this.commit()
}
}
}
} else {
if this.MyTnp.LevelNo == TransRootNodeLevel {
return this.rollback(TransNodeIDNil)
} else {
if Config.tcs != nil {
this.TransRep.RetCode = TransResult_Failed
Config.tcs.SendTransResult(this.ParentTnp, this.MyTnp, this.TransRep)
}
return this.rollback(TransNodeIDNil)
}
}
return TransExeResult_Success
}
func (this *TransNode) commit() TransExeResult {
defer this.owner.releaseTrans(this)
if this == nil {
return TransExeResult_NullPointer
}
if !this.start {
return TransExeResult_NoStart
}
if this.handler == nil {
return TransExeResult_NoSetHandler
}
if this.done {
return TransExeResult_HadDone
}
defer this.notifyBrother(TransExeResult_Success)
this.done = true
this.handler.OnCommit(this)
this.incStats(TransStatsOp_Commit)
this.statsRuningTime()
if len(this.Childs) > 0 && Config.tcs != nil {
for _, v := range this.Childs {
if v.Tct == TransactCommitPolicy_TwoPhase {
Config.tcs.SendCmdToTransNode(v, TransCmd_Commit)
}
}
}
return TransExeResult_Success
}
func (this *TransNode) rollback(exclude TransNodeID) TransExeResult {
defer this.owner.releaseTrans(this)
if this == nil {
return TransExeResult_NullPointer
}
if !this.start {
return TransExeResult_NoStart
}
if this.handler == nil {
return TransExeResult_NoSetHandler
}
if this.done {
return TransExeResult_HadDone
}
defer this.notifyBrother(TransExeResult_Failed)
this.done = true
this.handler.OnRollBack(this)
this.incStats(TransStatsOp_Rollback)
this.statsRuningTime()
if len(this.Childs) > 0 && Config.tcs != nil {
for k, v := range this.Childs {
if k != exclude && v.Tct == TransactCommitPolicy_TwoPhase {
Config.tcs.SendCmdToTransNode(v, TransCmd_RollBack)
}
}
}
return TransExeResult_Success
}
func (this *TransNode) timeout() TransExeResult {
if this == nil {
return TransExeResult_NullPointer
}
if !this.start {
return TransExeResult_NoStart
}
if this.handler == nil {
return TransExeResult_NoSetHandler
}
if this.done {
return TransExeResult_HadDone
}
if this.MyTnp.LevelNo > TransRootNodeLevel {
if Config.tcs != nil {
this.TransRep.RetCode = TransResult_TimeOut
Config.tcs.SendTransResult(this.ParentTnp, this.MyTnp, this.TransRep)
}
}
this.incStats(TransStatsOp_Timeout)
this.rollback(TransNodeIDNil)
return TransExeResult_Success
}
func (this *TransNode) childTransRep(child TransNodeID, retCode int, ud interface{}) TransExeResult {
if this == nil {
return TransExeResult_NullPointer
}
if this.handler == nil {
return TransExeResult_NoSetHandler
}
if !this.start {
return TransExeResult_NoStart
}
if this.done {
return TransExeResult_HadDone
}
if _, exist := this.Childs[child]; !exist {
return TransExeResult_ChildNodeNotExist
}
if this.finChild == nil {
this.finChild = make(map[TransNodeID]interface{})
}
if _, exist := this.finChild[child]; exist {
return TransExeResult_ChildNodeRepeateRet
}
this.finChild[child] = ud
ret := this.handler.OnChildTransRep(this, child, retCode, ud)
if retCode == TransResult_Success && ret == TransExeResult_Success {
// the child nodes are returned and also run their own end (note: they may be executed asynchronously)
if len(this.Childs) == len(this.finChild) && this.yield == this.resume {
if this.MyTnp.LevelNo == TransRootNodeLevel {
this.commit()
} else {
if Config.tcs != nil {
this.TransRep.RetCode = retCode
Config.tcs.SendTransResult(this.ParentTnp, this.MyTnp, this.TransRep)
}
if this.MyTnp.Tct == TransactCommitPolicy_SelfDecide {
this.commit()
}
}
}
} else {
// They are not the root, then the parent would like to report fails
if this.MyTnp.LevelNo > TransRootNodeLevel {
if Config.tcs != nil {
this.TransRep.RetCode = retCode
Config.tcs.SendTransResult(this.ParentTnp, this.MyTnp, this.TransRep)
}
}
var exclude TransNodeID
if retCode != TransResult_Success {
exclude = child
}
// Sub-transaction fails or times out or the results were not satisfactory, timing optimization, advance RollBack
this.rollback(exclude)
}
return TransExeResult_Success
}
func (this *TransNode) StartChildTrans(tnp *TransNodeParam, ud interface{}, timeout time.Duration) TransExeResult {
if this.done {
return TransExeResult_HadDone
}
tnp.TId = this.owner.spawnTransNodeID()
tnp.TimeOut = timeout
tnp.ExpiresTs = time.Now().Add(timeout).UnixNano()
tnp.LevelNo = this.MyTnp.LevelNo + 1
if this.Childs == nil {
this.Childs = make(map[TransNodeID]*TransNodeParam)
}
this.Childs[tnp.TId] = tnp
if Config.tcs != nil {
Config.tcs.SendTransStart(this.MyTnp, tnp, ud)
}
return TransExeResult_Success
}
func (this *TransNode) GetChildTransParam(childid TransNodeID) *TransNodeParam {
if v, exist := this.Childs[childid]; exist {
return v
}
return nil
}
func (this *TransNode) Yield() TransExeResult {
this.yield = true
SendTranscatYield(this)
this.incStats(TransStatsOp_Yiled)
return TransExeResult_Success
}
func (this *TransNode) Resume() TransExeResult {
this.resume = true
SendTranscatResume(this)
this.incStats(TransStatsOp_Resume)
return TransExeResult_Success
}
func (this *TransNode) Go(obj *basic.Object) TransExeResult {
this.ownerObj = obj
return this.execute(this.ud)
}
func (this *TransNode) checkExeOver() {
if this.resume == this.yield {
if this.AsynCallback != nil {
this.AsynCallback(this)
}
if this.done == false {
var ter TransExeResult
if this.TransRep.RetCode == TransResult_Success {
ter = TransExeResult_Success
} else {
ter = TransExeResult_AsynFailed
}
this.doneExecRet(ter)
}
}
}
func (this *TransNode) MakeBrotherWith(brother *TransNode, tbn TransBrotherNotify) {
if this.brothers == nil {
this.brothers = make(map[*TransNode]TransBrotherNotify)
}
this.brothers[brother] = tbn
}
func (this *TransNode) notifyBrother(ter TransExeResult) {
for k, v := range this.brothers {
v(k, ter)
}
}
func Stats() map[int]TransStats {
stats := make(map[int]TransStats)
transStats.Range(func(k, v interface{}) bool {
if s, ok := v.(*TransStats); ok {
d := *s
stats[int(k.(TransType))] = d
}
return true
})
return stats
}