nim-raft/raft/consensus_state_machine.nim

127 lines
4.9 KiB
Nim
Raw Normal View History

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
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)
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] =
(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 )
# 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]]),
(RaftNodeState, Option[ConsensusFsmTransActionType])
2023-11-05 05:27:11 +02:00
]
2023-09-22 04:07:41 +03:00
# FSM type definition
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
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](
T: type ConsensusFsm[RaftNodeState, EventType, NodeType, RaftMessageBase], startSymbol: RaftNodeState): T =
2023-11-05 05:27:11 +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](
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,
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
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](
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)]
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](
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]
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