cache account payload (#2478)
Instead of caching just the storage id, we can cache the full payload which further reduces expensive hikes
This commit is contained in:
parent
d07540766f
commit
01ab209497
|
@ -340,11 +340,12 @@ proc deleteAccountRecord*(
|
||||||
# Delete storage tree if present
|
# Delete storage tree if present
|
||||||
if stoID.isValid:
|
if stoID.isValid:
|
||||||
? db.delSubTreeImpl stoID
|
? db.delSubTreeImpl stoID
|
||||||
db.layersPutStoID(accPath, VertexID(0))
|
|
||||||
|
|
||||||
db.deleteImpl(hike).isOkOr:
|
db.deleteImpl(hike).isOkOr:
|
||||||
return err(error[1])
|
return err(error[1])
|
||||||
|
|
||||||
|
db.layersPutAccPayload(accPath, nil)
|
||||||
|
|
||||||
ok()
|
ok()
|
||||||
|
|
||||||
|
|
||||||
|
@ -437,7 +438,7 @@ proc deleteStorageData*(
|
||||||
# De-register the deleted storage tree from the account record
|
# De-register the deleted storage tree from the account record
|
||||||
let leaf = wpAcc.vtx.dup # Dup on modify
|
let leaf = wpAcc.vtx.dup # Dup on modify
|
||||||
leaf.lData.stoID = VertexID(0)
|
leaf.lData.stoID = VertexID(0)
|
||||||
db.layersPutStoID(accPath, VertexID(0))
|
db.layersPutAccPayload(accPath, leaf.lData)
|
||||||
db.layersPutVtx((accHike.root, wpAcc.vid), leaf)
|
db.layersPutVtx((accHike.root, wpAcc.vid), leaf)
|
||||||
db.layersResKey((accHike.root, wpAcc.vid))
|
db.layersResKey((accHike.root, wpAcc.vid))
|
||||||
ok(true)
|
ok(true)
|
||||||
|
@ -468,7 +469,7 @@ proc deleteStorageTree*(
|
||||||
# De-register the deleted storage tree from the accounts record
|
# De-register the deleted storage tree from the accounts record
|
||||||
let leaf = wpAcc.vtx.dup # Dup on modify
|
let leaf = wpAcc.vtx.dup # Dup on modify
|
||||||
leaf.lData.stoID = VertexID(0)
|
leaf.lData.stoID = VertexID(0)
|
||||||
db.layersPutStoID(accPath, VertexID(0))
|
db.layersPutAccPayload(accPath, leaf.lData)
|
||||||
db.layersPutVtx((accHike.root, wpAcc.vid), leaf)
|
db.layersPutVtx((accHike.root, wpAcc.vid), leaf)
|
||||||
db.layersResKey((accHike.root, wpAcc.vid))
|
db.layersResKey((accHike.root, wpAcc.vid))
|
||||||
ok()
|
ok()
|
||||||
|
|
|
@ -82,15 +82,11 @@ proc deltaPersistent*(
|
||||||
be.putLstFn(writeBatch, lSst)
|
be.putLstFn(writeBatch, lSst)
|
||||||
? be.putEndFn writeBatch # Finalise write batch
|
? be.putEndFn writeBatch # Finalise write batch
|
||||||
|
|
||||||
# Copy back updated payloads - these only change when storage is written to
|
# Copy back updated payloads
|
||||||
# the first time or when storage is removed completely
|
for accPath, pyl in db.balancer.accPyls:
|
||||||
for accPath, stoID in db.balancer.accSids:
|
|
||||||
let accKey = accPath.to(AccountKey)
|
let accKey = accPath.to(AccountKey)
|
||||||
if stoID.isValid:
|
if not db.accPyls.lruUpdate(accKey, pyl):
|
||||||
if not db.accSids.lruUpdate(accKey, stoID):
|
discard db.accPyls.lruAppend(accKey, pyl, accLruSize)
|
||||||
discard db.accSids.lruAppend(accKey, stoID, accLruSize)
|
|
||||||
else:
|
|
||||||
db.accSids.del(accKey)
|
|
||||||
|
|
||||||
# Update dudes and this descriptor
|
# Update dudes and this descriptor
|
||||||
? updateSiblings.update().commit()
|
? updateSiblings.update().commit()
|
||||||
|
|
|
@ -84,12 +84,13 @@ type
|
||||||
# Debugging data below, might go away in future
|
# Debugging data below, might go away in future
|
||||||
xMap*: Table[HashKey,HashSet[RootedVertexID]] ## For pretty printing/debugging
|
xMap*: Table[HashKey,HashSet[RootedVertexID]] ## For pretty printing/debugging
|
||||||
|
|
||||||
accSids*: KeyedQueue[AccountKey, VertexID]
|
accPyls*: KeyedQueue[AccountKey, PayloadRef]
|
||||||
## Account path to storage id cache, for contract accounts - storage is
|
## Account path to payload cache - accounts are frequently accessed by
|
||||||
## frequently accessed by account path when contracts interact with it -
|
## account path when contracts interact with them - this cache ensures
|
||||||
## this cache ensures that we don't have to re-travers the storage trie
|
## that we don't have to re-traverse the storage trie for every such
|
||||||
## path for every such interaction - a better solution would probably be
|
## interaction
|
||||||
## to cache this in a type exposed to the high-level API
|
## TODO a better solution would probably be to cache this in a type
|
||||||
|
## exposed to the high-level API
|
||||||
|
|
||||||
AristoDbAction* = proc(db: AristoDbRef) {.gcsafe, raises: [].}
|
AristoDbAction* = proc(db: AristoDbRef) {.gcsafe, raises: [].}
|
||||||
## Generic call back function/closure.
|
## Generic call back function/closure.
|
||||||
|
|
|
@ -115,7 +115,7 @@ type
|
||||||
kMap*: Table[RootedVertexID,HashKey] ## Merkle hash key mapping
|
kMap*: Table[RootedVertexID,HashKey] ## Merkle hash key mapping
|
||||||
vTop*: VertexID ## Last used vertex ID
|
vTop*: VertexID ## Last used vertex ID
|
||||||
|
|
||||||
accSids*: Table[Hash256, VertexID] ## Account path -> stoID
|
accPyls*: Table[Hash256, PayloadRef] ## Account path -> VertexRef
|
||||||
|
|
||||||
LayerRef* = ref LayerObj
|
LayerRef* = ref LayerObj
|
||||||
LayerObj* = object
|
LayerObj* = object
|
||||||
|
|
|
@ -55,6 +55,32 @@ proc retrievePayload(
|
||||||
|
|
||||||
return err(FetchPathNotFound)
|
return err(FetchPathNotFound)
|
||||||
|
|
||||||
|
proc retrieveAccountPayload(
|
||||||
|
db: AristoDbRef;
|
||||||
|
accPath: Hash256;
|
||||||
|
): Result[PayloadRef,AristoError] =
|
||||||
|
if (let pyl = db.layersGetAccPayload(accPath); pyl.isSome()):
|
||||||
|
if not pyl[].isValid():
|
||||||
|
return err(FetchPathNotFound)
|
||||||
|
return ok pyl[]
|
||||||
|
|
||||||
|
let accKey = accPath.to(AccountKey)
|
||||||
|
if (let pyl = db.accPyls.lruFetch(accKey); pyl.isSome()):
|
||||||
|
if not pyl[].isValid():
|
||||||
|
return err(FetchPathNotFound)
|
||||||
|
return ok pyl[]
|
||||||
|
|
||||||
|
# Updated payloads are stored in the layers so if we didn't find them there,
|
||||||
|
# it must have been in the database
|
||||||
|
let
|
||||||
|
payload = db.retrievePayload(VertexID(1), accPath.data).valueOr:
|
||||||
|
if error == FetchAccInaccessible:
|
||||||
|
discard db.accPyls.lruAppend(accKey, nil, accLruSize)
|
||||||
|
return err(FetchPathNotFound)
|
||||||
|
return err(error)
|
||||||
|
|
||||||
|
ok db.accPyls.lruAppend(accKey, payload, accLruSize)
|
||||||
|
|
||||||
proc retrieveMerkleHash(
|
proc retrieveMerkleHash(
|
||||||
db: AristoDbRef;
|
db: AristoDbRef;
|
||||||
root: VertexID;
|
root: VertexID;
|
||||||
|
@ -79,9 +105,6 @@ proc hasPayload(
|
||||||
root: VertexID;
|
root: VertexID;
|
||||||
path: openArray[byte];
|
path: openArray[byte];
|
||||||
): Result[bool,AristoError] =
|
): Result[bool,AristoError] =
|
||||||
if path.len == 0:
|
|
||||||
return err(FetchPathInvalid)
|
|
||||||
|
|
||||||
let error = db.retrievePayload(root, path).errorOr:
|
let error = db.retrievePayload(root, path).errorOr:
|
||||||
return ok(true)
|
return ok(true)
|
||||||
|
|
||||||
|
@ -89,6 +112,17 @@ proc hasPayload(
|
||||||
return ok(false)
|
return ok(false)
|
||||||
err(error)
|
err(error)
|
||||||
|
|
||||||
|
proc hasAccountPayload(
|
||||||
|
db: AristoDbRef;
|
||||||
|
accPath: Hash256;
|
||||||
|
): Result[bool,AristoError] =
|
||||||
|
let error = db.retrieveAccountPayload(accPath).errorOr:
|
||||||
|
return ok(true)
|
||||||
|
|
||||||
|
if error == FetchPathNotFound:
|
||||||
|
return ok(false)
|
||||||
|
err(error)
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Public helpers
|
# Public helpers
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
@ -120,20 +154,10 @@ proc fetchStorageID*(
|
||||||
db: AristoDbRef;
|
db: AristoDbRef;
|
||||||
accPath: Hash256;
|
accPath: Hash256;
|
||||||
): Result[VertexID,AristoError] =
|
): Result[VertexID,AristoError] =
|
||||||
## Public helper function fro retrieving a storage (vertex) ID for a
|
## Public helper function for retrieving a storage (vertex) ID for a
|
||||||
## given account.
|
## given account.
|
||||||
|
|
||||||
if (let stoID = db.layersGetStoID(accPath); stoID.isSome()):
|
|
||||||
if not stoID[].isValid():
|
|
||||||
return err(FetchPathNotFound)
|
|
||||||
return ok stoID[]
|
|
||||||
|
|
||||||
let accKey = accPath.to(AccountKey)
|
|
||||||
if (let stoID = db.accSids.lruFetch(accKey); stoID.isSome()):
|
|
||||||
return ok stoID[]
|
|
||||||
|
|
||||||
let
|
let
|
||||||
payload = db.retrievePayload(VertexID(1), accPath.data).valueOr:
|
payload = db.retrieveAccountPayload(accPath).valueOr:
|
||||||
if error == FetchAccInaccessible:
|
if error == FetchAccInaccessible:
|
||||||
return err(FetchPathNotFound)
|
return err(FetchPathNotFound)
|
||||||
return err(error)
|
return err(error)
|
||||||
|
@ -143,9 +167,8 @@ proc fetchStorageID*(
|
||||||
if not stoID.isValid:
|
if not stoID.isValid:
|
||||||
return err(FetchPathNotFound)
|
return err(FetchPathNotFound)
|
||||||
|
|
||||||
# If we didn't find a cached storage ID in the layers, we must be using the
|
ok stoID
|
||||||
# database version which we cache here, even across database commits
|
|
||||||
ok db.accSids.lruAppend(accKey, stoID, accLruSize)
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Public functions
|
# Public functions
|
||||||
|
@ -159,15 +182,15 @@ proc fetchLastSavedState*(
|
||||||
## `uint64` identifier (may be interpreted as block number.)
|
## `uint64` identifier (may be interpreted as block number.)
|
||||||
db.getLstUbe()
|
db.getLstUbe()
|
||||||
|
|
||||||
|
|
||||||
proc fetchAccountRecord*(
|
proc fetchAccountRecord*(
|
||||||
db: AristoDbRef;
|
db: AristoDbRef;
|
||||||
accPath: Hash256;
|
accPath: Hash256;
|
||||||
): Result[AristoAccount,AristoError] =
|
): Result[AristoAccount,AristoError] =
|
||||||
## Fetch an account record from the database indexed by `accPath`.
|
## Fetch an account record from the database indexed by `accPath`.
|
||||||
##
|
##
|
||||||
let pyl = ? db.retrievePayload(VertexID(1), accPath.data)
|
let pyl = ? db.retrieveAccountPayload(accPath)
|
||||||
assert pyl.pType == AccountData # debugging only
|
assert pyl.pType == AccountData # debugging only
|
||||||
|
|
||||||
ok pyl.account
|
ok pyl.account
|
||||||
|
|
||||||
proc fetchAccountState*(
|
proc fetchAccountState*(
|
||||||
|
@ -184,8 +207,7 @@ proc hasPathAccount*(
|
||||||
## For an account record indexed by `accPath` query whether this record exists
|
## For an account record indexed by `accPath` query whether this record exists
|
||||||
## on the database.
|
## on the database.
|
||||||
##
|
##
|
||||||
db.hasPayload(VertexID(1), accPath.data)
|
db.hasAccountPayload(accPath)
|
||||||
|
|
||||||
|
|
||||||
proc fetchGenericData*(
|
proc fetchGenericData*(
|
||||||
db: AristoDbRef;
|
db: AristoDbRef;
|
||||||
|
@ -219,7 +241,6 @@ proc hasPathGeneric*(
|
||||||
? root.mustBeGeneric()
|
? root.mustBeGeneric()
|
||||||
db.hasPayload(root, path)
|
db.hasPayload(root, path)
|
||||||
|
|
||||||
|
|
||||||
proc fetchStorageData*(
|
proc fetchStorageData*(
|
||||||
db: AristoDbRef;
|
db: AristoDbRef;
|
||||||
accPath: Hash256;
|
accPath: Hash256;
|
||||||
|
|
|
@ -91,15 +91,16 @@ func layersGetKeyOrVoid*(db: AristoDbRef; rvid: RootedVertexID): HashKey =
|
||||||
## Simplified version of `layersGetKey()`
|
## Simplified version of `layersGetKey()`
|
||||||
db.layersGetKey(rvid).valueOr: VOID_HASH_KEY
|
db.layersGetKey(rvid).valueOr: VOID_HASH_KEY
|
||||||
|
|
||||||
func layersGetStoID*(db: AristoDbRef; accPath: Hash256): Opt[VertexID] =
|
func layersGetAccPayload*(db: AristoDbRef; accPath: Hash256): Opt[PayloadRef] =
|
||||||
db.top.delta.accSids.withValue(accPath, item):
|
db.top.delta.accPyls.withValue(accPath, item):
|
||||||
return Opt.some(item[])
|
return Opt.some(item[])
|
||||||
|
|
||||||
for w in db.rstack:
|
for w in db.rstack:
|
||||||
w.delta.accSids.withValue(accPath, item):
|
w.delta.accPyls.withValue(accPath, item):
|
||||||
return Opt.some(item[])
|
return Opt.some(item[])
|
||||||
|
|
||||||
Opt.none(VertexID)
|
Opt.none(PayloadRef)
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Public functions: setter variants
|
# Public functions: setter variants
|
||||||
|
@ -146,8 +147,8 @@ proc layersUpdateVtx*(
|
||||||
db.layersResKey(rvid)
|
db.layersResKey(rvid)
|
||||||
|
|
||||||
|
|
||||||
func layersPutStoID*(db: AristoDbRef; accPath: Hash256; stoID: VertexID) =
|
func layersPutAccPayload*(db: AristoDbRef; accPath: Hash256; pyl: PayloadRef) =
|
||||||
db.top.delta.accSids[accPath] = stoID
|
db.top.delta.accPyls[accPath] = pyl
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Public functions
|
# Public functions
|
||||||
|
@ -164,8 +165,8 @@ func layersMergeOnto*(src: LayerRef; trg: var LayerObj) =
|
||||||
for (vid,key) in src.delta.kMap.pairs:
|
for (vid,key) in src.delta.kMap.pairs:
|
||||||
trg.delta.kMap[vid] = key
|
trg.delta.kMap[vid] = key
|
||||||
trg.delta.vTop = src.delta.vTop
|
trg.delta.vTop = src.delta.vTop
|
||||||
for (accPath,stoID) in src.delta.accSids.pairs:
|
for (accPath,pyl) in src.delta.accPyls.pairs:
|
||||||
trg.delta.accSids[accPath] = stoID
|
trg.delta.accPyls[accPath] = pyl
|
||||||
|
|
||||||
func layersCc*(db: AristoDbRef; level = high(int)): LayerRef =
|
func layersCc*(db: AristoDbRef; level = high(int)): LayerRef =
|
||||||
## Provide a collapsed copy of layers up to a particular transaction level.
|
## Provide a collapsed copy of layers up to a particular transaction level.
|
||||||
|
@ -181,7 +182,7 @@ func layersCc*(db: AristoDbRef; level = high(int)): LayerRef =
|
||||||
sTab: layers[0].delta.sTab.dup, # explicit dup for ref values
|
sTab: layers[0].delta.sTab.dup, # explicit dup for ref values
|
||||||
kMap: layers[0].delta.kMap,
|
kMap: layers[0].delta.kMap,
|
||||||
vTop: layers[^1].delta.vTop,
|
vTop: layers[^1].delta.vTop,
|
||||||
accSids: layers[0].delta.accSids,
|
accPyls: layers[0].delta.accPyls,
|
||||||
))
|
))
|
||||||
|
|
||||||
# Consecutively merge other layers on top
|
# Consecutively merge other layers on top
|
||||||
|
@ -190,8 +191,8 @@ func layersCc*(db: AristoDbRef; level = high(int)): LayerRef =
|
||||||
result.delta.sTab[vid] = vtx
|
result.delta.sTab[vid] = vtx
|
||||||
for (vid,key) in layers[n].delta.kMap.pairs:
|
for (vid,key) in layers[n].delta.kMap.pairs:
|
||||||
result.delta.kMap[vid] = key
|
result.delta.kMap[vid] = key
|
||||||
for (accPath,stoID) in layers[n].delta.accSids.pairs:
|
for (accPath,pyl) in layers[n].delta.accPyls.pairs:
|
||||||
result.delta.accSids[accPath] = stoID
|
result.delta.accPyls[accPath] = pyl
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Public iterators
|
# Public iterators
|
||||||
|
|
|
@ -28,7 +28,7 @@ import
|
||||||
std/typetraits,
|
std/typetraits,
|
||||||
eth/common,
|
eth/common,
|
||||||
results,
|
results,
|
||||||
"."/[aristo_desc, aristo_hike, aristo_layers, aristo_utils, aristo_vid],
|
"."/[aristo_desc, aristo_hike, aristo_layers, aristo_vid],
|
||||||
./aristo_merge/merge_payload_helper
|
./aristo_merge/merge_payload_helper
|
||||||
|
|
||||||
const
|
const
|
||||||
|
@ -58,6 +58,7 @@ proc mergeAccountRecord*(
|
||||||
pyl = PayloadRef(pType: AccountData, account: accRec)
|
pyl = PayloadRef(pType: AccountData, account: accRec)
|
||||||
rc = db.mergePayloadImpl(VertexID(1), accPath.data, pyl)
|
rc = db.mergePayloadImpl(VertexID(1), accPath.data, pyl)
|
||||||
if rc.isOk:
|
if rc.isOk:
|
||||||
|
db.layersPutAccPayload(accPath, pyl)
|
||||||
ok true
|
ok true
|
||||||
elif rc.error in MergeNoAction:
|
elif rc.error in MergeNoAction:
|
||||||
ok false
|
ok false
|
||||||
|
@ -140,15 +141,14 @@ proc mergeStorageData*(
|
||||||
# Mark account path Merkle keys for update
|
# Mark account path Merkle keys for update
|
||||||
resetKeys()
|
resetKeys()
|
||||||
|
|
||||||
if stoID.isValid:
|
|
||||||
return ok()
|
|
||||||
|
|
||||||
else:
|
if not stoID.isValid:
|
||||||
# Make sure that there is an account that refers to that storage trie
|
# Make sure that there is an account that refers to that storage trie
|
||||||
let leaf = vtx.dup # Dup on modify
|
let leaf = vtx.dup # Dup on modify
|
||||||
leaf.lData.stoID = useID
|
leaf.lData.stoID = useID
|
||||||
db.layersPutStoID(accPath, useID)
|
db.layersPutAccPayload(accPath, leaf.lData)
|
||||||
db.layersPutVtx((VertexID(1), touched[pos - 1]), leaf)
|
db.layersPutVtx((VertexID(1), touched[pos - 1]), leaf)
|
||||||
|
|
||||||
return ok()
|
return ok()
|
||||||
|
|
||||||
elif rc.error in MergeNoAction:
|
elif rc.error in MergeNoAction:
|
||||||
|
|
Loading…
Reference in New Issue