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
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:
@@ -1,5 +1,6 @@
|
||||
## B-Tree Index — ordered key-value index
|
||||
import std/tables
|
||||
import std/locks
|
||||
|
||||
const
|
||||
DefaultBTreeOrder* = 32
|
||||
@@ -16,6 +17,7 @@ type
|
||||
root: BTreeNode[K, V]
|
||||
order: int
|
||||
size: int
|
||||
lock*: Lock
|
||||
|
||||
proc newBTreeNode[K, V](isLeaf: bool = true): BTreeNode[K, V] =
|
||||
BTreeNode[K, V](
|
||||
@@ -24,7 +26,8 @@ proc newBTreeNode[K, V](isLeaf: bool = true): BTreeNode[K, V] =
|
||||
)
|
||||
|
||||
proc newBTreeIndex*[K, V](order: int = DefaultBTreeOrder): BTreeIndex[K, V] =
|
||||
BTreeIndex[K, V](root: newBTreeNode[K, V](), order: order, size: 0)
|
||||
result = BTreeIndex[K, V](root: newBTreeNode[K, V](), order: order, size: 0)
|
||||
initLock(result.lock)
|
||||
|
||||
proc search[K, V](node: BTreeNode[K, V], key: K): seq[V] =
|
||||
var i = 0
|
||||
@@ -84,66 +87,91 @@ proc insertNonFull[K, V](node: BTreeNode[K, V], key: K, value: V, order: int) =
|
||||
insertNonFull(node.children[i], key, value, order)
|
||||
|
||||
proc insert*[K, V](btree: var BTreeIndex[K, V], key: K, value: V) =
|
||||
if btree.root.keys.len == btree.order - 1:
|
||||
var newRoot = newBTreeNode[K, V](isLeaf = false)
|
||||
newRoot.children.add(btree.root)
|
||||
splitChild(newRoot, 0, btree.order)
|
||||
btree.root = newRoot
|
||||
insertNonFull(btree.root, key, value, btree.order)
|
||||
else:
|
||||
insertNonFull(btree.root, key, value, btree.order)
|
||||
inc btree.size
|
||||
acquire(btree.lock)
|
||||
try:
|
||||
if btree.root.keys.len == btree.order - 1:
|
||||
var newRoot = newBTreeNode[K, V](isLeaf = false)
|
||||
newRoot.children.add(btree.root)
|
||||
splitChild(newRoot, 0, btree.order)
|
||||
btree.root = newRoot
|
||||
insertNonFull(btree.root, key, value, btree.order)
|
||||
else:
|
||||
insertNonFull(btree.root, key, value, btree.order)
|
||||
inc btree.size
|
||||
finally:
|
||||
release(btree.lock)
|
||||
|
||||
proc get*[K, V](btree: BTreeIndex[K, V], key: K): seq[V] =
|
||||
search(btree.root, key)
|
||||
acquire(btree.lock)
|
||||
try:
|
||||
result = search(btree.root, key)
|
||||
finally:
|
||||
release(btree.lock)
|
||||
|
||||
proc contains*[K, V](btree: BTreeIndex[K, V], key: K): bool =
|
||||
return btree.get(key).len > 0
|
||||
acquire(btree.lock)
|
||||
try:
|
||||
result = search(btree.root, key).len > 0
|
||||
finally:
|
||||
release(btree.lock)
|
||||
|
||||
proc scan*[K, V](btree: BTreeIndex[K, V], startKey, endKey: K): seq[(K, seq[V])] =
|
||||
result = @[]
|
||||
var node = btree.root
|
||||
while not node.isLeaf:
|
||||
var i = 0
|
||||
while i < node.keys.len and startKey > node.keys[i]:
|
||||
inc i
|
||||
node = node.children[i]
|
||||
acquire(btree.lock)
|
||||
try:
|
||||
result = @[]
|
||||
var node = btree.root
|
||||
while not node.isLeaf:
|
||||
var i = 0
|
||||
while i < node.keys.len and startKey > node.keys[i]:
|
||||
inc i
|
||||
node = node.children[i]
|
||||
|
||||
while node != nil:
|
||||
for i in 0..<node.keys.len:
|
||||
if node.keys[i] >= startKey:
|
||||
if node.keys[i] <= endKey:
|
||||
result.add((node.keys[i], node.values[i]))
|
||||
else:
|
||||
return
|
||||
node = node.next
|
||||
while node != nil:
|
||||
for i in 0..<node.keys.len:
|
||||
if node.keys[i] >= startKey:
|
||||
if node.keys[i] <= endKey:
|
||||
result.add((node.keys[i], node.values[i]))
|
||||
else:
|
||||
return
|
||||
node = node.next
|
||||
finally:
|
||||
release(btree.lock)
|
||||
|
||||
proc len*[K, V](btree: BTreeIndex[K, V]): int = btree.size
|
||||
proc len*[K, V](btree: BTreeIndex[K, V]): int =
|
||||
acquire(btree.lock)
|
||||
try:
|
||||
result = btree.size
|
||||
finally:
|
||||
release(btree.lock)
|
||||
|
||||
proc remove*[K, V](btree: var BTreeIndex[K, V], key: K, value: V) =
|
||||
proc removeRec(node: BTreeNode[K, V]): bool =
|
||||
var i = 0
|
||||
while i < node.keys.len and key > node.keys[i]:
|
||||
inc i
|
||||
if node.isLeaf:
|
||||
if i < node.keys.len and key == node.keys[i]:
|
||||
var vals = node.values[i]
|
||||
var idx = -1
|
||||
for j in 0..<vals.len:
|
||||
if vals[j] == value:
|
||||
idx = j
|
||||
break
|
||||
if idx >= 0:
|
||||
vals.del(idx)
|
||||
if vals.len == 0:
|
||||
node.keys.del(i)
|
||||
node.values.del(i)
|
||||
else:
|
||||
node.values[i] = vals
|
||||
return true
|
||||
return false
|
||||
else:
|
||||
return removeRec(node.children[i])
|
||||
acquire(btree.lock)
|
||||
try:
|
||||
proc removeRec(node: BTreeNode[K, V]): bool =
|
||||
var i = 0
|
||||
while i < node.keys.len and key > node.keys[i]:
|
||||
inc i
|
||||
if node.isLeaf:
|
||||
if i < node.keys.len and key == node.keys[i]:
|
||||
var vals = node.values[i]
|
||||
var idx = -1
|
||||
for j in 0..<vals.len:
|
||||
if vals[j] == value:
|
||||
idx = j
|
||||
break
|
||||
if idx >= 0:
|
||||
vals.del(idx)
|
||||
if vals.len == 0:
|
||||
node.keys.del(i)
|
||||
node.values.del(i)
|
||||
else:
|
||||
node.values[i] = vals
|
||||
return true
|
||||
return false
|
||||
else:
|
||||
return removeRec(node.children[i])
|
||||
|
||||
if removeRec(btree.root):
|
||||
dec btree.size
|
||||
if removeRec(btree.root):
|
||||
dec btree.size
|
||||
finally:
|
||||
release(btree.lock)
|
||||
|
||||
Reference in New Issue
Block a user