nim-raft/raft/consensus_state_machine.nim

79 lines
4.0 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-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