Add basic state machine test etc.

This commit is contained in:
Raycho Mukelov 2023-08-31 15:03:07 +03:00
parent 0f5ce4687b
commit 7017e9fc69
4 changed files with 96 additions and 5 deletions

View File

@ -42,10 +42,10 @@ type
# Raft Node Abstract State Machine type
RaftNodeStateMachine*[SmCommandType, SmStateType] = object # Some opaque State Machine Impelementation to be used by the Raft Node
RaftNodeStateMachine*[SmCommandType, SmStateType] = ref object # Some opaque State Machine Impelementation to be used by the Raft Node
# providing at minimum operations for initialization, querying the current state
# and RaftNodeLogEntry (SmCommandType) application
state: SmStateType
state*: ref SmStateType
# Raft Node Persistent Storage basic definition
RaftNodePersistentStorage*[SmCommandType, SmStateType] = object # Should be some kind of Persistent Transactional Store Wrapper

View File

@ -0,0 +1,38 @@
# 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 ../raft/types
import std/tables
export tables
type
SmState* = Table[string, string]
SmCommands* = enum
scSet = 0,
scDel = 1
SmCommand* = object
cmd*: SmCommands
key*: string
val*: string
RaftBasicSm* = RaftNodeStateMachine[SmCommand, SmState]
proc RaftSmInit*(stateMachine: var RaftBasicSm) =
new(stateMachine)
new(stateMachine.state)
proc RaftSmApply*(stateMachine: RaftBasicSm, command: SmCommand) =
case command.cmd:
of scSet:
stateMachine.state[command.key] = command.val
of scDel:
stateMachine.state.del(command.key)

View File

@ -0,0 +1,50 @@
# 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 ../raft/types
import basic_state_machine
proc smRunner() =
var
sm: RaftBasicSm
smCommandsLog: seq[SmCommand]
suite "Test Basic State Machine Implementation":
test "Test Init":
RaftSmInit(sm)
check sm != nil and sm.state != nil and sm.state.len == 0
test "Init commands":
smCommandsLog.add(SmCommand(cmd: scSet, key: "a", val: "a"))
smCommandsLog.add(SmCommand(cmd: scSet, key: "b", val: "b"))
smCommandsLog.add(SmCommand(cmd: scSet, key: "c", val: "c"))
smCommandsLog.add(SmCommand(cmd: scSet, key: "d", val: "d"))
smCommandsLog.add(SmCommand(cmd: scSet, key: "e", val: "e"))
smCommandsLog.add(SmCommand(cmd: scSet, key: "f", val: "f"))
smCommandsLog.add(SmCommand(cmd: scDel, key: "a"))
smCommandsLog.add(SmCommand(cmd: scDel, key: "a"))
smCommandsLog.add(SmCommand(cmd: scSet, key: "a", val: "a"))
smCommandsLog.add(SmCommand(cmd: scDel, key: "a"))
smCommandsLog.add(SmCommand(cmd: scSet, key: "g", val: "g"))
smCommandsLog.add(SmCommand(cmd: scDel, key: "d"))
smCommandsLog.add(SmCommand(cmd: scSet, key: "h", val: "h"))
check smCommandsLog.len == 13
test "Apply commands and check result":
for c in smCommandsLog:
RaftSmApply(sm, c)
check sm.state[] == {"b": "b", "c": "c", "e": "e", "f": "f", "g": "g", "h": "h"}.toTable
if isMainModule:
smRunner()

View File

@ -19,6 +19,9 @@ var
runningMtx: Lock
running: bool
timersChan: seq[RaftTimer]
timersChanMtx: Lock
proc RaftTimerCreateCustomImpl*(timerInterval: int, oneshot: bool, timerCallback: RaftTimerCallback): RaftTimer {.nimcall, gcsafe.} =
var
timer = RaftTimer(mtx: Lock(), canceled: false, expired: false, timeout: timerInterval, oneshot: oneshot)
@ -49,15 +52,15 @@ proc RaftTimerCancelCustomImpl*(timer: RaftTimer): bool {.nimcall, gcsafe, disca
proc RaftTimerPollThread() {.thread, nimcall, gcsafe.} =
while running:
try:
withLock(timersChanMtx):
debugEcho timersChan.len
poll()
debugEcho activeDescriptors()
except ValueError as e:
debugEcho e.msg
# Add a 'dummy' timer if no other handles are present to prevent more
# ValueError exceptions this is a workaround for a asyncdyspatch bug
# see - https://github.com/nim-lang/Nim/issues/14564
addTimer(10000, false, proc (fd: AsyncFD): bool {.closure, gcsafe.} = false)
addTimer(1, true, proc (fd: AsyncFD): bool {.closure, gcsafe.} = true)
proc RaftTimerJoinPollThread*() {.nimcall, gcsafe.} =
joinThread(pollThr)