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-11-05 05:27:11 +02:00
import chronicles
2023-09-22 04:07:41 +03:00
type
2023-10-28 21:16:21 +03:00
# Node events
2023-10-28 21:04:34 +03:00
EventType = enum
2023-11-05 05:27:11 +02:00
VotingTimeout ,
ElectionTimeout ,
HeartbeatTimeout ,
HeartbeatReceived ,
HeartbeatSent ,
AppendEntriesReceived ,
AppendEntriesSent ,
RequestVoteReceived ,
RequestVoteSent ,
ClientRequestReceived ,
ClientRequestProcessed
2023-10-28 21:16:21 +03:00
2023-11-05 05:27:11 +02:00
# Define callback to use with Terminals. Node states are updated/read in-place in the node object
2023-11-05 09:18:12 +02:00
ConsensusFsmTransActionType * [ NodeType ] = proc ( node : NodeType ) {. gcsafe . }
2023-10-28 21:16:21 +03:00
2023-10-28 21:07:41 +03:00
# Define logical functions (conditions) computed from our NodeType etc. (Truth Table)
2023-11-05 09:18:12 +02:00
LogicalConditionValueType * = bool
LogicalCondition * [ NodeTytpe , RaftMessageBase ] =
proc ( n : NodeTytpe , msg : Option [ RaftMessageBase ] ) : LogicalConditionValueType
LogicalConditionsLut * [ RaftNodeState , EventType , NodeType , RaftMessageBase ] =
Table [ ( RaftNodeState , EventType ) , seq [ LogicalCondition [ NodeType , Option [ RaftMessageBase ] ] ] ]
2023-10-28 21:16:21 +03:00
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-11-05 05:27:11 +02:00
TerminalSymbol * [ EventType , NodeType , RaftMessageBase ] =
2023-11-05 09:18:12 +02:00
( EventType , seq [ LogicalConditionValueType ] )
2023-10-28 21:16:21 +03:00
2023-09-22 04:07:41 +03:00
# Define State Transition Rules LUT of the form ( NonTerminal -> Terminal ) -> NonTerminal )
2023-11-05 09:18:12 +02:00
# NonTerminal is a NodeState and Terminal is a TerminalSymbol - the tuple (EventType, seq[LogicalConditionValueType])
StateTransitionsRulesLut * [ RaftNodeState , EventType , NodeType , RaftMessageBase ] = Table [
2023-11-05 05:27:11 +02:00
( RaftNodeState , TerminalSymbol [ NodeType , EventType , Option [ RaftMessageBase ] ] ) ,
2023-11-05 09:18:12 +02:00
( RaftNodeState , Option [ ConsensusFsmTransActionType ] )
2023-11-05 05:27:11 +02:00
]
2023-09-22 04:07:41 +03:00
# FSM type definition
2023-11-05 09:18:12 +02:00
ConsensusFsm * [ RaftNodeState , EventType , NodeType , RaftMessageBase ] = ref object
2023-09-22 04:07:41 +03:00
mtx : RLock
2023-11-05 05:27:11 +02:00
state : RaftNodeState
2023-11-05 09:18:12 +02:00
logicalFunctionsLut : LogicalConditionsLut [ RaftNodeState , EventType , NodeType , RaftMessageBase ]
2023-11-05 09:23:02 +02:00
stateTransitionsLut : StateTransitionsRulesLut [ RaftNodeState , EventType , NodeType , RaftMessageBase ]
2023-09-22 04:07:41 +03:00
# FSM type constructor
2023-11-05 05:27:11 +02:00
proc new * [ RaftNodeState , EventType , NodeType , RaftNodeStates ] (
2023-11-05 09:18:12 +02:00
T : type ConsensusFsm [ RaftNodeState , EventType , NodeType , RaftMessageBase ] , startSymbol : RaftNodeState ) : T =
2023-11-05 05:27:11 +02:00
2023-11-05 09:18:12 +02:00
result = new ( ConsensusFsm [ NodeType , EventType , RaftNodeStates ] )
2023-09-22 04:07:41 +03:00
initRLock ( result . mtx )
result . state = startSymbol
2023-11-05 05:27:11 +02:00
debug " new: " , fsm = repr ( result )
2023-11-05 07:23:07 +02:00
proc addFsmTransition * [ RaftNodeState , EventType , NodeType , RaftMessageBase ] (
2023-11-05 09:18:12 +02:00
fsm : ConsensusFsm [ RaftNodeState , EventType , NodeType , RaftMessageBase ] ,
2023-11-05 05:27:11 +02:00
fromState : RaftNodeState ,
2023-11-05 06:42:19 +02:00
termSymb : TerminalSymbol [ EventType , NodeType , RaftMessageBase ] ,
toState : RaftNodeState ,
2023-11-05 09:18:12 +02:00
action : Option [ ConsensusFsmTransActionType ] ) =
2023-11-05 05:27:11 +02:00
2023-11-05 09:23:02 +02:00
fsm . stateTransitionsLut [ ( fromState . state , termSymb ) ] = ( toState , action )
2023-11-05 05:27:11 +02:00
2023-11-05 09:18:12 +02:00
proc addFsmTransitionLogicalConditions * [ RaftNodeState , EventType , NodeType , RaftMessageBase ] (
fsm : ConsensusFsm [ RaftNodeState , EventType , NodeType , RaftMessageBase ] ,
state : RaftNodeState ,
event : EventType ,
logicalConditions : seq [ LogicalCondition [ NodeType , Option [ RaftMessageBase ] ] ] ) =
fsm . logicalFunctionsLut [ ( state , event ) ] = logicalConditions
2023-11-05 07:23:07 +02:00
proc computeFsmLogicFunctionsPermutationValuе * [ RaftNodeState , NodeType , EventType , RaftMessageBase ] (
2023-11-05 09:18:12 +02:00
fsm : ConsensusFsm [ RaftNodeState , EventType , NodeType , RaftMessageBase ] ,
2023-11-05 07:23:07 +02:00
node : NodeType ,
2023-11-05 05:27:11 +02:00
termSymb : TerminalSymbol ,
msg : Option [ RaftMessageBase ] ) : TerminalSymbol =
2023-09-22 04:07:41 +03:00
2023-10-28 21:04:34 +03:00
let
2023-11-05 05:27:11 +02:00
e = termSymb [ 0 ]
debug " computeFSMLogicFunctionsPermutationValue: " , eventType = e , " " , nonTermSymb = nts , " " , msg = msg
2023-09-22 04:07:41 +03:00
var
2023-10-28 21:04:34 +03:00
logicFunctionsConds = fsm . logicalFunctionsLut [ ( e , NodeType ) ]
2023-11-05 09:18:12 +02:00
logicFunctionsCondsValues = seq [ LogicalConditionValueType ]
2023-11-05 05:27:11 +02:00
debug " computeFSMLogicFunctionsPermutationValue: " , logicFunctionsConds = logicFunctionsConds
2023-10-28 20:13:37 +03:00
2023-10-30 01:49:29 +02:00
for f in logicFunctionsConds :
2023-11-05 07:23:07 +02:00
logicFunctionsCondsValues . add f ( node , msg )
2023-11-05 05:27:11 +02:00
debug " computeFSMLogicFunctionsPermutationValue: " , logicFunctionsCondsValues = logicFunctionsCondsValues
termSymb [ 1 ] = logicFunctionsConds
result = termSymb
2023-10-28 20:13:37 +03:00
2023-11-05 07:23:07 +02:00
proc fsmAdvance * [ RaftNodeState , EventType , NodeType , RaftMessageBase ] (
2023-11-05 09:18:12 +02:00
fsm : ConsensusFsm [ RaftNodeState , EventType , NodeType , RaftMessageBase ] ,
2023-11-05 05:27:11 +02:00
node : NodeType ,
2023-11-05 07:23:07 +02:00
termSymb : TerminalSymbol [ EventType , NodeType , RaftMessageBase ] ,
2023-11-05 05:27:11 +02:00
msg : Option [ RaftMessageBase ] ) : RaftNodeState =
2023-09-22 04:07:41 +03:00
withRLock ( ) :
var
2023-11-05 07:23:07 +02:00
input = computeFsmLogicFunctionsPermutationValue ( fsm , node , termSymb , msg )
2023-11-05 09:23:02 +02:00
let trans = fsm . stateTransitionsLut [ ( fsm . state , input ) ]
2023-11-05 05:27:11 +02:00
let action = trans [ 1 ]
2023-10-28 20:13:37 +03:00
2023-11-05 05:27:11 +02:00
fsm . state = trans [ 0 ]
2023-11-05 09:18:12 +02:00
debug " ConsensusFsmAdvance " , fsmState = fsm . state , " " , input = input , " " , action = repr ( action )
2023-11-05 05:27:11 +02:00
if action . isSome :
action ( node )
2023-10-23 01:29:38 +00:00
result = fsm . state