don't rewrite hash->slot map (#2463)
Avoid writing the same slot/hash values to the hash->slot mapping to avoid spamming the rocksdb WAL and cause unnecessary compaction In the same vein, avoid writing trivially detectable A-B-A storage changes which happen with surprising frequency.
This commit is contained in:
parent
9a499eb45f
commit
ab23148aab
|
@ -34,6 +34,7 @@ const
|
|||
# which would cause a worst case of 386MB memory usage though in reality
|
||||
# code sizes are much smaller - it would make sense to study these numbers
|
||||
# in greater detail.
|
||||
slotsLruSize = 16 * 1024
|
||||
|
||||
type
|
||||
AccountFlag = enum
|
||||
|
@ -80,6 +81,11 @@ type
|
|||
## when underpriced code opcodes are being run en masse - both advantages
|
||||
## help performance broadly as well.
|
||||
|
||||
slots: KeyedQueue[UInt256, Hash256]
|
||||
## Because the same slots often reappear, we want to avoid writing them
|
||||
## over and over again to the database to avoid the WAL and compation
|
||||
## write amplification that ensues
|
||||
|
||||
ReadOnlyStateDB* = distinct AccountsLedgerRef
|
||||
|
||||
TransactionState = enum
|
||||
|
@ -302,7 +308,8 @@ proc originalStorageValue(
|
|||
|
||||
# Not in the original values cache - go to the DB.
|
||||
let
|
||||
slotKey = slot.toBytesBE.keccakHash
|
||||
slotKey = ac.slots.lruFetch(slot).valueOr:
|
||||
slot.toBytesBE.keccakHash
|
||||
rc = ac.ledger.slotFetch(acc.toAccountKey, slotKey)
|
||||
if rc.isOk:
|
||||
result = rc.value
|
||||
|
@ -368,29 +375,39 @@ proc persistStorage(acc: AccountRef, ac: AccountsLedgerRef) =
|
|||
|
||||
# Save `overlayStorage[]` on database
|
||||
for slot, value in acc.overlayStorage:
|
||||
let slotKey = slot.toBytesBE.keccakHash
|
||||
acc.originalStorage[].withValue(slot, v):
|
||||
if v[] == value:
|
||||
continue # Avoid writing A-B-A updates
|
||||
|
||||
var cached = true
|
||||
let slotKey = ac.slots.lruFetch(slot).valueOr:
|
||||
cached = false
|
||||
ac.slots.lruAppend(slot, slot.toBytesBE.keccakHash, slotsLruSize)
|
||||
|
||||
if value > 0:
|
||||
ac.ledger.slotMerge(acc.toAccountKey, slotKey, value).isOkOr:
|
||||
raiseAssert info & $$error
|
||||
|
||||
# move the overlayStorage to originalStorage, related to EIP2200, EIP1283
|
||||
acc.originalStorage[slot] = value
|
||||
|
||||
else:
|
||||
ac.ledger.slotDelete(acc.toAccountKey, slotKey).isOkOr:
|
||||
if error.error != StoNotFound:
|
||||
raiseAssert info & $$error
|
||||
discard
|
||||
let
|
||||
key = slot.toBytesBE.keccakHash.data.slotHashToSlotKey
|
||||
rc = ac.kvt.put(key.toOpenArray, blobify(slot).data)
|
||||
if rc.isErr:
|
||||
warn logTxt "persistStorage()", slot, error=($$rc.error)
|
||||
|
||||
# move the overlayStorage to originalStorage, related to EIP2200, EIP1283
|
||||
for slot, value in acc.overlayStorage:
|
||||
if value > 0:
|
||||
acc.originalStorage[slot] = value
|
||||
else:
|
||||
acc.originalStorage.del(slot)
|
||||
acc.overlayStorage.clear()
|
||||
|
||||
if not cached:
|
||||
# Write only if it was not cached to avoid writing the same data over and
|
||||
# over..
|
||||
let
|
||||
key = slotKey.data.slotHashToSlotKey
|
||||
rc = ac.kvt.put(key.toOpenArray, blobify(slot).data)
|
||||
if rc.isErr:
|
||||
warn logTxt "persistStorage()", slot, error=($$rc.error)
|
||||
|
||||
acc.overlayStorage.clear()
|
||||
|
||||
proc makeDirty(ac: AccountsLedgerRef, address: EthAddress, cloneStorage = true): AccountRef =
|
||||
ac.isDirty = true
|
||||
|
|
|
@ -13,20 +13,21 @@ import
|
|||
|
||||
type
|
||||
DBKeyKind* = enum
|
||||
genericHash
|
||||
blockNumberToHash
|
||||
blockHashToScore
|
||||
transactionHashToBlock
|
||||
canonicalHeadHash
|
||||
slotHashToSlot
|
||||
contractHash
|
||||
transitionStatus
|
||||
safeHash
|
||||
finalizedHash
|
||||
skeletonProgress
|
||||
skeletonBlockHashToNumber
|
||||
skeletonHeader
|
||||
skeletonBody
|
||||
# Careful - changing the assigned ordinals will break existing databases
|
||||
genericHash = 0
|
||||
blockNumberToHash = 1
|
||||
blockHashToScore = 2
|
||||
transactionHashToBlock = 3
|
||||
canonicalHeadHash = 4
|
||||
slotHashToSlot = 5
|
||||
contractHash = 6
|
||||
transitionStatus = 7
|
||||
safeHash = 8
|
||||
finalizedHash = 9
|
||||
skeletonProgress = 10
|
||||
skeletonBlockHashToNumber = 11
|
||||
skeletonHeader = 12
|
||||
skeletonBody = 13
|
||||
|
||||
DbKey* = object
|
||||
# The first byte stores the key type. The rest are key-specific values
|
||||
|
|
Loading…
Reference in New Issue