start writing consensus fsm tests
This commit is contained in:
parent
0b25b118bd
commit
ddb58f7063
|
@ -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
|
||||||
|
|
|
@ -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 computeFSMLogicFunctionsPermutationValue[NonTerminalSymbol, RaftNodeState, NodeType, EventType, RaftMessageBase](
|
proc addNewFsmTransition*[RaftNodeState, EventType, NodeType, RaftMessageBase](
|
||||||
fsm: ConsensusFSM[RaftNodeState, EventType, NodeType, RaftMessageBase],
|
fsm: ConsensusFSM[RaftNodeState, EventType, NodeType, RaftMessageBase],
|
||||||
nts: NonTerminalSymbol, rawInput: TerminalSymbol, msg: Option[RaftMessageBase]): TerminalSymbol =
|
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 =
|
||||||
|
|
||||||
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
|
||||||
|
|
Loading…
Reference in New Issue