Jordan Hrycaj ffa8ad2246
Core db use differential tx layers for aristo and kvt (#1949)
* Fix kvt headers

* Provide differential layers for KVT transaction stack

why:
  Significant performance improvement

* Provide abstraction layer for database top cache layer

why:
  This will eventually implemented as a differential database layers
  or transaction layers. The latter is needed to improve performance.

behavioural changes:
  Zero vertex and keys (i.e. delete requests) are not optimised out
  until the last layer is written to the database.

* Provide differential layers for Aristo transaction stack

why:
  Significant performance improvement
2023-12-19 12:39:23 +00:00

174 lines
5.8 KiB
Nim

# nimbus-eth1
# Copyright (c) 2023 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.
{.push raises: [].}
import
std/[sequtils, sets],
eth/[common, trie/nibbles],
results,
".."/[aristo_desc, aristo_get, aristo_layers, aristo_serialise, aristo_utils]
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
proc checkTopStrict*(
db: AristoDbRef; # Database, top layer
): Result[void,(VertexID,AristoError)] =
# No need to specify zero keys if implied by a leaf path with valid target
# vertex ID (i.e. not deleted).
var zeroKeys: HashSet[VertexID]
for (vid,vtx) in db.layersWalkVtx:
let lbl = db.layersGetLabelOrVoid vid
if not vtx.isValid:
if lbl.isValid:
return err((vid,CheckStkVtxKeyMismatch))
else: # Empty key flags key is for update
zeroKeys.incl vid
elif lbl.isValid:
# So `vtx` and `lbl` exist
let node = vtx.toNode(db).valueOr:
return err((vid,CheckStkVtxIncomplete))
if lbl.key != node.digestTo(HashKey):
return err((vid,CheckStkVtxKeyMismatch))
let revVids = db.layersGetLebalOrVoid lbl
if not revVids.isValid:
return err((vid,CheckStkRevKeyMissing))
if vid notin revVids:
return err((vid,CheckStkRevKeyMismatch))
elif not db.dirty or db.layersGetLabel(vid).isErr:
# So `vtx` exists but not `lbl`, so cache is supposed dirty and the
# vertex has a zero entry.
return err((vid,CheckStkVtxKeyMissing))
else: # Empty key flags key is for update
zeroKeys.incl vid
for (vid,key) in db.layersWalkLabel:
if not key.isValid and vid notin zeroKeys:
if not db.getVtx(vid).isValid:
return err((vid,CheckStkKeyStrayZeroEntry))
let
pAmkVtxCount = db.layersWalkLebal.toSeq.mapIt(it[1]).foldl(a + b.len, 0)
sTabVtxCount = db.layersWalkVtx.toSeq.mapIt(it[1]).filterIt(it.isValid).len
# Non-zero values mist sum up the same
if pAmkVtxCount + zeroKeys.len < sTabVtxCount:
return err((VertexID(0),CheckStkVtxCountMismatch))
ok()
proc checkTopProofMode*(
db: AristoDbRef; # Database, top layer
): Result[void,(VertexID,AristoError)] =
if 0 < db.pPrf.len:
for vid in db.pPrf:
let vtx = db.layersGetVtxOrVoid vid
if vtx.isValid:
let node = vtx.toNode(db).valueOr:
return err((vid,CheckRlxVtxIncomplete))
let lbl = db.layersGetlabelOrVoid vid
if not lbl.isValid:
return err((vid,CheckRlxVtxKeyMissing))
if lbl.key != node.digestTo(HashKey):
return err((vid,CheckRlxVtxKeyMismatch))
let revVids = db.layersGetLebalOrVoid lbl
if not revVids.isValid:
return err((vid,CheckRlxRevKeyMissing))
if vid notin revVids:
return err((vid,CheckRlxRevKeyMismatch))
else:
for (vid,lbl) in db.layersWalkLabel:
if lbl.isValid: # Otherwise to be deleted
let vtx = db.getVtx vid
if vtx.isValid:
let node = vtx.toNode(db).valueOr:
continue
if lbl.key != node.digestTo(HashKey):
return err((vid,CheckRlxVtxKeyMismatch))
let revVids = db.layersGetLebalOrVoid lbl
if not revVids.isValid:
return err((vid,CheckRlxRevKeyMissing))
if vid notin revVids:
return err((vid,CheckRlxRevKeyMismatch))
ok()
proc checkTopCommon*(
db: AristoDbRef; # Database, top layer
): Result[void,(VertexID,AristoError)] =
# Some `kMap[]` entries may ne void indicating backend deletion
let
kMapCount = db.layersWalkLabel.toSeq.mapIt(it[1]).filterIt(it.isValid).len
kMapNilCount = db.layersWalkLabel.toSeq.len - kMapCount
# Collect leafs and check deleted entries
var nNilVtx = 0
for (vid,vtx) in db.layersWalkVtx:
if vtx.isValid:
case vtx.vType:
of Leaf:
discard
of Branch:
block check42Links:
var seen = false
for n in 0 .. 15:
if vtx.bVid[n].isValid:
if seen:
break check42Links
seen = true
return err((vid,CheckAnyVtxBranchLinksMissing))
of Extension:
if vtx.ePfx.len == 0:
return err((vid,CheckAnyVtxExtPfxMissing))
else:
nNilVtx.inc
let rc = db.layersGetLabel vid
if rc.isErr:
return err((vid,CheckAnyVtxEmptyKeyMissing))
if rc.value.isValid:
return err((vid,CheckAnyVtxEmptyKeyExpected))
# If present, there are at least as many deleted hashes as there are deleted
# vertices.
if kMapNilCount != 0 and kMapNilCount < nNilVtx:
return err((VertexID(0),CheckAnyVtxEmptyKeyMismatch))
let pAmkVtxCount = db.layersWalkLebal.toSeq.mapIt(it[1]).foldl(a + b.len, 0)
if pAmkVtxCount != kMapCount:
var knownKeys: HashSet[VertexID]
for (key,vids) in db.layersWalkLebal:
for vid in vids:
if db.layersGetLabel(vid).isErr:
return err((vid,CheckAnyRevVtxMissing))
if vid in knownKeys:
return err((vid,CheckAnyRevVtxDup))
knownKeys.incl vid
return err((VertexID(0),CheckAnyRevCountMismatch)) # should not apply(!)
for vid in db.pPrf:
if db.layersGetLabel(vid).isErr:
return err((vid,CheckAnyVtxLockWithoutKey))
ok()
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------