Week 4: Production Hardening — Thread Safety, Fuzz Tests, Property-Based Tests, NimForum Smoke Test
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

Thread Safety:
- Add lock to BTreeIndex (insert/get/scan/remove/contains)
- Add lock to InvertedIndex (addDocument/removeDocument/search/bm25/tfidf/fuzzy/regex)
- Add ctxLock to ExecutionContext for DDL/schema metadata, autoIncCounters, sequences
- Deep-copy sessionVars in cloneForConnection() to isolate per-connection state
- Fix activeConnections check-then-act race with Lock

Wire Protocol Hardening:
- Fix RangeDefect in deserializeValue for invalid FieldKind bytes
- Fix IndexDefect in fkFloat32/fkFloat64 by reusing readUint32/readUint64
- Fix int32/int64 cast from uint32/uint64 to use cast[] instead of constructor
- Fix parseHeader to validate MsgKind instead of holey enum cast

Property-Based Tests (tests/prop_test.nim):
- 8 invariants for evalExprValue: literal types, commutativity, identity,
  double negation, NULL propagation

Wire Protocol Fuzz (tests/fuzz_test.nim):
- 7 fuzz tests: random bytes deserialization, truncated buffers,
  MsgKind casts, parseHeader logic, message roundtrips

NimForum Adapter Smoke Test (tests/nimforum_smoke_test.nim):
- Full TCP server lifecycle with adapter CRUD and parameterized queries

Test Results: 423 tests pass, 0 failures
This commit is contained in:
2026-05-17 11:57:55 +03:00
parent 6021bfcb10
commit 19fa760604
9 changed files with 676 additions and 241 deletions
+33 -8
View File
@@ -7,6 +7,7 @@ import std/tables
import std/os
import std/endians
import std/monotimes
import std/locks
import config
import logging
import ../protocol/wire
@@ -37,6 +38,7 @@ type
gossipProtocol*: GossipProtocol
tls*: TLSContext
activeConnections*: int
activeConnectionsLock*: Lock
proc newServer*(config: BaraConfig): Server =
let dataDir = config.dataDir / "server"
@@ -83,13 +85,14 @@ proc newServer*(config: BaraConfig): Server =
gp.onSuspect = proc(nodeId: string) {.gcsafe.} =
cm.onNodeSuspect(nodeId)
Server(config: config, running: false, db: db, ctx: ctx,
result = Server(config: config, running: false, db: db, ctx: ctx,
txnManager: ctx.txnManager, distTxnManager: newDistTxnManager(),
replicationManager: newReplicationManager(),
shardRouter: shardRouter,
clusterMembership: cm,
gossipProtocol: gp,
tls: tls)
initLock(result.activeConnectionsLock)
# ----------------------------------------------------------------------
# Wire Protocol Helpers
@@ -105,9 +108,13 @@ proc parseHeader(data: string): (bool, MessageHeader) =
if data.len < 12:
return (false, MessageHeader())
let rawKind = readUint32BE(data, 0)
{.push warning[HoleEnumConv]: off.}
let kind = MsgKind(rawKind)
{.pop.}
let kind = cast[MsgKind](rawKind)
case kind
of mkClientHandshake, mkQuery, mkQueryParams, mkExecute, mkBatch, mkTransaction, mkClose, mkPing, mkAuth,
mkServerHandshake, mkReady, mkData, mkComplete, mkError, mkAuthChallenge, mkAuthOk, mkSchemaChange, mkPong, mkTransactionState:
discard
else:
return (false, MessageHeader())
let length = readUint32BE(data, 4)
let requestId = readUint32BE(data, 8)
return (true, MessageHeader(kind: kind, length: length, requestId: requestId))
@@ -523,8 +530,12 @@ proc handleClient(server: Server, client: AsyncSocket, clientId: int) {.async.}
except Exception as e:
errorMsg("Client " & $clientId & " error: " & e.msg)
finally:
if server.activeConnections > 0:
dec server.activeConnections
acquire(server.activeConnectionsLock)
try:
if server.activeConnections > 0:
dec server.activeConnections
finally:
release(server.activeConnectionsLock)
info("Client " & $clientId & " disconnected")
client.close()
@@ -547,7 +558,16 @@ proc run*(server: Server) {.async.} =
info("BaraDB listening on " & server.config.address & ":" & $server.config.port)
while server.running:
let client = await sock.accept()
if server.config.maxConnections > 0 and server.activeConnections >= server.config.maxConnections:
acquire(server.activeConnectionsLock)
var shouldAccept = true
try:
if server.config.maxConnections > 0 and server.activeConnections >= server.config.maxConnections:
shouldAccept = false
else:
inc server.activeConnections
finally:
release(server.activeConnectionsLock)
if not shouldAccept:
client.close()
continue
if server.tls != nil:
@@ -555,10 +575,15 @@ proc run*(server: Server) {.async.} =
server.tls.wrapServer(client)
except Exception as e:
errorMsg("TLS handshake failed: " & e.msg)
acquire(server.activeConnectionsLock)
try:
if server.activeConnections > 0:
dec server.activeConnections
finally:
release(server.activeConnectionsLock)
client.close()
continue
inc clientId
inc server.activeConnections
asyncCheck server.handleClient(client, clientId)
proc stop*(server: Server) =