feat: crash recovery, Raft election timer, CLI autocomplete, roadmap update — 246 tests

Crash Recovery:
- WAL file scanning with magic/version/entry parsing
- REDO/UNDO analysis — identify committed vs uncommitted txns
- Summary reporting with entry/txn/apply status

Raft Election Timer:
- ElectionTimer with configurable timeout and tick/check/reset
- Automatic election start on follower timeout
- Candidate timeout restart on failed elections

CLI Autocomplete:
- Full autocomplete engine for commands and SQL keywords
- Suggest function for single/multi completions
- 60+ keywords (SELECT, FROM, WHERE, JOIN, GROUP BY, etc.)

ROADMAP.md:
- All completed items properly marked
- Updated status table — 9 out of 12 phases at 90%+

14 new tests (246 total, all passing)
This commit is contained in:
2026-05-06 02:12:49 +03:00
parent a7e66cb661
commit b9f6059cbf
5 changed files with 402 additions and 45 deletions
+45
View File
@@ -4,6 +4,7 @@ import std/sets
import std/deques
import std/algorithm
import std/random
import std/monotimes
type
RaftState* = enum
@@ -278,3 +279,47 @@ proc state*(node: RaftNode): RaftState = node.state
proc isLeader*(node: RaftNode): bool = node.state == rsLeader
proc leaderId*(node: RaftNode): string = node.leaderId
proc logLen*(node: RaftNode): int = node.log.len
# Leader election timer loop
type
ElectionTimer* = ref object
node: RaftNode
timeoutMs: int
lastHeartbeat: int64
running: bool
proc newElectionTimer*(node: RaftNode, timeoutMs: int = 150): ElectionTimer =
ElectionTimer(
node: node,
timeoutMs: timeoutMs,
lastHeartbeat: getMonoTime().ticks(),
running: false,
)
proc resetTimeout*(timer: ElectionTimer) =
timer.lastHeartbeat = getMonoTime().ticks()
proc checkTimeout*(timer: ElectionTimer): bool =
let elapsed = (getMonoTime().ticks() - timer.lastHeartbeat) div 1_000_000
return elapsed > timer.timeoutMs
proc startElection*(timer: ElectionTimer) =
if timer.node.state != rsCandidate:
timer.node.becomeCandidate()
proc tick*(timer: ElectionTimer) =
case timer.node.state
of rsFollower:
if timer.checkTimeout():
timer.startElection()
timer.resetTimeout()
of rsCandidate:
if timer.checkTimeout():
# Election timed out — restart
timer.node.becomeCandidate()
timer.resetTimeout()
of rsLeader:
timer.resetTimeout() # Keep alive
proc stop*(timer: ElectionTimer) =
timer.running = false