fix(raft): state machine callback, start from main, remove asyncCheck, config

- applyCommand callback: committed entries applied to state machine
- lastApplied incremented on commit via applyCommitted()
- RaftNetwork.run() started from main() when raftEnabled=true
- asyncCheck replaced with try/await in processMessage
- bindAddr without hardcoded 127.0.0.1
- raft config: raftEnabled/Port/Peers/NodeId + env vars
- 283 tests, 0 failures
This commit is contained in:
2026-05-07 14:22:04 +03:00
parent d259c1a1fc
commit c2dc280ddd
4 changed files with 64 additions and 17 deletions
+14
View File
@@ -29,6 +29,10 @@ type
jwtSecret*: string
rateLimitGlobal*: int
rateLimitPerClient*: int
raftEnabled*: bool
raftPort*: int
raftPeers*: seq[string]
raftNodeId*: string
CompactionStrategy* = enum
csSizeTiered = "size_tiered"
@@ -61,6 +65,10 @@ proc defaultConfig*(): BaraConfig =
jwtSecret: "",
rateLimitGlobal: 10_000,
rateLimitPerClient: 1_000,
raftEnabled: false,
raftPort: 9473,
raftPeers: @[],
raftNodeId: "",
)
# ----------------------------------------------------------------------
@@ -152,6 +160,12 @@ proc loadConfigFromEnv*(cfg: var BaraConfig) =
cfg.jwtSecret = getEnv("BARADB_JWT_SECRET", cfg.jwtSecret)
cfg.rateLimitGlobal = parseEnvInt(getEnv("BARADB_RATE_LIMIT_GLOBAL", ""), cfg.rateLimitGlobal)
cfg.rateLimitPerClient = parseEnvInt(getEnv("BARADB_RATE_LIMIT_PER_CLIENT", ""), cfg.rateLimitPerClient)
cfg.raftEnabled = parseEnvBool(getEnv("BARADB_RAFT_ENABLED", ""), cfg.raftEnabled)
cfg.raftPort = parseEnvInt(getEnv("BARADB_RAFT_PORT", ""), cfg.raftPort)
let peersEnv = getEnv("BARADB_RAFT_PEERS", "")
if peersEnv.len > 0:
cfg.raftPeers = peersEnv.split(",")
cfg.raftNodeId = getEnv("BARADB_RAFT_NODE_ID", cfg.raftNodeId)
# ----------------------------------------------------------------------
# Master Loader
+18 -2
View File
@@ -32,6 +32,8 @@ type
log*: seq[LogEntry]
commitIndex*: uint64
lastApplied*: uint64
# State machine callback
applyCommand*: proc(cmd: string, data: seq[byte]) {.gcsafe.}
# Leader state
nextIndex*: Table[string, uint64]
matchIndex*: Table[string, uint64]
@@ -115,6 +117,15 @@ proc lastLogTerm*(node: RaftNode): uint64 =
return 0
return node.log[^1].term
proc applyCommitted(node: RaftNode) =
while node.lastApplied < node.commitIndex:
let idx = int(node.lastApplied)
if idx < node.log.len:
let entry = node.log[idx]
if node.applyCommand != nil:
node.applyCommand(entry.command, entry.data)
inc node.lastApplied
proc becomeFollower*(node: RaftNode, term: uint64) =
node.state = rsFollower
node.currentTerm = term
@@ -198,6 +209,7 @@ proc handleAppendEntries*(node: RaftNode, msg: RaftMessage): RaftMessage =
# Update commit index
if msg.leaderCommit > node.commitIndex:
node.commitIndex = min(msg.leaderCommit, node.lastLogIndex)
node.applyCommitted()
reply.success = true
reply.matchIdx = node.lastLogIndex
@@ -283,6 +295,7 @@ proc handleAppendReply*(node: RaftNode, peerId: string, reply: RaftMessage) =
if medianIdx <= node.lastLogIndex and
node.log[medianIdx - 1].term == node.currentTerm:
node.commitIndex = medianIdx
node.applyCommitted()
else:
if node.nextIndex[peerId] > 1:
dec node.nextIndex[peerId]
@@ -479,7 +492,10 @@ proc receiveLoop(net: RaftNetwork, client: AsyncSocket) {.async.} =
for i in 0 ..< payloadLen:
payload[i] = byte(payloadStr[i])
let msg = deserializeRaftMessage(payload)
asyncCheck net.processMessage(msg)
try:
await net.processMessage(msg)
except:
discard
except:
discard
finally:
@@ -496,7 +512,7 @@ proc heartbeatLoop(net: RaftNetwork) {.async.} =
proc run*(net: RaftNetwork) {.async.} =
net.socket = newAsyncSocket()
net.socket.setSockOpt(OptReuseAddr, true)
net.socket.bindAddr(Port(net.node.raftPort), "127.0.0.1")
net.socket.bindAddr(Port(net.node.raftPort))
net.socket.listen()
net.running = true
asyncCheck net.heartbeatLoop()
+16
View File
@@ -12,6 +12,7 @@ import barabadb/core/logging
import barabadb/protocol/ssl
import barabadb/storage/lsm
import barabadb/storage/compaction
import barabadb/core/raft
type
CompactionManager* = ref object
@@ -81,6 +82,21 @@ proc main() =
let cm = newCompactionManager(httpServer.db)
asyncCheck cm.startCompactionLoop()
# Start Raft cluster if enabled
if config.raftEnabled:
info("Starting Raft node " & config.raftNodeId & " on port " & $config.raftPort)
var raftNode = newRaftNode(config.raftNodeId, config.raftPeers, config.raftPort)
# Wire state machine to apply committed entries to the database
raftNode.applyCommand = proc(cmd: string, data: seq[byte]) {.gcsafe.} =
if cmd == "put":
let parts = cast[string](data).split("\x00")
if parts.len >= 2:
httpServer.db.put(parts[0], cast[seq[byte]](parts[1]))
elif cmd == "delete":
httpServer.db.delete(cast[string](data))
var raftNet = newRaftNetwork(raftNode)
asyncCheck raftNet.run()
# Start TCP wire protocol server on main thread with async event loop
waitFor runTcpServer(config)