Real-World Use Case: Two-Phase Commit Protocol
One of the best showcases for FSMs is the Two-Phase Commit protocol (TPC), widely used in distributed systems to ensure atomic transactions. Let’s explore how GFSM can model this protocol step by step.1. Define Your State Machine
To build a TPC protocol FSM, we start with a basic state machine:graph TD; Init-->Wait; Wait-->Abort; Wait-->Commit;
–
–
–
2. Enumerate All Possible States
GFSM allows any
type State int
const (
Init State = iota
Wait
Abort
Commit
)
const (
Init State = iota
Wait
Abort
Commit
)
3. Define State Behaviors
Each state in GFSM is represented by an action handler implementing the
type StateAction[StateIdentifier comparable] interface {
OnEnter(smCtx StateMachineContext)
OnExit(smCtx StateMachineContext)
Execute(smCtx StateMachineContext, eventCtx EventContext) StateIdentifier
}
OnEnter(smCtx StateMachineContext)
OnExit(smCtx StateMachineContext)
Execute(smCtx StateMachineContext, eventCtx EventContext) StateIdentifier
}
–
–
–
For example, the
func (s *initState) Execute(smCtx gfsm.StateMachineContext, eventCtx gfsm.EventContext) State {
cCtx := smCtx.(*coordinatorContext)
req, ok := eventCtx.(commitRequest)
if !ok {
// Handle invalid event
return Init
}
// Transition logic
return Wait
}
cCtx := smCtx.(*coordinatorContext)
req, ok := eventCtx.(commitRequest)
if !ok {
// Handle invalid event
return Init
}
// Transition logic
return Wait
}
4. Build the State Machine
Using the
sm := gfsm.NewBuilder[State]().
SetDefaultState(Init).
SetSmContext(&coordinatorContext{partCnt: 3}).
RegisterState(Init, &initState{}, []State{Wait}).
RegisterState(Wait, &waitState{}, []State{Abort, Commit}).
RegisterState(Abort, &responseState{
keepResp: Abort,
}, []State{Init}).
RegisterState(Commit, &responseState{
keepResp: Commit,
}, []State{Init}).
Build()
SetDefaultState(Init).
SetSmContext(&coordinatorContext{partCnt: 3}).
RegisterState(Init, &initState{}, []State{Wait}).
RegisterState(Wait, &waitState{}, []State{Abort, Commit}).
RegisterState(Abort, &responseState{
keepResp: Abort,
}, []State{Init}).
RegisterState(Commit, &responseState{
keepResp: Commit,
}, []State{Init}).
Build()
Here,
5. Run the State Machine
Once constructed, the FSM is ready to process events. Start the state machine, handle events, and gracefully terminate it:
sm.Start()
defer sm.Stop()
err := sm.ProcessEvent(commitRequest{"commit_1"})
if err != nil {
// Handle error
}
defer sm.Stop()
err := sm.ProcessEvent(commitRequest{"commit_1"})
if err != nil {
// Handle error
}