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
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:
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) =
|
||||
|
||||
Reference in New Issue
Block a user