2023-08-09 10:06:34 +00: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.
2023-08-14 20:49:21 +00:00
# Raft Node Public Types
2023-08-09 10:06:34 +00:00
2023-09-06 19:27:22 +00:00
import std / rlocks
2023-08-14 20:49:21 +00:00
import options
import stew / results
2023-08-29 09:20:40 +00:00
import uuids
2023-09-03 00:53:48 +00:00
import chronos
2023-08-09 10:06:34 +00:00
2023-09-04 09:47:27 +00:00
export
results ,
options ,
2023-09-06 19:27:22 +00:00
rlocks ,
2023-09-04 09:47:27 +00:00
uuids ,
chronos
2023-08-09 10:06:34 +00:00
2023-08-31 23:56:15 +00:00
const
DefaultUUID * = initUUID ( 0 , 0 ) # 00000000-0000-0000-0000-000000000000
2023-08-14 20:49:21 +00:00
type
2023-08-09 10:06:34 +00:00
RaftNodeState * = enum
2023-08-14 20:49:21 +00:00
rnsUnknown = 0 ,
rnsFollower = 1 ,
rnsCandidate = 2
2023-09-03 00:53:48 +00:00
rnsLeader = 3 ,
rnsStopped = 4
2023-08-09 10:06:34 +00:00
2023-08-25 09:00:40 +00:00
RaftNodeId * = UUID # uuid4 uniquely identifying every Raft Node
2023-09-11 16:55:30 +00:00
RaftNodeTerm * = int # Raft Node Term Type
RaftLogIndex * = int # Raft Node Log Index Type
2023-08-09 10:06:34 +00:00
2023-09-03 17:52:35 +00:00
RaftNodePeer * = ref object # Raft Node Peer object
2023-08-14 20:49:21 +00:00
id * : RaftNodeId
nextIndex * : RaftLogIndex # For each peer Raft Node, index of the next log entry to send to that Node
# (initialized to leader last log index + 1)
matchIndex * : RaftLogIndex # For each peer Raft Node, index of highest log entry known to be replicated on Node
# (initialized to 0, increases monotonically)
hasVoted * : bool # Indicates if this peer have voted for this Raft Node During Election
canVote * : bool # Indicates if this peer can vote
2023-09-23 01:26:45 +00:00
appendEntriesTimer * : Future [ void ]
2023-08-14 20:49:21 +00:00
RaftNodePeers * = seq [ RaftNodePeer ] # List of Raft Node Peers
2023-08-09 10:06:34 +00:00
# Raft Node Abstract State Machine type
2023-08-31 12:03:07 +00:00
RaftNodeStateMachine * [ SmCommandType , SmStateType ] = ref object # Some opaque State Machine Impelementation to be used by the Raft Node
2023-08-14 20:49:21 +00:00
# providing at minimum operations for initialization, querying the current state
# and RaftNodeLogEntry (SmCommandType) application
2023-08-31 12:03:07 +00:00
state * : ref SmStateType
2023-08-09 10:06:34 +00:00
# Raft Node Persistent Storage basic definition
2023-08-14 20:49:21 +00:00
RaftNodePersistentStorage * [ SmCommandType , SmStateType ] = object # Should be some kind of Persistent Transactional Store Wrapper
2023-08-09 10:06:34 +00:00
# Basic modules (algos) definitions
2023-08-14 20:49:21 +00:00
RaftNodeAccessCallback [ SmCommandType , SmStateType ] = proc : RaftNode [ SmCommandType , SmStateType ] {. nimcall , gcsafe . } # This should be implementes as a closure holding the RaftNode
2023-08-09 10:06:34 +00:00
2023-08-14 20:49:21 +00:00
RaftConsensusModule * [ SmCommandType , SmStateType ] = object of RootObj
stateTransitionsFsm : seq [ byte ] # I plan to use nim.fsm https://github.com/ba0f3/fsm.nim
2023-08-22 01:04:47 +00:00
gatheredVotesCount : int
2023-08-14 20:49:21 +00:00
raftNodeAccessCallback : RaftNodeAccessCallback [ SmCommandType , SmStateType ]
2023-08-09 10:06:34 +00:00
2023-08-14 20:49:21 +00:00
RaftLogCompactionModule * [ SmCommandType , SmStateType ] = object of RootObj
raftNodeAccessCallback : RaftNodeAccessCallback [ SmCommandType , SmStateType ]
2023-08-09 10:06:34 +00:00
2023-08-14 20:49:21 +00:00
RaftMembershipChangeModule * [ SmCommandType , SmStateType ] = object of RootObj
raftNodeAccessCallback : RaftNodeAccessCallback [ SmCommandType , SmStateType ]
2023-08-09 10:06:34 +00:00
# Callback for sending messages out of this Raft Node
2023-08-25 09:00:40 +00:00
RaftMessageId * = UUID # UUID assigned to every Raft Node Message,
2023-08-14 20:49:21 +00:00
# so it can be matched with it's corresponding response etc.
2023-08-09 10:06:34 +00:00
2023-09-04 09:47:27 +00:00
# Raft Node Messages OPs
RaftMessageOps * = enum
rmoRequestVote = 0 , # Request Raft Node vote during election.
rmoAppendLogEntry = 1 , # Append log entry (when replicating) or represent a Heart-Beat
# if log entries are missing.
rmoInstallSnapshot = 2 # For dynamic adding of new Raft Nodes to speed up the new nodes
# when they have to catch-up to the currently replicated log.
2023-09-17 00:47:29 +00:00
RaftMessageBase * [ SmCommandType , SmStateType ] = ref object of RootObj # Base Type for Raft Protocol Messages.
2023-09-04 09:47:27 +00:00
msgId * : RaftMessageId # Message UUID.
senderId * : RaftNodeId # Sender Raft Node ID.
receiverId * : RaftNodeId # Receiver Raft Node ID.
2023-08-14 20:49:21 +00:00
2023-09-17 00:47:29 +00:00
RaftMessageResponseBase * [ SmCommandType , SmStateType ] = ref object of RaftMessageBase [ SmCommandType , SmStateType ]
2023-08-31 20:52:52 +00:00
2023-09-17 00:47:29 +00:00
# Callback for Sending Raft Node Messages out of this Raft Node.
RaftMessageSendCallback * [ SmCommandType , SmStateType ] = proc ( raftMessage : RaftMessageBase [ SmCommandType , SmStateType ] ) :
Future [ RaftMessageResponseBase [ SmCommandType , SmStateType ] ] {. async , gcsafe . }
2023-08-14 20:49:21 +00:00
# For later use when adding/removing new nodes (dynamic configuration chganges)
2023-09-04 09:47:27 +00:00
RaftNodeConfiguration * = ref object
peers * : RaftNodePeers
2023-08-14 20:49:21 +00:00
# Raft Node Log definition
LogEntryType * = enum
etUnknown = 0 ,
etConfiguration = 1 ,
etData = 2 ,
etNoOp = 3
2023-09-04 09:47:27 +00:00
RaftNodeLogEntry * [ SmCommandType ] = object # Abstarct Raft Node Log entry containing opaque binary data (Blob etc.)
2023-08-14 20:49:21 +00:00
term * : RaftNodeTerm
entryType * : LogEntryType # Type of entry - data to append, configuration or no op etc.
data * : Option [ SmCommandType ] # Entry data (State Machine Command) - this is mutually exclusive with configuration
# depending on entryType field
2023-09-04 09:47:27 +00:00
configuration * : Option [ RaftNodeConfiguration ] # Node configuration
2023-08-14 20:49:21 +00:00
2023-08-22 01:04:47 +00:00
RaftNodeLog * [ SmCommandType ] = object # Needs more elaborate definition.
2023-08-14 20:49:21 +00:00
# Probably this will be a RocksDB/MDBX/SQLite Store Wrapper etc.
logData * : seq [ RaftNodeLogEntry [ SmCommandType ] ] # Raft Node Log Data
2023-08-09 10:06:34 +00:00
2023-09-02 21:16:26 +00:00
RaftTimerCallback * = proc ( ) {. gcsafe . } # Pass any function wrapped in a closure
2023-08-10 07:17:46 +00:00
2023-08-09 10:06:34 +00:00
# Raft Node Object type
2023-08-31 14:05:41 +00:00
RaftNode * [ SmCommandType , SmStateType ] = ref object
2023-08-09 10:06:34 +00:00
# Timers
2023-09-17 00:47:29 +00:00
votesFuts * : seq [ Future [ RaftMessageResponseBase [ SmCommandType , SmStateType ] ] ]
2023-09-03 17:52:35 +00:00
2023-09-11 16:55:30 +00:00
electionTimeout * : int
2023-09-03 00:53:48 +00:00
heartBeatTimeout * : int
appendEntriesTimeout * : int
2023-09-17 00:47:29 +00:00
votingTimeout * : int
2023-09-03 00:53:48 +00:00
heartBeatTimer * : Future [ void ]
2023-09-07 02:04:27 +00:00
electionTimeoutTimer * : Future [ void ]
2023-08-09 10:06:34 +00:00
2023-08-14 20:49:21 +00:00
# Mtx definition(s) go here
2023-09-06 19:27:22 +00:00
raftStateMutex * : RLock
2023-08-09 10:06:34 +00:00
# Misc
2023-09-17 00:47:29 +00:00
msgSendCallback * : RaftMessageSendCallback [ SmCommandType , SmStateType ]
2023-08-14 20:49:21 +00:00
persistentStorage : RaftNodePersistentStorage [ SmCommandType , SmStateType ]
2023-08-09 10:06:34 +00:00
# Persistent state
2023-08-31 20:52:52 +00:00
id * : RaftNodeId # This Raft Node ID
state * : RaftNodeState # This Raft Node State
currentTerm * : RaftNodeTerm # Latest term this Raft Node has seen (initialized to 0 on first boot, increases monotonically)
2023-08-31 23:56:15 +00:00
votedFor * : RaftNodeId # Candidate RaftNodeId that received vote in current term (or DefaultUUID if none),
# also used to redirect Client Requests in case this Raft Node is not the leader
2023-09-11 16:55:30 +00:00
log * : RaftNodeLog [ SmCommandType ] # This Raft Node Log
2023-08-31 20:52:52 +00:00
stateMachine * : RaftNodeStateMachine [ SmCommandType , SmStateType ] # Not sure for now putting it here. I assume that persisting the State Machine's
2023-08-14 20:49:21 +00:00
# state is enough to consider it 'persisted'
2023-08-31 20:52:52 +00:00
peers * : RaftNodePeers # This Raft Node Peers IDs. I am not sure if this must be persistent or volatile but making it persistent
2023-08-31 23:56:15 +00:00
# makes sense for the moment
2023-08-14 20:49:21 +00:00
2023-08-09 10:06:34 +00:00
# Volatile state
2023-08-31 20:52:52 +00:00
commitIndex * : RaftLogIndex # Index of highest log entry known to be committed (initialized to 0, increases monotonically)
lastApplied * : RaftLogIndex # Index of highest log entry applied to state machine (initialized to 0, increases monotonically)
2023-08-31 23:56:15 +00:00
currentLeaderId * : RaftNodeId # The ID of the current leader Raft Node or DefaultUUID if None is leader (election is in progress etc.)