Jacek Sieka 81e75622cf
storage: store root id together with vid, for better locality of refe… (#2449)
The state and account MPT:s currenty share key space in the database
based on that vertex id:s are assigned essentially randomly, which means
that when two adjacent slot values from the same contract are accessed,
they might reside at large distance from each other.

Here, we prefix each vertex id by its root causing them to be sorted
together thus bringing all data belonging to a particular contract
closer together - the same effect also happens for the main state MPT
whose nodes now end up clustered together more tightly.

In the future, the prefix given to the storage keys can also be used to
perform range operations such as reading all the storage at once and/or
deleting an account with a batch operation.

Notably, parts of the API already supported this rooting concept while
parts didn't - this PR makes the API consistent by always working with a
root+vid.
2024-07-04 15:46:52 +02:00

82 lines
2.7 KiB
Nim

# nimbus-eth1
# Copyright (c) 2023-2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
# http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
# http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed
# except according to those terms.
import
std/tables,
eth/common,
results,
".."/[aristo_desc, aristo_get]
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
proc deltaMerge*(
db: AristoDbRef;
upper: LayerDeltaRef; # Src filter, `nil` is ok
lower: LayerDeltaRef; # Trg filter, `nil` is ok
beStateRoot: HashKey; # Merkle hash key
): Result[LayerDeltaRef,(VertexID,AristoError)] =
## Merge argument `upper` into the `lower` filter instance.
##
## Note that the namimg `upper` and `lower` indicate that the filters are
## stacked and the database access is `upper -> lower -> backend` whereas
## the `src/trg` matching logic goes the other way round.
##
# Degenerate case: `upper` is void
if lower.isNil:
if upper.isNil:
# Even more degenerate case when both filters are void
return ok LayerDeltaRef(nil)
return ok(upper)
# Degenerate case: `upper` is non-trivial and `lower` is void
if upper.isNil:
return ok(lower)
# Verify stackability
let lowerTrg = lower.kMap.getOrVoid (VertexID(1), VertexID(1))
# There is no need to deep copy table vertices as they will not be modified.
let newFilter = LayerDeltaRef(
sTab: lower.sTab,
kMap: lower.kMap,
vTop: upper.vTop)
for (rvid,vtx) in upper.sTab.pairs:
if vtx.isValid or not newFilter.sTab.hasKey rvid:
newFilter.sTab[rvid] = vtx
elif newFilter.sTab.getOrVoid(rvid).isValid:
let rc = db.getVtxUbe rvid
if rc.isOk:
newFilter.sTab[rvid] = vtx # VertexRef(nil)
elif rc.error == GetVtxNotFound:
newFilter.sTab.del rvid
else:
return err((rvid.vid,rc.error))
for (rvid,key) in upper.kMap.pairs:
if key.isValid or not newFilter.kMap.hasKey rvid:
newFilter.kMap[rvid] = key
elif newFilter.kMap.getOrVoid(rvid).isValid:
let rc = db.getKeyUbe rvid
if rc.isOk:
newFilter.kMap[rvid] = key
elif rc.error == GetKeyNotFound:
newFilter.kMap.del rvid
else:
return err((rvid.vid,rc.error))
ok newFilter
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------