Skip to content

Remove account leaf cache #3275

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
5 changes: 2 additions & 3 deletions execution_chain/db/aristo/aristo_constants.nim
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ const
v += 1'u64 shl (i * 4)
v

ACC_LRU_SIZE* = 1024 * 1024
## LRU cache size for accounts that have storage, see `.accLeaves` and
## `.stoLeaves` fields of the main descriptor.
STO_LRU_SIZE* = 1024 * 1024
## LRU cache size for the storage leaf cache (see AristoDbRef.stoLeaves)

# End
12 changes: 1 addition & 11 deletions execution_chain/db/aristo/aristo_delete.nim
Original file line number Diff line number Diff line change
Expand Up @@ -147,15 +147,7 @@ proc deleteAccountRecord*(
if stoID.isValid:
?db.delStoTreeImpl(stoID.vid, accPath)

let otherLeaf = ?db.deleteImpl(accHike)

db.layersPutAccLeaf(accPath, nil)

if otherLeaf.isValid:
db.layersPutAccLeaf(
Hash32(getBytes(NibblesBuf.fromBytes(accPath.data).replaceSuffix(otherLeaf.pfx))),
AccLeafRef(otherLeaf),
)
discard ?db.deleteImpl(accHike)

ok()

Expand Down Expand Up @@ -214,7 +206,6 @@ proc deleteStorageData*(
# De-register the deleted storage tree from the account record
let leaf = AccLeafRef(wpAcc.vtx).dup # Dup on modify
leaf.stoID.isValid = false
db.layersPutAccLeaf(accPath, leaf)
db.layersPutVtx((accHike.root, wpAcc.vid), leaf)

ok()
Expand Down Expand Up @@ -248,7 +239,6 @@ proc deleteStorageTree*(
# De-register the deleted storage tree from the accounts record
let leaf = accVtx.dup # Dup on modify
leaf.stoID.isValid = false
db.layersPutAccLeaf(accPath, leaf)
db.layersPutVtx((accHike.root, wpAcc.vid), leaf)
ok()

Expand Down
10 changes: 0 additions & 10 deletions execution_chain/db/aristo/aristo_desc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ type
kMap*: Table[RootedVertexID,HashKey] ## Merkle hash key mapping
vTop*: VertexID ## Last used vertex ID

accLeaves*: Table[Hash32, AccLeafRef] ## Account path -> VertexRef
stoLeaves*: Table[Hash32, StoLeafRef] ## Storage path -> VertexRef

blockNumber*: Opt[uint64] ## Block number set when checkpointing the frame
Expand All @@ -85,7 +84,6 @@ type

Snapshot* = object
vtx*: Table[RootedVertexID, VtxSnapshot]
acc*: Table[Hash32, (AccLeafRef, int)]
sto*: Table[Hash32, (StoLeafRef, int)]
level*: Opt[int] # when this snapshot was taken

Expand All @@ -110,14 +108,6 @@ type

txRef*: AristoTxRef ## Bottom-most in-memory frame

accLeaves*: LruCache[Hash32, AccLeafRef]
## Account path to payload cache - accounts are frequently accessed by
## account path when contracts interact with them - this cache ensures
## that we don't have to re-traverse the storage trie for every such
## interaction
## TODO a better solution would probably be to cache this in a type
## exposed to the high-level API

stoLeaves*: LruCache[Hash32, StoLeafRef]
## Mixed account/storage path to payload cache - same as above but caches
## the full lookup of storage slots
Expand Down
32 changes: 9 additions & 23 deletions execution_chain/db/aristo/aristo_fetch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,6 @@ proc retrieveLeaf(

return err(FetchPathNotFound)

proc cachedAccLeaf*(db: AristoTxRef; accPath: Hash32): Opt[AccLeafRef] =
# Return vertex from layers or cache, `nil` if it's known to not exist and
# none otherwise
db.layersGetAccLeaf(accPath) or
db.db.accLeaves.get(accPath) or
Opt.none(AccLeafRef)

proc cachedStoLeaf*(db: AristoTxRef; mixPath: Hash32): Opt[StoLeafRef] =
# Return vertex from layers or cache, `nil` if it's known to not exist and
# none otherwise
Expand Down Expand Up @@ -132,18 +125,9 @@ proc retrieveAccLeaf(
db: AristoTxRef;
accPath: Hash32;
): Result[AccLeafRef,AristoError] =
if (let leafVtx = db.cachedAccLeaf(accPath); leafVtx.isSome()):
if not leafVtx[].isValid():
return err(FetchPathNotFound)
return ok leafVtx[]

let (staticVtx, path, next) = db.retrieveAccStatic(accPath).valueOr:
if error == FetchPathNotFound:
db.db.accLeaves.put(accPath, nil)
return err(error)
let (staticVtx, path, next) = ? db.retrieveAccStatic(accPath)

if staticVtx.isValid():
db.db.accLeaves.put(accPath, staticVtx)
return ok staticVtx

# Updated payloads are stored in the layers so if we didn't find them there,
Expand All @@ -155,13 +139,10 @@ proc retrieveAccLeaf(
# meaning that it was a hit - else searches for non-existing paths would
# skew the results towards more depth than exists in the MPT
db.db.lookups.hits += 1
db.db.accLeaves.put(accPath, nil)
return err(error)

db.db.lookups.higher += 1

db.db.accLeaves.put(accPath, AccLeafRef(leafVtx))

ok AccLeafRef(leafVtx)

proc retrieveMerkleHash(
Expand Down Expand Up @@ -215,11 +196,16 @@ proc fetchAccountHike*(
): Result[void,AristoError] =
## Expand account path to account leaf or return failure

# Prefer the leaf cache so as not to burden the lower layers
let leaf = db.cachedAccLeaf(accPath)
if leaf == Opt.some(AccLeafRef(nil)):
# Pre-lookup in case the account does not exist
let (staticVtx, path, next) = db.retrieveAccStatic(accPath).valueOr:
return err(FetchAccInaccessible)

let leaf =
if staticVtx.isValid():
Opt.some(staticVtx)
else:
Opt.none(AccLeafRef)

accPath.hikeUp(STATE_ROOT_VID, db, leaf, accHike).isOkOr:
return err(FetchAccInaccessible)

Expand Down
3 changes: 1 addition & 2 deletions execution_chain/db/aristo/aristo_init/init_common.nim
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,7 @@ proc finishSession*(hdl: TypedPutHdlRef; db: TypedBackendRef) =
proc initInstance*(db: AristoDbRef): Result[void, AristoError] =
let vTop = ?db.getTuvFn()
db.txRef = AristoTxRef(db: db, vTop: vTop, snapshot: Snapshot(level: Opt.some(0)))
db.accLeaves = LruCache[Hash32, AccLeafRef].init(ACC_LRU_SIZE)
db.stoLeaves = LruCache[Hash32, StoLeafRef].init(ACC_LRU_SIZE)
db.stoLeaves = LruCache[Hash32, StoLeafRef].init(STO_LRU_SIZE)
ok()

proc finish*(db: AristoDbRef; eradicate = false) =
Expand Down
2 changes: 2 additions & 0 deletions execution_chain/db/aristo/aristo_init/rocks_db/rdb_desc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ type
rdBranchLru*: LruCache[VertexID, (VertexID, uint16)]
rdBranchSize*: int

rdEmptyLru*: LruCache[VertexID,tuple[]] ## Read cache

AristoCFs* = enum
## Column family symbols/handles and names used on the database
AdmCF = "AriAdm" ## Admin column family name
Expand Down
13 changes: 13 additions & 0 deletions execution_chain/db/aristo/aristo_init/rocks_db/rdb_get.nim
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,16 @@ proc getVtx*(
)
return ok(move(rc.value))

block:
var rc =
if GetVtxFlag.PeekCache in flags:
rdb.rdEmptyLru.peek(rvid.vid)
else:
rdb.rdEmptyLru.get(rvid.vid)
if rc.isOk():
rdbVtxLruStats[rvid.to(RdbStateType)][RdbVertexType.Empty].inc(true)
return ok(VertexRef(nil))

# Otherwise fetch from backend database
# A threadvar is used to avoid allocating an environment for onData
var res {.threadvar.}: Result[VertexRef, AristoError]
Expand All @@ -217,6 +227,9 @@ proc getVtx*(

if not gotData:
rdbVtxLruStats[rvid.to(RdbStateType)][RdbVertexType.Empty].inc(false)
if GetVtxFlag.PeekCache notin flags:
rdb.rdEmptyLru.put(rvid.vid, default(tuple[]))

return ok(VertexRef(nil))

if res.isErr():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ proc init*(rdb: var RdbInst, opts: DbOptions, baseDb: RocksDbInstanceRef) =
rdb.rdKeyLru = typeof(rdb.rdKeyLru).init(rdb.rdKeySize)
rdb.rdVtxLru = typeof(rdb.rdVtxLru).init(rdb.rdVtxSize)
rdb.rdBranchLru = typeof(rdb.rdBranchLru).init(rdb.rdBranchSize)
rdb.rdEmptyLru = typeof(rdb.rdEmptyLru).init(rdb.rdVtxSize) # reuse vtx cache size

if opts.rdbPrintStats:
let
Expand Down
5 changes: 5 additions & 0 deletions execution_chain/db/aristo/aristo_init/rocks_db/rdb_put.nim
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ proc putVtx*(
else:
discard rdb.rdVtxLru.update(rvid.vid, vtx)

rdb.rdEmptyLru.del(rvid.vid)

if key.isValid:
if rdb.rdKeyLru.len < rdb.rdKeyLru.capacity:
rdb.rdKeyLru.put(rvid.vid, key)
Expand All @@ -126,6 +128,9 @@ proc putVtx*(
rdb.rdVtxLru.del rvid.vid
rdb.rdKeyLru.del rvid.vid

if rdb.rdEmptyLru.len < rdb.rdEmptyLru.capacity:
rdb.rdEmptyLru.put(rvid.vid, default(tuple[]))

ok()

# ------------------------------------------------------------------------------
Expand Down
23 changes: 0 additions & 23 deletions execution_chain/db/aristo/aristo_layers.nim
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,6 @@ func layersGetKeyOrVoid*(db: AristoTxRef; rvid: RootedVertexID): HashKey =
## Simplified version of `layersGetKey()`
(db.layersGetKey(rvid).valueOr (VOID_HASH_KEY, 0))[0]

func layersGetAccLeaf*(db: AristoTxRef; accPath: Hash32): Opt[AccLeafRef] =
for w in db.rstack(stopAtSnapshot = true):
if w.snapshot.level.isSome():
w.snapshot.acc.withValue(accPath, item):
return Opt.some(item[][0])
break

w.accLeaves.withValue(accPath, item):
return Opt.some(item[])

Opt.none(AccLeafRef)

func layersGetStoLeaf*(db: AristoTxRef; mixPath: Hash32): Opt[StoLeafRef] =
for w in db.rstack(stopAtSnapshot = true):
if w.snapshot.level.isSome():
Expand Down Expand Up @@ -129,12 +117,6 @@ func layersResKeys*(db: AristoTxRef; hike: Hike, skip: int) =
for i in (skip + 1)..hike.legs.len:
db.layersResKey((hike.root, hike.legs[^i].wp.vid), hike.legs[^i].wp.vtx)

func layersPutAccLeaf*(db: AristoTxRef; accPath: Hash32; leafVtx: AccLeafRef) =
db.accLeaves[accPath] = leafVtx

if db.snapshot.level.isSome():
db.snapshot.acc[accPath] = (leafVtx, db.level)

func layersPutStoLeaf*(db: AristoTxRef; mixPath: Hash32; leafVtx: StoLeafRef) =
db.stoLeaves[mixPath] = leafVtx

Expand All @@ -149,11 +131,9 @@ func isEmpty*(ly: AristoTxRef): bool =
## Returns `true` if the layer does not contain any changes, i.e. all the
## tables are empty.
ly.snapshot.vtx.len == 0 and
ly.snapshot.acc.len == 0 and
ly.snapshot.sto.len == 0 and
ly.sTab.len == 0 and
ly.kMap.len == 0 and
ly.accLeaves.len == 0 and
ly.stoLeaves.len == 0

proc copyFrom*(snapshot: var Snapshot, tx: AristoTxRef) =
Expand All @@ -163,8 +143,6 @@ proc copyFrom*(snapshot: var Snapshot, tx: AristoTxRef) =
do:
snapshot.vtx[rvid] = (vtx, VOID_HASH_KEY, tx.level)

for k, v in tx.accLeaves:
snapshot.acc[k] = (v, tx.level)
for k, v in tx.stoLeaves:
snapshot.sto[k] = (v, tx.level)

Expand Down Expand Up @@ -195,7 +173,6 @@ proc mergeAndReset*(trg, src: AristoTxRef) =
mergeAndReset(trg.sTab, src.sTab)
mergeAndReset(trg.kMap, src.kMap)

mergeAndReset(trg.accLeaves, src.accLeaves)
mergeAndReset(trg.stoLeaves, src.stoLeaves)

# ------------------------------------------------------------------------------
Expand Down
13 changes: 1 addition & 12 deletions execution_chain/db/aristo/aristo_merge.nim
Original file line number Diff line number Diff line change
Expand Up @@ -216,21 +216,11 @@ proc mergeAccountRecord*(
## not on the database already or different from `accRec`, and `false`
## otherwise.
##
let updated = db.mergePayloadImpl(
STATE_ROOT_VID, accPath, db.cachedAccLeaf(accPath), accRec
).valueOr:
discard db.mergePayloadImpl(STATE_ROOT_VID, accPath, Opt.none(AccLeafRef), accRec).valueOr:
if error == MergeNoAction:
return ok false
return err(error)

# Update leaf cache both of the merged value and potentially the displaced
# leaf resulting from splitting a leaf into a branch with two leaves
db.layersPutAccLeaf(accPath, updated[0])
if updated[1].isValid:
let otherPath =
Hash32(getBytes(NibblesBuf.fromBytes(accPath.data).replaceSuffix(updated[1].pfx)))
db.layersPutAccLeaf(otherPath, updated[2])

ok true

proc mergeStorageData*(
Expand Down Expand Up @@ -282,7 +272,6 @@ proc mergeStorageData*(
# Make sure that there is an account that refers to that storage trie
let leaf = AccLeafRef(accHike.legs[^1].wp.vtx).dup # Dup on modify
leaf.stoID = useID
db.layersPutAccLeaf(accPath, leaf)
db.layersPutVtx((STATE_ROOT_VID, accHike.legs[^1].wp.vid), leaf)

ok()
Expand Down
20 changes: 1 addition & 19 deletions execution_chain/db/aristo/aristo_tx_frame.nim
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,13 @@ proc buildSnapshot(txFrame: AristoTxRef, minLevel: int) =
tbl.del(k)

txFrame.snapshot.vtx.delIfIt(it[2] < minLevel)
txFrame.snapshot.acc.delIfIt(it[1] < minLevel)
txFrame.snapshot.sto.delIfIt(it[1] < minLevel)

if frame.snapshot.level.isSome() and isKeyframe:
txFrame.snapshot.vtx = initTable[RootedVertexID, VtxSnapshot](
max(1024, max(frame.sTab.len, frame.snapshot.vtx.len))
)

txFrame.snapshot.acc = initTable[Hash32, (AccLeafRef, int)](
max(1024, max(frame.accLeaves.len, frame.snapshot.acc.len))
)

txFrame.snapshot.sto = initTable[Hash32, (StoLeafRef, int)](
max(1024, max(frame.stoLeaves.len, frame.snapshot.sto.len))
)
Expand All @@ -77,10 +72,6 @@ proc buildSnapshot(txFrame: AristoTxRef, minLevel: int) =
if v[2] >= minLevel:
txFrame.snapshot.vtx[k] = v

for k, v in frame.snapshot.acc:
if v[1] >= minLevel:
txFrame.snapshot.acc[k] = v

for k, v in frame.snapshot.sto:
if v[1] >= minLevel:
txFrame.snapshot.sto[k] = v
Expand Down Expand Up @@ -141,8 +132,7 @@ proc persist*(
# which caters to the scenario where changes from multiple blocks
# have already been written to sTab and the changes can moved into
# the bottom.
if (bottom.snapshot.vtx.len + bottom.snapshot.acc.len + bottom.snapshot.sto.len) ==
0:
if (bottom.snapshot.vtx.len + bottom.snapshot.sto.len) == 0:
bottom.snapshot.level.reset()
else:
# Incoming snapshots already have sTab baked in - make sure we don't
Expand Down Expand Up @@ -212,27 +202,19 @@ with --debug-eager-state-root."""
# in-memory and on-disk state)

# Copy back updated payloads
for accPath, vtx in txFrame.accLeaves:
if vtx == nil:
db.accLeaves.del(accPath)
else:
discard db.accLeaves.update(accPath, vtx)

for mixPath, vtx in txFrame.stoLeaves:
if vtx == nil:
db.stoLeaves.del(mixPath)
else:
discard db.stoLeaves.update(mixPath, vtx)

txFrame.snapshot.vtx.clear()
txFrame.snapshot.acc.clear()
txFrame.snapshot.sto.clear()
# Since txFrame is now the base, it contains all changes and therefore acts
# as a snapshot
txFrame.snapshot.level = Opt.some(txFrame.level)
txFrame.sTab.clear()
txFrame.kMap.clear()
txFrame.accLeaves.clear()
txFrame.stoLeaves.clear()
txFrame.blockNumber.reset()

Expand Down
4 changes: 2 additions & 2 deletions execution_chain/db/opts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ const
## re-reads from file.
##
## A bit of space on top of the filter is left for data block caching
defaultRdbVtxCacheSize* = 512 * 1024 * 1024
defaultRdbVtxCacheSize* = 768 * 1024 * 1024
## Cache of branches and leaves in the state MPTs (world and account)
defaultRdbKeyCacheSize* = 1280 * 1024 * 1024
## Hashes of the above
defaultRdbBranchCacheSize* = 1024 * 1024 * 1024
defaultRdbBranchCacheSize* = 1280 * 1024 * 1024
## Cache of branches and leaves in the state MPTs (world and account)


Expand Down
Loading