// 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 }