start writing consensus fsm tests

This commit is contained in:
Raycho Mukelov 2023-11-05 05:27:11 +02:00
parent 0b25b118bd
commit ddb58f7063
2 changed files with 69 additions and 31 deletions

View File

@ -1,3 +1,3 @@
* testbasictimers - 504 milliseconds, 57 microseconds, and 888 nanoseconds * testbasictimers - 504 milliseconds, 149 microseconds, and 657 nanoseconds
* testbasicstatemachine - 1 millisecond, 819 microseconds, and 596 nanoseconds * testbasicstatemachine - 1 millisecond, 831 microseconds, and 189 nanoseconds
* testbasicclusterelection - 2 minutes, 87 milliseconds, 171 microseconds, and 543 nanoseconds * testbasicclusterelection - 2 minutes, 80 milliseconds, 340 microseconds, and 536 nanoseconds

View File

@ -10,69 +10,107 @@
import std/tables import std/tables
import std/rlocks import std/rlocks
import types import types
import chronicles
type type
# Node events # Node events
EventType = enum EventType = enum
VotingTimeout, ElectionTimeout, HeartbeatTimeout, HeartbeatReceived, HeartbeatSent, AppendEntriesReceived, VotingTimeout,
AppendEntriesSent, RequestVoteReceived, RequestVoteSent, ClientRequestReceived, ClientRequestProcessed ElectionTimeout,
# Define callback to use with Terminals. Node states are updated/read in-place in the node object HeartbeatTimeout,
ConsensusFSMCallbackType*[NodeType] = proc(node: NodeType) {.gcsafe.} HeartbeatReceived,
HeartbeatSent,
AppendEntriesReceived,
AppendEntriesSent,
RequestVoteReceived,
RequestVoteSent,
ClientRequestReceived,
ClientRequestProcessed
# Define Non-Terminals as a (unique) tuples of the internal state and a sequence of callbacks # Define callback to use with Terminals. Node states are updated/read in-place in the node object
NonTerminalSymbol*[RaftNodeState] = RaftNodeState ConsensusFSMTransActionType*[NodeType] = proc(node: NodeType) {.gcsafe.}
# Define logical functions (conditions) computed from our NodeType etc. (Truth Table) # Define logical functions (conditions) computed from our NodeType etc. (Truth Table)
LogicalFunctionConditionValueType* = bool LogicalFunctionConditionValueType* = bool
LogicalFunctionCondition*[EventType, NodeTytpe, RaftMessageBase] = proc(e: EventType, n: NodeTytpe, msg: Option[RaftMessageBase]): bool LogicalFunctionCondition*[EventType, NodeTytpe, RaftMessageBase] =
LogicalFunctionConditionsLUT*[RaftNodeState, EventType, NodeType, RaftMessageBase] = Table[(RaftNodeState, EventType), seq[LogicalFunctionCondition[EventType, NodeType, Option[RaftMessageBase]]]] proc(e: EventType, n: NodeTytpe, msg: Option[RaftMessageBase]): bool
LogicalFunctionConditionsLUT*[RaftNodeState, EventType, NodeType, RaftMessageBase] =
Table[(RaftNodeState, EventType), seq[LogicalFunctionCondition[EventType, NodeType, Option[RaftMessageBase]]]]
# 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 # 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) # (kind of Truth Table)
TerminalSymbol*[EventType, NodeType, RaftMessageBase] = (EventType, seq[LogicalFunctionConditionValueType]) TerminalSymbol*[EventType, NodeType, RaftMessageBase] =
(EventType, seq[LogicalFunctionConditionValueType])
# Define State Transition Rules LUT of the form ( NonTerminal -> Terminal ) -> NonTerminal ) # Define State Transition Rules LUT of the form ( NonTerminal -> Terminal ) -> NonTerminal )
StateTransitionsRulesLUT*[RaftNodeState, EventType, NodeType, RaftMessageBase] = Table[ StateTransitionsRulesLUT*[RaftNodeState, EventType, NodeType, RaftMessageBase] = Table[
(NonTerminalSymbol[RaftNodeState], TerminalSymbol[NodeType, EventType, RaftMessageBase]), (RaftNodeState, TerminalSymbol[NodeType, EventType, Option[RaftMessageBase]]),
NonTerminalSymbol[NodeType]] (RaftNodeState, Option[ConsensusFSMTransActionType])
]
# FSM type definition # FSM type definition
ConsensusFSM*[RaftNodeState, EventType, NodeType, RaftMessageBase] = ref object ConsensusFSM*[RaftNodeState, EventType, NodeType, RaftMessageBase] = ref object
mtx: RLock mtx: RLock
state: NonTerminalSymbol[NodeType] state: RaftNodeState
stateTransitionsLUT: StateTransitionsRulesLUT[RaftNodeState, EventType, NodeType, RaftMessageBase] stateTransitionsLUT: StateTransitionsRulesLUT[RaftNodeState, EventType, NodeType, RaftMessageBase]
logicalFunctionsLut: LogicalFunctionConditionsLUT[RaftNodeState, EventType, NodeType, RaftMessageBase] logicalFunctionsLut: LogicalFunctionConditionsLUT[RaftNodeState, EventType, NodeType, RaftMessageBase]
# FSM type constructor # FSM type constructor
proc new*[RaftNodeState, EventType, NodeType, RaftNodeStates](T: type ConsensusFSM[RaftNodeState, EventType, NodeType, RaftMessageBase], proc new*[RaftNodeState, EventType, NodeType, RaftNodeStates](
lut: StateTransitionsRulesLUT[RaftNodeState, EventType, NodeType, RaftMessageBase], T: type ConsensusFSM[RaftNodeState, EventType, NodeType, RaftMessageBase], startSymbol: RaftNodeState): T =
startSymbol: NonTerminalSymbol[NodeType]
): T =
result = new(ConsensusFSM[NodeType, EventType, RaftNodeStates]) result = new(ConsensusFSM[NodeType, EventType, RaftNodeStates])
initRLock(result.mtx) initRLock(result.mtx)
result.state = startSymbol result.state = startSymbol
result.stateTransitionsLUT = lut debug "new: ", fsm=repr(result)
proc addNewFsmTransition*[RaftNodeState, EventType, NodeType, RaftMessageBase](
fsm: ConsensusFSM[RaftNodeState, EventType, NodeType, RaftMessageBase],
fromState: RaftNodeState,
toState: RaftNodeState) =
fsm.stateTransitionsLUT[fromState.state] = (toState, none)
proc computeFSMLogicFunctionsPermutationValue[RaftNodeState, NodeType, EventType, RaftMessageBase](
fsm: ConsensusFSM[RaftNodeState, EventType, NodeType, RaftMessageBase],
nts: RaftNodeState,
termSymb: TerminalSymbol,
msg: Option[RaftMessageBase]): TerminalSymbol =
proc computeFSMLogicFunctionsPermutationValue[NonTerminalSymbol, RaftNodeState, NodeType, EventType, RaftMessageBase](
fsm: ConsensusFSM[RaftNodeState, EventType, NodeType, RaftMessageBase],
nts: NonTerminalSymbol, rawInput: TerminalSymbol, msg: Option[RaftMessageBase]): TerminalSymbol =
let let
e = rawInput[0] e = termSymb[0]
debug "computeFSMLogicFunctionsPermutationValue: ", eventType=e, " ", nonTermSymb=nts, " ", msg=msg
var var
logicFunctionsConds = fsm.logicalFunctionsLut[(e, NodeType)] logicFunctionsConds = fsm.logicalFunctionsLut[(e, NodeType)]
logicFunctionsCondsValues = seq[LogicalFunctionConditionValueType]
debug "computeFSMLogicFunctionsPermutationValue: ", logicFunctionsConds=logicFunctionsConds
for f in logicFunctionsConds: for f in logicFunctionsConds:
f = f(nts[1], e, msg) logicFunctionsCondsValues.add f(nts, e, msg)
rawInput[1] = logicFunctionsConds debug "computeFSMLogicFunctionsPermutationValue: ", logicFunctionsCondsValues=logicFunctionsCondsValues
result = rawInput
termSymb[1] = logicFunctionsConds
result = termSymb
proc consensusFSMAdvance[RaftNodeState, EventType, NodeType, RaftMessageBase](
fsm: ConsensusFSM[RaftNodeState, EventType, NodeType, RaftMessageBase],
node: NodeType,
event: EventType,
rawInput: TerminalSymbol[EventType, NodeType, RaftMessageBase],
msg: Option[RaftMessageBase]): RaftNodeState =
proc consensusFSMAdvance[RaftNodeState, EventType, NodeType, RaftMessageBase](fsm: ConsensusFSM[RaftNodeState, EventType, NodeType, RaftMessageBase], node: NodeType, event: EventType,
rawInput: TerminalSymbol[EventType, NodeType, RaftMessageBase], msg: Option[RaftMessageBase]): NonTerminalSymbol[NodeType] =
withRLock(): withRLock():
var var
input = computeFSMLogicFunctionsPermutationValue(fsm, node, event, rawInput) input = computeFSMLogicFunctionsPermutationValue(fsm, node, event, rawInput)
let trans = fsm.stateTransitionsLUT[(fsm.state, input)]
let action = trans[1]
fsm.state = fsm.stateTransitionsLUT[(fsm.state, input)] fsm.state = trans[0]
debug "consensusFSMAdvance", fsmState=fsm.state, " ", input=input, " ", action=repr(action)
if action.isSome:
action(node)
result = fsm.state result = fsm.state