feat: zero-copy serialization, adaptive query, distributed txns, vector batch/rebuild — 222 tests

Zero-Copy Serialization:
- Direct memory buffer with schema-based field offsets
- Write/read int32/int64/float/bool/string without copies
- FastMem copy operations (fastCopy, fastCopyFrom, slice)
- ZcTable for batch columnar records

Adaptive Query Execution:
- Cardinality estimation with exponential moving average
- Reoptimize triggers when actual/estimated row ratio exceeds threshold
- Plan caching with hash-based lookup
- Execution context with parallelism hints and explain

Distributed Transactions:
- Two-phase commit across multiple nodes
- Saga pattern with step-by-step execute/compensate
- DistTxnManager with cleanup lifecycle

Vector Batch Operations:
- batchInsert/batchSearch for HNSW and IVF-PQ
- IndexWatcher with auto-rebuild based on unindexed count and ratio
- Rebuild statistics tracking

26 new tests (222 total, all passing)
This commit is contained in:
2026-05-06 01:57:28 +03:00
parent 3ed3036b11
commit d80ec4e449
5 changed files with 924 additions and 1 deletions
+94
View File
@@ -3,6 +3,7 @@ import std/math
import std/algorithm
import std/random
import std/tables
import std/monotimes
type
DistanceMetric* = enum
@@ -227,3 +228,96 @@ proc clear*(idx: HNSWIndex) =
idx.nodes.clear()
idx.entryPoint = 0
idx.maxLevel = 0
proc clear*(idx: IVFPQIndex) =
for i in 0..<idx.nClusters:
idx.clusters[i].entries.setLen(0)
# Batch insert for HNSW
proc batchInsert*(idx: HNSWIndex, batch: seq[(uint64, Vector)],
metadata: seq[Table[string, string]] = @[]) =
for i, (id, vec) in batch:
var meta = initTable[string, string]()
if i < metadata.len:
meta = metadata[i]
idx.insert(id, vec, meta)
# Batch insert for IVF-PQ
proc batchInsert*(idx: IVFPQIndex, batch: seq[(uint64, Vector)]) =
var entries: seq[VectorEntry] = @[]
for (id, vec) in batch:
entries.add(VectorEntry(id: id, vector: vec, metadata: @[]))
idx.train(entries, nIterations = 5)
# Batch search
proc batchSearch*(idx: HNSWIndex, queries: seq[Vector], k: int,
metric: DistanceMetric = dmCosine): seq[seq[(uint64, float64)]] =
result = newSeq[seq[(uint64, float64)]](queries.len)
for i, query in queries:
result[i] = idx.search(query, k, metric)
# Auto-rebuild index when threshold exceeded
type
RebuildConfig* = object
maxUnindexedCount*: int
checkInterval*: int64 # nanoseconds
rebuildThreshold*: float64 # ratio of unindexed/total to trigger rebuild
autoRebuild*: bool
IndexWatcher* = ref object
config: RebuildConfig
unindexedCount: int
totalCount: int
lastCheck: int64
lastRebuild: int64
rebuildsCount: int
proc defaultRebuildConfig*(): RebuildConfig =
RebuildConfig(
maxUnindexedCount: 10000,
checkInterval: 60_000_000_000, # 1 minute
rebuildThreshold: 0.1, # 10% unindexed triggers rebuild
autoRebuild: true,
)
proc newIndexWatcher*(config: RebuildConfig = defaultRebuildConfig()): IndexWatcher =
IndexWatcher(
config: config,
unindexedCount: 0,
totalCount: 0,
lastCheck: 0,
lastRebuild: 0,
rebuildsCount: 0,
)
proc trackInsert*(watcher: IndexWatcher) =
inc watcher.totalCount
proc trackUnindexed*(watcher: IndexWatcher, count: int = 1) =
watcher.unindexedCount += count
proc shouldRebuild*(watcher: IndexWatcher): bool =
if not watcher.config.autoRebuild:
return false
if watcher.unindexedCount > watcher.config.maxUnindexedCount:
return true
if watcher.totalCount == 0:
return false
let ratio = float64(watcher.unindexedCount) / float64(watcher.totalCount)
if ratio > watcher.config.rebuildThreshold:
return true
return false
proc markRebuilt*(watcher: IndexWatcher) =
watcher.unindexedCount = 0
inc watcher.rebuildsCount
watcher.lastRebuild = getMonoTime().ticks()
proc stats*(watcher: IndexWatcher): (int, int, int) =
return (watcher.totalCount, watcher.unindexedCount, watcher.rebuildsCount)
proc rebuildIfNeeded*(watcher: IndexWatcher, idx: HNSWIndex,
rebuildFn: proc(idx: HNSWIndex)) =
if watcher.shouldRebuild():
rebuildFn(idx)
watcher.markRebuilt()