447 lines
10 KiB
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
|
|
}
|