diff --git a/execution_chain/db/aristo/aristo_constants.nim b/execution_chain/db/aristo/aristo_constants.nim index efcc669436..e5225678ab 100644 --- a/execution_chain/db/aristo/aristo_constants.nim +++ b/execution_chain/db/aristo/aristo_constants.nim @@ -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 diff --git a/execution_chain/db/aristo/aristo_delete.nim b/execution_chain/db/aristo/aristo_delete.nim index 06eaa2a3ea..05fb294e81 100644 --- a/execution_chain/db/aristo/aristo_delete.nim +++ b/execution_chain/db/aristo/aristo_delete.nim @@ -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() @@ -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() @@ -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() diff --git a/execution_chain/db/aristo/aristo_desc.nim b/execution_chain/db/aristo/aristo_desc.nim index c27ff3ad47..a852e2f175 100644 --- a/execution_chain/db/aristo/aristo_desc.nim +++ b/execution_chain/db/aristo/aristo_desc.nim @@ -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 @@ -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 @@ -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 diff --git a/execution_chain/db/aristo/aristo_fetch.nim b/execution_chain/db/aristo/aristo_fetch.nim index 82ead79a90..dd8028e785 100644 --- a/execution_chain/db/aristo/aristo_fetch.nim +++ b/execution_chain/db/aristo/aristo_fetch.nim @@ -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 @@ -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, @@ -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( @@ -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) diff --git a/execution_chain/db/aristo/aristo_init/init_common.nim b/execution_chain/db/aristo/aristo_init/init_common.nim index 83ca547a37..11b8fa2f9e 100644 --- a/execution_chain/db/aristo/aristo_init/init_common.nim +++ b/execution_chain/db/aristo/aristo_init/init_common.nim @@ -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) = diff --git a/execution_chain/db/aristo/aristo_init/rocks_db/rdb_desc.nim b/execution_chain/db/aristo/aristo_init/rocks_db/rdb_desc.nim index 26b41983a4..f9945bc00e 100644 --- a/execution_chain/db/aristo/aristo_init/rocks_db/rdb_desc.nim +++ b/execution_chain/db/aristo/aristo_init/rocks_db/rdb_desc.nim @@ -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 diff --git a/execution_chain/db/aristo/aristo_init/rocks_db/rdb_get.nim b/execution_chain/db/aristo/aristo_init/rocks_db/rdb_get.nim index 15ec67e5a2..39cecfc32c 100644 --- a/execution_chain/db/aristo/aristo_init/rocks_db/rdb_get.nim +++ b/execution_chain/db/aristo/aristo_init/rocks_db/rdb_get.nim @@ -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] @@ -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(): diff --git a/execution_chain/db/aristo/aristo_init/rocks_db/rdb_init.nim b/execution_chain/db/aristo/aristo_init/rocks_db/rdb_init.nim index b875846d58..1ccafba2df 100644 --- a/execution_chain/db/aristo/aristo_init/rocks_db/rdb_init.nim +++ b/execution_chain/db/aristo/aristo_init/rocks_db/rdb_init.nim @@ -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 diff --git a/execution_chain/db/aristo/aristo_init/rocks_db/rdb_put.nim b/execution_chain/db/aristo/aristo_init/rocks_db/rdb_put.nim index 1a5ee71bd0..c342b5a8b3 100644 --- a/execution_chain/db/aristo/aristo_init/rocks_db/rdb_put.nim +++ b/execution_chain/db/aristo/aristo_init/rocks_db/rdb_put.nim @@ -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) @@ -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() # ------------------------------------------------------------------------------ diff --git a/execution_chain/db/aristo/aristo_layers.nim b/execution_chain/db/aristo/aristo_layers.nim index c5f01af5be..34afd43f8c 100644 --- a/execution_chain/db/aristo/aristo_layers.nim +++ b/execution_chain/db/aristo/aristo_layers.nim @@ -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(): @@ -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 @@ -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) = @@ -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) @@ -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) # ------------------------------------------------------------------------------ diff --git a/execution_chain/db/aristo/aristo_merge.nim b/execution_chain/db/aristo/aristo_merge.nim index 16a015d9bf..3a0aaa8b7a 100644 --- a/execution_chain/db/aristo/aristo_merge.nim +++ b/execution_chain/db/aristo/aristo_merge.nim @@ -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*( @@ -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() diff --git a/execution_chain/db/aristo/aristo_tx_frame.nim b/execution_chain/db/aristo/aristo_tx_frame.nim index a9f87fbe30..5dd1c9e776 100644 --- a/execution_chain/db/aristo/aristo_tx_frame.nim +++ b/execution_chain/db/aristo/aristo_tx_frame.nim @@ -57,7 +57,6 @@ 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: @@ -65,10 +64,6 @@ proc buildSnapshot(txFrame: AristoTxRef, minLevel: int) = 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)) ) @@ -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 @@ -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 @@ -212,12 +202,6 @@ 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) @@ -225,14 +209,12 @@ with --debug-eager-state-root.""" 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() diff --git a/execution_chain/db/opts.nim b/execution_chain/db/opts.nim index 0f50851e70..ae223ca7e4 100644 --- a/execution_chain/db/opts.nim +++ b/execution_chain/db/opts.nim @@ -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)