Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion execution_chain/pruner.nim
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,11 @@ proc pruneLoop(pruner: BackgroundPrunerRef) {.async: (raises: [CancelledError]).
if header.timestamp >= cutoff:
break

kvt.deleteBlockBodyAndReceiptsBe(header)
if not kvt.deleteBlockBodyAndReceiptsBe(header):
warn "Background pruner: failed to delete block data",
blkNum = currentBlock
break

currentBlock += 1
blocksSinceSave += 1

Expand Down
41 changes: 29 additions & 12 deletions execution_chain/pruner/db_utils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -25,43 +25,60 @@ logScope:
# Direct-backend deletion helpers (bypass transaction layer)
# ------------------------------------------------------------------------------

proc deleteTransactionsBe(kvt: KvtDbRef, txRoot: Hash32) =
proc deleteTransactionsBe(kvt: KvtDbRef, txRoot: Hash32): bool =
if txRoot == EMPTY_ROOT_HASH:
return
return true

kvt.delRangeBe(
hashIndexKey(txRoot, 0), hashIndexKey(txRoot, uint16.high), compactRange = true
).isOkOr:
warn "pruner: deleteTransactionsBe", txRoot, error
return false

proc deleteReceiptsBe(kvt: KvtDbRef, receiptsRoot: Hash32) =
true

proc deleteReceiptsBe(kvt: KvtDbRef, receiptsRoot: Hash32): bool =
if receiptsRoot == EMPTY_ROOT_HASH:
return
return true

kvt.delRangeBe(
hashIndexKey(receiptsRoot, 0),
hashIndexKey(receiptsRoot, uint16.high),
compactRange = true,
).isOkOr:
warn "pruner: deleteReceiptsBe", receiptsRoot, error
return false

true

proc deleteUnclesBe(kvt: KvtDbRef, ommersHash: Hash32) =
proc deleteUnclesBe(kvt: KvtDbRef, ommersHash: Hash32): bool =
if ommersHash == EMPTY_UNCLE_HASH:
return
return true

kvt.delBe(genericHashKey(ommersHash).toOpenArray).isOkOr:
warn "pruner: deleteUnclesBe", ommersHash, error
return false

proc deleteWithdrawalsBe(kvt: KvtDbRef, withdrawalsRoot: Hash32) =
true

proc deleteWithdrawalsBe(kvt: KvtDbRef, withdrawalsRoot: Hash32): bool =
if withdrawalsRoot == EMPTY_ROOT_HASH:
return
return true

kvt.delBe(withdrawalsKey(withdrawalsRoot).toOpenArray).isOkOr:
warn "pruner: deleteWithdrawalsBe", withdrawalsRoot, error
return false

true

proc deleteBlockBodyAndReceiptsBe*(kvt: KvtDbRef, header: Header) =
kvt.deleteTransactionsBe(header.transactionsRoot)
kvt.deleteUnclesBe(header.ommersHash)
proc deleteBlockBodyAndReceiptsBe*(kvt: KvtDbRef, header: Header): bool =
if not kvt.deleteTransactionsBe(header.transactionsRoot):
return false
if not kvt.deleteUnclesBe(header.ommersHash):
return false
if header.withdrawalsRoot.isSome:
kvt.deleteWithdrawalsBe(header.withdrawalsRoot.get())
if not kvt.deleteWithdrawalsBe(header.withdrawalsRoot.get()):
return false
kvt.deleteReceiptsBe(header.receiptsRoot)

# ------------------------------------------------------------------------------
Expand Down
45 changes: 45 additions & 0 deletions tests/test_pruner.nim
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,51 @@ suite "Pruner integration tests":
for blkNum in 1'u64 .. 3'u64:
check bt2.getBlockHeader(BlockNumber blkNum).isOk

test "pruner does not advance tail when backend deletion fails":
let com = env.newCom()
var chain = ForkedChainRef.init(com, baseDistance = 0, persistBatchSize = 1)
let
genesis = Block.init(com.genesisHeader, BlockBody())
baseTxFrame = com.db.baseTxFrame()
txFrame = baseTxFrame.txFrameBegin
blk1 = txFrame.makeBlk(1, genesis)
blk2 = txFrame.makeBlk(2, blk1)
blk3 = txFrame.makeBlk(3, blk2)
txFrame.dispose()

check (waitFor chain.importBlock(blk1)).isOk
check (waitFor chain.forkChoice(blk1.blockHash, blk1.blockHash)).isOk
check (waitFor chain.importBlock(blk2)).isOk
check (waitFor chain.forkChoice(blk2.blockHash, blk2.blockHash)).isOk
check (waitFor chain.importBlock(blk3)).isOk
check (waitFor chain.forkChoice(blk3.blockHash, blk3.blockHash)).isOk

let
kvt = com.db.kvt
hdr1 = com.db.baseTxFrame().getBlockHeader(BlockNumber 1).expect("header exists")
wdRoot1 = hdr1.withdrawalsRoot.get()
originalDelKvpFn = kvt.delKvpFn

check kvt.hasBe(withdrawalsKey(wdRoot1).toOpenArray)
kvt.putBe(tailIdKey().toOpenArray, BlockNumber(1).toBytesLE())

kvt.delKvpFn =
proc(key: openArray[byte]): Result[void, KvtError] {.gcsafe, raises: [].} =
discard key
err(RdbBeDriverDelError)

let pruner = BackgroundPrunerRef.init(com,
batchSize = 10,
loopDelay = chronos.milliseconds(50))
pruner.start()

waitFor sleepAsync(chronos.milliseconds(150))
waitFor pruner.stop()
kvt.delKvpFn = originalDelKvpFn

check kvt.getHistoryExpiredBe() == BlockNumber(1)
check kvt.hasBe(withdrawalsKey(wdRoot1).toOpenArray)

test "getHistoryExpiredBe returns 0 when not set":
let com = env.newCom()
let kvt = com.db.kvt
Expand Down
Loading