Storage hardening: btree size fix, WAL entryCount note, LSM put error handling
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

- btree.nim: insertNonFull returns bool; size only increments for new keys
- wal.nim: comment about entryCount reset on append
- lsm.nim: recovery/put/delete/putUnsafe raise IOError if put fails after flush
This commit is contained in:
2026-05-18 12:14:30 +03:00
parent 8596cea548
commit 3f29c519d4
3 changed files with 22 additions and 11 deletions
+10 -6
View File
@@ -69,16 +69,18 @@ proc splitChild[K, V](parent: BTreeNode[K, V], index: int, order: int) =
else:
child.keys.setLen(mid)
proc insertNonFull[K, V](node: BTreeNode[K, V], key: K, value: V, order: int) =
proc insertNonFull[K, V](node: BTreeNode[K, V], key: K, value: V, order: int): bool =
## Returns true if a new key was inserted, false if existing key got a new value.
var i = node.keys.len - 1
if node.isLeaf:
while i >= 0 and key < node.keys[i]:
dec i
if i >= 0 and key == node.keys[i]:
node.values[i].add(value)
return
return false
node.keys.insert(key, i + 1)
node.values.insert(@[value], i + 1)
return true
else:
while i >= 0 and key < node.keys[i]:
dec i
@@ -87,20 +89,22 @@ proc insertNonFull[K, V](node: BTreeNode[K, V], key: K, value: V, order: int) =
splitChild(node, i, order)
if key > node.keys[i]:
inc i
insertNonFull(node.children[i], key, value, order)
return insertNonFull(node.children[i], key, value, order)
proc insert*[K, V](btree: var BTreeIndex[K, V], key: K, value: V) =
acquire(btree.lock)
try:
var inserted = false
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)
inserted = insertNonFull(btree.root, key, value, btree.order)
else:
insertNonFull(btree.root, key, value, btree.order)
inc btree.size
inserted = insertNonFull(btree.root, key, value, btree.order)
if inserted:
inc btree.size
finally:
release(btree.lock)
+10 -5
View File
@@ -375,11 +375,13 @@ proc newLSMTree*(dir: string, memMaxSize: int = DefaultMemTableSize): LSMTree =
of wekPut:
if not result.memTable.put(key, value, timestamp):
result.flushUnsafe()
discard result.memTable.put(key, value, timestamp)
if not result.memTable.put(key, value, timestamp):
raise newException(IOError, "WAL recovery: failed to insert key into memtable")
of wekDelete:
if not result.memTable.put(key, @[], timestamp, deleted = true):
result.flushUnsafe()
discard result.memTable.put(key, @[], timestamp, deleted = true)
if not result.memTable.put(key, @[], timestamp, deleted = true):
raise newException(IOError, "WAL recovery: failed to insert delete tombstone into memtable")
of wekCommit:
discard
of wekCheckpoint:
@@ -401,7 +403,8 @@ proc put*(db: LSMTree, key: string, value: seq[byte]) =
db.flushUnsafe()
db.immutableMem = db.memTable
db.memTable = newMemTable(db.memMaxSize)
discard db.memTable.put(key, value, ts)
if not db.memTable.put(key, value, ts):
raise newException(IOError, "LSM put failed after flush")
proc delete*(db: LSMTree, key: string) =
let ts = uint64(getMonoTime().ticks())
@@ -416,7 +419,8 @@ proc delete*(db: LSMTree, key: string) =
db.flushUnsafe()
db.immutableMem = db.memTable
db.memTable = newMemTable(db.memMaxSize)
discard db.memTable.put(key, @[], ts, deleted = true)
if not db.memTable.put(key, @[], ts, deleted = true):
raise newException(IOError, "LSM delete failed after flush")
proc putUnsafe*(db: LSMTree, key: string, value: seq[byte], deleted: bool = false) =
## Direct LSM insert without WAL logging — used by recovery.
@@ -428,7 +432,8 @@ proc putUnsafe*(db: LSMTree, key: string, value: seq[byte], deleted: bool = fals
db.flushUnsafe()
db.immutableMem = db.memTable
db.memTable = newMemTable(db.memMaxSize)
discard db.memTable.put(key, value, ts, deleted)
if not db.memTable.put(key, value, ts, deleted):
raise newException(IOError, "LSM putUnsafe failed after flush")
proc deleteUnsafe*(db: LSMTree, key: string) =
putUnsafe(db, key, @[], deleted = true)
+2
View File
@@ -38,6 +38,8 @@ proc newWriteAheadLog*(dir: string, syncOnWrite: bool = true): WriteAheadLog =
stream.write(WALMagic)
stream.write(WALVersion)
stream.flush()
# NOTE: entryCount is reset to 0 when appending to existing WAL.
# For an accurate count, call readEntries() after construction.
WriteAheadLog(path: path, stream: stream, entryCount: 0, syncOnWrite: syncOnWrite)
proc writeEntry*(wal: var WriteAheadLog, entry: WalEntry) =