mirror of
https://github.com/status-im/nim-raft.git
synced 2025-01-27 05:14:45 +00:00
Add basic Raft Cluster test
This commit is contained in:
parent
79e96d40a5
commit
634094f766
3
.gitignore
vendored
3
.gitignore
vendored
@ -5,4 +5,5 @@
|
||||
*.exe
|
||||
*.out
|
||||
nimcache/
|
||||
build/
|
||||
build/
|
||||
nimbledeps/
|
@ -1,9 +1,11 @@
|
||||
# Nimbus
|
||||
# Copyright (c) 2018-2019 Status Research & Development GmbH
|
||||
# nim-raft
|
||||
# Copyright (c) 2023 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
# * 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/times
|
||||
import vm_compile_info
|
@ -1,12 +1,11 @@
|
||||
# Nimbus
|
||||
# Copyright (c) 2018 Status Research & Development GmbH
|
||||
# nim-raft
|
||||
# Copyright (c) 2023 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
# http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||
# http://opensource.org/licenses/MIT)
|
||||
# at your option. This file may not be copied, modified, or distributed except
|
||||
# according to those terms.
|
||||
# * 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.
|
||||
|
||||
func vmName(): string =
|
||||
when defined(evmc_enabled):
|
@ -23,12 +23,6 @@ type
|
||||
rmreSuccess = 0,
|
||||
rmreFail = 1
|
||||
|
||||
RaftMessageResponseBase* = ref object of RootObj
|
||||
msgId*: RaftMessageId # Original Message ID
|
||||
senderId*: RaftNodeId # Sender Raft Node ID
|
||||
respondentId: RaftNodeId # Responding RaftNodeId
|
||||
senderTerm*: RaftNodeTerm # Sender Raft Node Term
|
||||
|
||||
RaftMessageRequestVote* = ref object of RaftMessageBase
|
||||
lastLogTerm*: RaftNodeTerm
|
||||
lastLogIndex*: RaftLogIndex
|
||||
@ -59,6 +53,7 @@ type
|
||||
|
||||
RaftNodeClientRequest*[SmCommandType] = ref object
|
||||
op*: RaftNodeClientRequestOps
|
||||
nodeId*: RaftNodeId
|
||||
payload*: Option[SmCommandType] # Optional RaftMessagePayload carrying a Log Entry
|
||||
|
||||
RaftNodeClientResponse*[SmStateType] = ref object
|
||||
|
@ -17,16 +17,23 @@ proc RaftNodeSmInit[SmCommandType, SmStateType](stateMachine: var RaftNodeStateM
|
||||
|
||||
# Raft Node Public API procedures / functions
|
||||
proc RaftNodeCreateNew*[SmCommandType, SmStateType]( # Create New Raft Node
|
||||
id: RaftNodeId, peers: RaftNodePeers,
|
||||
persistentStorage: RaftNodePersistentStorage,
|
||||
id: RaftNodeId, peersIds: seq[RaftNodeId],
|
||||
# persistentStorage: RaftNodePersistentStorage,
|
||||
msgSendCallback: RaftMessageSendCallback): RaftNode[SmCommandType, SmStateType] =
|
||||
var
|
||||
sm = RaftNodeStateMachine[SmCommandType, SmStateType]
|
||||
sm: RaftNodeStateMachine[SmCommandType, SmStateType]
|
||||
peers: RaftNodePeers
|
||||
|
||||
RaftNodeSmInit[SmCommandType, SmStateType](sm)
|
||||
|
||||
for peerId in peersIds:
|
||||
peers.add(RaftNodePeer(id: peerId, nextIndex: 0, matchIndex: 0, hasVoted: false, canVote: true))
|
||||
|
||||
result = RaftNode[SmCommandType, SmStateType](
|
||||
id: id, state: rnsFollower, currentTerm: 0, votedFor: nil, peers: peers, commitIndex: 0, lastApplied: 0,
|
||||
id: id, state: rnsFollower, currentTerm: 0, peers: peers, commitIndex: 0, lastApplied: 0,
|
||||
stateMachine: sm, msgSendCallback: msgSendCallback
|
||||
)
|
||||
initLock(result.raftStateMutex)
|
||||
|
||||
proc RaftNodeLoad*[SmCommandType, SmStateType](
|
||||
persistentStorage: RaftNodePersistentStorage, # Load Raft Node From Storage
|
||||
|
@ -14,7 +14,7 @@ import options
|
||||
import stew/results
|
||||
import uuids
|
||||
|
||||
export results, options
|
||||
export results, options, locks, uuids
|
||||
|
||||
|
||||
type
|
||||
@ -24,7 +24,6 @@ type
|
||||
rnsCandidate = 2
|
||||
rnsLeader = 3
|
||||
|
||||
UUID = object
|
||||
RaftNodeId* = UUID # uuid4 uniquely identifying every Raft Node
|
||||
RaftNodeTerm* = uint64 # Raft Node Term Type
|
||||
RaftLogIndex* = uint64 # Raft Node Log Index Type
|
||||
@ -71,10 +70,17 @@ type
|
||||
RaftMessageBase* = ref object of RootObj # Base Type for Raft Protocol Messages
|
||||
msgId*: RaftMessageId # Message UUID
|
||||
senderId*: RaftNodeId # Sender Raft Node ID
|
||||
receiverId*: RaftNodeId # Receiver Raft Node ID
|
||||
senderTerm*: RaftNodeTerm # Sender Raft Node Term
|
||||
|
||||
RaftMessageSendCallback* = proc (recipient: RaftNodeId, raftMessage: RaftMessageBase) {.nimcall, gcsafe.} # Callback for Sending Raft Node Messages
|
||||
# out of this Raft Node.
|
||||
RaftMessageResponseBase* = ref object of RootObj
|
||||
msgId*: RaftMessageId # Original Message ID
|
||||
senderId*: RaftNodeId # Sender Raft Node ID
|
||||
respondentId: RaftNodeId # Responding RaftNodeId
|
||||
senderTerm*: RaftNodeTerm # Sender Raft Node Term
|
||||
|
||||
RaftMessageSendCallback* = proc (raftMessage: RaftMessageBase): RaftMessageResponseBase {.gcsafe.} # Callback for Sending Raft Node Messages
|
||||
# out of this Raft Node.
|
||||
|
||||
# For later use when adding/removing new nodes (dynamic configuration chganges)
|
||||
RaftNodeConfiguration* = object
|
||||
@ -120,7 +126,7 @@ type
|
||||
appendEntriesTimer: RaftTimer
|
||||
|
||||
# Mtx definition(s) go here
|
||||
raftStateMutex: Lock
|
||||
raftStateMutex*: Lock
|
||||
|
||||
# Modules (Algos)
|
||||
consensusModule: RaftConsensusModule[SmCommandType, SmStateType]
|
||||
@ -128,22 +134,22 @@ type
|
||||
membershipChangeModule: RaftMembershipChangeModule[SmCommandType, SmStateType]
|
||||
|
||||
# Misc
|
||||
msgSendCallback: RaftMessageSendCallback
|
||||
msgSendCallback*: RaftMessageSendCallback
|
||||
persistentStorage: RaftNodePersistentStorage[SmCommandType, SmStateType]
|
||||
|
||||
# Persistent state
|
||||
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)
|
||||
votedFor: RaftNodeId # Candidate RaftNodeId that received vote in current term (or nil/zero if none),
|
||||
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)
|
||||
votedFor*: RaftNodeId # Candidate RaftNodeId that received vote in current term (or nil/zero if none),
|
||||
# also used to redirect Client Requests in case this Raft Node is not the leader
|
||||
log: RaftNodeLog[SmCommandType] # This Raft Node Log
|
||||
stateMachine: RaftNodeStateMachine[SmCommandType, SmStateType] # Not sure for now putting it here. I assume that persisting the State Machine's
|
||||
stateMachine*: RaftNodeStateMachine[SmCommandType, SmStateType] # Not sure for now putting it here. I assume that persisting the State Machine's
|
||||
# state is enough to consider it 'persisted'
|
||||
peers: RaftNodePeers # This Raft Node Peers IDs. I am not sure if this must be persistent or volatile but making it persistent
|
||||
peers*: RaftNodePeers # This Raft Node Peers IDs. I am not sure if this must be persistent or volatile but making it persistent
|
||||
# makes sense for the moment
|
||||
|
||||
# Volatile state
|
||||
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)
|
||||
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)
|
||||
currentLeaderId: RaftNodeId # The ID of the cirrent leader Raft Node or 0/nil if None is leader (election is in progress etc.)
|
@ -7,10 +7,11 @@
|
||||
# This file may not be copied, modified, or distributed except according to
|
||||
# those terms.
|
||||
|
||||
import test_macro
|
||||
import ../misc/test_macro
|
||||
|
||||
{. warning[UnusedImport]: off .}
|
||||
{. warning[UnusedImport]:off .}
|
||||
|
||||
cliBuilder:
|
||||
import ./test_basic_state_machine,
|
||||
import ./test_basic_cluster,
|
||||
./test_basic_state_machine,
|
||||
./test_basic_timers
|
43
tests/basic_cluster.nim
Normal file
43
tests/basic_cluster.nim
Normal file
@ -0,0 +1,43 @@
|
||||
# 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 basic_timers
|
||||
import basic_state_machine
|
||||
import ../raft/raft_api
|
||||
|
||||
type
|
||||
BasicRaftNode* = RaftNode[SmCommand, SmState]
|
||||
|
||||
BasicRaftCluster* = ref object
|
||||
nodes*: seq[BasicRaftNode]
|
||||
|
||||
proc BasicRaftClusterRaftMessageSendCallbackCreate(cluster: BasicRaftCluster): RaftMessageSendCallback =
|
||||
proc (msg: RaftMessageBase): RaftMessageResponseBase {.closure.} =
|
||||
var
|
||||
nodeIdx: int = -1
|
||||
|
||||
for i in 0..cluster.nodes.len:
|
||||
if cluster.nodes[i].id == msg.receiverId:
|
||||
nodeIdx = i
|
||||
break
|
||||
|
||||
cluster.nodes[nodeIdx].RaftNodeMessageDeliver(msg)
|
||||
|
||||
proc BasicRaftClusterClientRequest*(cluster: BasicRaftCluster, req: RaftNodeClientRequest): RaftNodeClientResponse =
|
||||
discard
|
||||
|
||||
proc BasicRaftClusterInit*(nodesIds: seq[RaftNodeId]): BasicRaftCluster =
|
||||
new(result)
|
||||
for nodeId in nodesIds:
|
||||
var
|
||||
peersIds = nodesIds
|
||||
|
||||
peersIds.del(peersIds.find(nodeId))
|
||||
result.nodes.add(RaftNodeCreateNew[SmCommand, SmState](nodeId, peersIds, BasicRaftClusterRaftMessageSendCallbackCreate(result)))
|
||||
|
28
tests/test_basic_cluster.nim
Normal file
28
tests/test_basic_cluster.nim
Normal file
@ -0,0 +1,28 @@
|
||||
# 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 unittest2
|
||||
import basic_cluster
|
||||
import ../raft/types
|
||||
|
||||
proc basicClusterMain*() =
|
||||
var
|
||||
cluster: BasicRaftCluster
|
||||
nodesIds = newSeq[RaftNodeId](5)
|
||||
|
||||
suite "Basic Raft Cluster Tests":
|
||||
|
||||
test "Test Basic Raft Cluster Init (5 nodes)":
|
||||
for i in 0..4:
|
||||
nodesIds[i] = genUUID()
|
||||
|
||||
cluster = BasicRaftClusterInit(nodesIds)
|
||||
|
||||
if isMainModule:
|
||||
basicClusterMain()
|
Loading…
x
Reference in New Issue
Block a user