2023-09-22 04:07:41 +03:00
# nim-raft
# Copyright (c) 2023 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.
import std / tables
import std / rlocks
2023-10-28 20:13:51 +03:00
import types
2023-09-22 04:07:41 +03:00
type
2023-10-28 21:04:34 +03:00
EventType = enum
# Node events
NodeStart , NodeStop , NodeTick , NodeStepDown , NodeStepDownToCandidate , NodeStepDownToFollower , NodeStepDownToLeader ,
NodeStepDownToShutdown , VotingTimeout , ElectionTimeout , HeartbeatTimeout , HeartbeatReceived , HeartbeatSent , AppendEntriesReceived ,
AppendEntriesSent , RequestVoteReceived , RequestVoteSent , RequestVoteGranted , RequestVoteDenied , ClientRequestReceived ,
ClientRequestSent , ClientRequestProcessed , ClientRequestFailed , ClientRequestTimeout , ClientRequestRetry , ClientRequestRetryExhausted ,
AddNewNode , RemoveNode , NodeShutdown , NodeShutdownComplete , NodeShutdownFailed , NodeShutdownTimeout , NodeShutdownRetry
2023-10-28 20:13:51 +03:00
# Define callback to use with Terminals. Node states are updated/read in-place in the node object
2023-09-22 04:07:41 +03:00
ConsensusFSMCallbackType * [ NodeType ] = proc ( node : NodeType ) {. gcsafe . }
# Define Non-Terminals as a (unique) tuples of the internal state and a sequence of callbacks
2023-10-28 20:13:51 +03:00
NonTerminalSymbol * [ NodeType ] = ( NodeType , seq [ ConsensusFSMCallbackType [ NodeType ] ] )
# Define loose conditions computed from our NodeType (Truth Table)
2023-10-28 21:04:34 +03:00
LogicalFunctionConditionValueType * = bool
2023-10-28 20:13:51 +03:00
LogicalFunctionCondition * [ EventType , NodeTytpe , RaftMessageBase ] = proc ( e : EventType , n : NodeTytpe , msg : Option [ RaftMessageBase ] ) : bool
2023-10-28 21:04:34 +03:00
LogicalFunctionConditionsLUT * [ EventType , NodeType , RaftMessageBase ] = Table [ ( EventType , NodeType ) , LogicalFunctionCondition [ EventType , NodeType , Option [ RaftMessageBase ] ] ]
2023-10-28 20:13:51 +03:00
# Define Terminals as a tuple of a Event and a sequence of logical functions (conditions) and their respective values computed from NodeType, NodeTytpe and RaftMessageBase
# (kind of Truth Table)
2023-10-28 21:04:34 +03:00
TerminalSymbol * [ EventType , NodeType , RaftMessageBase ] = ( EventType , seq [ LogicalFunctionConditionValueType ] )
2023-09-22 04:07:41 +03:00
# Define State Transition Rules LUT of the form ( NonTerminal -> Terminal ) -> NonTerminal )
2023-10-28 20:13:51 +03:00
StateTransitionsRulesLUT * [ NodeType , EventType , RaftMessageBase ] = Table [
( NonTerminalSymbol [ NodeType ] , TerminalSymbol [ NodeType , EventType , RaftMessageBase ] ) ,
NonTerminalSymbol [ NodeType ] ]
2023-09-22 04:07:41 +03:00
# FSM type definition
2023-10-28 20:13:51 +03:00
ConsensusFSM * [ NodeType , EventType , BaseRaftMessage ] = ref object
2023-09-22 04:07:41 +03:00
mtx : RLock
2023-10-28 20:13:51 +03:00
state : NonTerminalSymbol [ NodeType ]
stateTransitionsLUT : StateTransitionsRulesLUT [ NodeType , EventType , RaftMessageBase ]
2023-10-28 21:04:34 +03:00
logicalFunctionsLut : LogicalFunctionConditionsLUT [ EventType , NodeType , RaftMessageBase ]
2023-09-22 04:07:41 +03:00
# FSM type constructor
2023-10-28 20:13:51 +03:00
proc new * [ NodeType , EventType , NodeStates ] ( T : type ConsensusFSM [ NodeType , EventType , RaftMessageBase ] ,
lut : StateTransitionsRulesLUT [ NodeType , EventType , RaftMessageBase ] ,
startSymbol : NonTerminalSymbol [ NodeType ]
2023-09-22 04:07:41 +03:00
) : T =
result = new ( ConsensusFSM [ NodeType , EventType , NodeStates ] )
initRLock ( result . mtx )
result . state = startSymbol
result . stateTransitionsLUT = lut
2023-10-28 21:04:34 +03:00
proc computeFSMLogicFunctionsPermutationValue [ NonTerminalSymbol , NodeType , EventType , RaftMessageBase ] (
fsm : ConsensusFSM [ NodeType , EventType , RaftMessageBase ] ,
nts : NonTerminalSymbol , rawInput : TerminalSymbol , msg : Option [ RaftMessageBase ] ) : TerminalSymbol =
let
e = rawInput [ 0 ]
2023-09-22 04:07:41 +03:00
var
2023-10-28 21:04:34 +03:00
logicFunctionsConds = fsm . logicalFunctionsLut [ ( e , NodeType ) ]
2023-10-28 20:13:37 +03:00
2023-10-28 20:13:51 +03:00
for f in nts [ 2 ] :
f = f ( nts [ 1 ] , e , msg )
2023-10-28 20:13:37 +03:00
2023-10-28 20:13:51 +03:00
rawInput [ 1 ] = logicFunctionsConds
2023-09-22 04:07:41 +03:00
result = rawInput
2023-10-28 20:13:51 +03:00
proc consensusFSMAdvance [ NodeType , EventType ] ( fsm : ConsensusFSM [ NodeType , EventType , RaftMessageBase ] , node : NodeType , event : EventType ,
rawInput : TerminalSymbol [ EventType , NodeType , RaftMessageBase ] ) : NonTerminalSymbol [ NodeType ] =
2023-09-22 04:07:41 +03:00
withRLock ( ) :
var
2023-10-28 21:04:34 +03:00
input = computeFSMLogicFunctionsPermutationValue ( fsm , node , event , rawInput )
2023-10-28 20:13:37 +03:00
fsm . state = fsm . stateTransitionsLUT [ ( fsm . state , input ) ]
2023-10-23 01:29:38 +00:00
result = fsm . state