fix(raft): multi-stmt write gate, rich apply, delete kv convention
CI / test (push) Has been cancelled
CI / verify (push) Has been cancelled
Clients CI / build-server (push) Has been cancelled
Clients CI / test-python (push) Has been cancelled
Clients CI / test-javascript (push) Has been cancelled
Clients CI / test-nim (push) Has been cancelled
Clients CI / test-rust (push) Has been cancelled

- Reject follower writes if any statement in the batch is DML/COMMIT
  (not only stmts[0]).
- COMMIT always emits empty-valued kvPairs for isDelete entries.
- applyCommand updates LSM plus secondary B-tree/FTS/HNSW indexes
  (applyReplicatedPut/Delete) so follower index scans see replicated rows.
- Tests: not-leader append, commit timeout, index apply unit, E2E
  index-backed SELECT on follower.
This commit is contained in:
2026-07-30 21:14:46 +03:00
parent a462d21b25
commit 0d51497f57
7 changed files with 193 additions and 9 deletions
+38
View File
@@ -2706,6 +2706,44 @@ suite "Raft SQL Write Path":
check res.keyValuePairs.len == 1
check res.keyValuePairs[0][1].len == 0
test "appendWriteToRaft fails when node is not leader":
var n = newRaftNode("n1", @["n2"], raftPort = 29111)
# Still a follower — appendLog returns index 0.
let (ok, err) = waitFor appendWriteToRaft(n,
@[("k", cast[seq[byte]]("v"))], timeoutMs = 200)
check not ok
check "lost leadership" in err
test "appendWriteToRaft times out without majority replies":
# Leader with peers but no network — commitIndex never advances.
var n = newRaftNode("n1", @["n2", "n3"], raftPort = 29112)
n.becomeLeader()
let (ok, err) = waitFor appendWriteToRaft(n,
@[("k", cast[seq[byte]]("v"))], timeoutMs = 300)
check not ok
check "raft commit timeout" in err
test "applyReplicatedPut updates secondary B-tree indexes":
var testDir = getTempDir() / "baradb_raft_apply_idx_" & $getCurrentProcessId() & "_" & $getMonoTime().ticks
createDir(testDir)
var db = newLSMTree(testDir)
var ctx = qexec.newExecutionContext(db)
discard qexec.executeQuery(ctx, parse(
"CREATE TABLE t (id INT PRIMARY KEY, name STRING)"))
discard qexec.executeQuery(ctx, parse(
"CREATE INDEX idx_name ON t (name)"))
# Simulate follower apply of a leader-replicated INSERT (no execInsert).
applyReplicatedPut(ctx, "t.id=1", cast[seq[byte]]("name=alice"))
check "t.name" in ctx.btrees
let entries = ctx.btrees["t.name"].get("alice")
check entries.len >= 1
check entries[0].lsmKey == "t.id=1"
# Delete must drop the index entry too.
applyReplicatedDelete(ctx, "t.id=1")
check ctx.btrees["t.name"].get("alice").len == 0
let (found, _) = db.get("t.id=1")
check not found
suite "CLI Autocomplete":
test "Autocomplete commands":
let res = autocomplete("he")