Core db maintenance update (#2087)

* CoreDb+Aristo: Fix handler code

* Aristo+Kvt: Remove cruft

* Aristo+Kvt: The function `forkTop()` always provides a single transaction

why:
  Previously it provided a single squashed tx only if there were any. Now
  it will provide a blind one if there were none.

* Fix Copyright header
This commit is contained in:
Jordan Hrycaj 2024-03-20 15:15:56 +00:00 committed by GitHub
parent c41206be39
commit 99238ce0e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 53 additions and 117 deletions

View File

@ -118,53 +118,13 @@ type
## A non centre descriptor should always be destructed after use (see ## A non centre descriptor should always be destructed after use (see
## also# comments on `fork()`.) ## also# comments on `fork()`.)
AristoApiForkFn* =
proc(db: AristoDbRef;
rawTopLayer = false;
): Result[AristoDbRef,AristoError]
{.noRaise.}
## This function creates a new empty descriptor accessing the same
## backend (if any) database as the argument `db`. This new descriptor
## joins the list of descriptors accessing the same backend database.
##
## After use, any unused non centre descriptor should be destructed
## via `forget()`. Not doing so will not only hold memory ressources
## but might also cost computing ressources for maintaining and
## updating backend filters when writing to the backend database .
##
## If the argument `rawTopLayer` is set `true` the function will
## provide an uninitalised and inconsistent (!) top layer. This
## setting avoids some database lookup for cases where the top layer
## is redefined anyway.
AristoApiForkTopFn* = AristoApiForkTopFn* =
proc(db: AristoDbRef; proc(db: AristoDbRef;
dontHashify = false; dontHashify = false;
): Result[AristoDbRef,AristoError] ): Result[AristoDbRef,AristoError]
{.noRaise.} {.noRaise.}
## Clone a top transaction into a new DB descriptor accessing the same ## Clone a descriptor in a way so that there is exactly one active
## backend database (if any) as the argument `db`. The new descriptor ## transaction.
## is linked to the transaction parent and is fully functional as a
## forked instance (see comments on `aristo_desc.reCentre()` for
## details.) If there is no active transaction, the top layer state
## is cloned.
##
## Input situation:
## ::
## tx -> db0 with tx is top transaction, tx.level > 0
##
## Output situation:
## ::
## tx -> db0 \
## > share the same backend
## tx1 -> db1 /
##
## where `tx.level > 0`, `db1.level == 1` and `db1` is returned. The
## transaction `tx1` can be retrieved via `db1.txTop()`.
##
## The new DB descriptor will contain a copy of the argument transaction
## `tx` as top layer of level 1 (i.e. this is he only transaction.)
## Rolling back will end up at the backend layer (incl. backend filter.)
## ##
## If the arguent flag `dontHashify` is passed `true`, the clone ## If the arguent flag `dontHashify` is passed `true`, the clone
## descriptor will *NOT* be hashified right after construction. ## descriptor will *NOT* be hashified right after construction.
@ -365,7 +325,6 @@ type
fetchPayload*: AristoApiFetchPayloadFn fetchPayload*: AristoApiFetchPayloadFn
finish*: AristoApiFinishFn finish*: AristoApiFinishFn
forget*: AristoApiForgetFn forget*: AristoApiForgetFn
fork*: AristoApiForkFn
forkTop*: AristoApiForkTopFn forkTop*: AristoApiForkTopFn
getKey*: AristoApiGetKeyFn getKey*: AristoApiGetKeyFn
getKeyRc*: AristoApiGetKeyRcFn getKeyRc*: AristoApiGetKeyRcFn
@ -397,7 +356,6 @@ type
AristoApiProfFetchPayloadFn = "fetchPayload" AristoApiProfFetchPayloadFn = "fetchPayload"
AristoApiProfFinishFn = "finish" AristoApiProfFinishFn = "finish"
AristoApiProfForgetFn = "forget" AristoApiProfForgetFn = "forget"
AristoApiProfForkFn = "fork"
AristoApiProfForkTopFn = "forkTop" AristoApiProfForkTopFn = "forkTop"
AristoApiProfGetKeyFn = "getKey" AristoApiProfGetKeyFn = "getKey"
AristoApiProfGetKeyRcFn = "getKeyRc" AristoApiProfGetKeyRcFn = "getKeyRc"
@ -439,7 +397,6 @@ when AutoValidateApiHooks:
doAssert not api.fetchPayload.isNil doAssert not api.fetchPayload.isNil
doAssert not api.finish.isNil doAssert not api.finish.isNil
doAssert not api.forget.isNil doAssert not api.forget.isNil
doAssert not api.fork.isNil
doAssert not api.forkTop.isNil doAssert not api.forkTop.isNil
doAssert not api.getKey.isNil doAssert not api.getKey.isNil
doAssert not api.getKeyRc.isNil doAssert not api.getKeyRc.isNil
@ -491,7 +448,6 @@ func init*(api: var AristoApiObj) =
api.fetchPayload = fetchPayload api.fetchPayload = fetchPayload
api.finish = finish api.finish = finish
api.forget = forget api.forget = forget
api.fork = fork
api.forkTop = forkTop api.forkTop = forkTop
api.getKey = getKey api.getKey = getKey
api.getKeyRc = getKeyRc api.getKeyRc = getKeyRc
@ -526,7 +482,6 @@ func dup*(api: AristoApiRef): AristoApiRef =
fetchPayload: api.fetchPayload, fetchPayload: api.fetchPayload,
finish: api.finish, finish: api.finish,
forget: api.forget, forget: api.forget,
fork: api.fork,
forkTop: api.forkTop, forkTop: api.forkTop,
getKey: api.getKey, getKey: api.getKey,
getKeyRc: api.getKeyRc, getKeyRc: api.getKeyRc,
@ -606,11 +561,6 @@ func init*(
AristoApiProfForgetFn.profileRunner: AristoApiProfForgetFn.profileRunner:
result = api.forget(a) result = api.forget(a)
profApi.fork =
proc(a: AristoDbRef; b = false): auto =
AristoApiProfForkFn.profileRunner:
result = api.fork(a, b)
profApi.forkTop = profApi.forkTop =
proc(a: AristoDbRef; b = false): auto = proc(a: AristoDbRef; b = false): auto =
AristoApiProfForkTopFn.profileRunner: AristoApiProfForkTopFn.profileRunner:

View File

@ -176,6 +176,7 @@ proc reCentre*(db: AristoDbRef) =
proc fork*( proc fork*(
db: AristoDbRef; db: AristoDbRef;
noTopLayer = false; noTopLayer = false;
noFilter = false;
): Result[AristoDbRef,AristoError] = ): Result[AristoDbRef,AristoError] =
## This function creates a new empty descriptor accessing the same backend ## This function creates a new empty descriptor accessing the same backend
## (if any) database as the argument `db`. This new descriptor joins the ## (if any) database as the argument `db`. This new descriptor joins the
@ -186,6 +187,9 @@ proc fork*(
## also cost computing ressources for maintaining and updating backend ## also cost computing ressources for maintaining and updating backend
## filters when writing to the backend database . ## filters when writing to the backend database .
## ##
## If the argument `noFilter` is set `true` the function will fork directly
## off the backend database and ignore any filter.
##
## If the argument `noTopLayer` is set `true` the function will provide an ## If the argument `noTopLayer` is set `true` the function will provide an
## uninitalised and inconsistent (!) descriptor object without top layer. ## uninitalised and inconsistent (!) descriptor object without top layer.
## This setting avoids some database lookup for cases where the top layer ## This setting avoids some database lookup for cases where the top layer
@ -199,8 +203,14 @@ proc fork*(
dudes: db.dudes, dudes: db.dudes,
backend: db.backend) backend: db.backend)
if not noFilter:
clone.roFilter = db.roFilter # Ref is ok here (filters are immutable)
if not noTopLayer: if not noTopLayer:
clone.top = LayerRef.init() clone.top = LayerRef.init()
if not db.roFilter.isNil:
clone.top.final.vGen = db.roFilter.vGen
else:
let rc = clone.backend.getIdgFn() let rc = clone.backend.getIdgFn()
if rc.isOk: if rc.isOk:
clone.top.final.vGen = rc.value clone.top.final.vGen = rc.value

View File

@ -18,8 +18,9 @@ import
results, results,
"."/[aristo_desc, aristo_filter, aristo_get, aristo_layers, aristo_hashify] "."/[aristo_desc, aristo_filter, aristo_get, aristo_layers, aristo_hashify]
func isTop*(tx: AristoTxRef): bool func isTop*(tx: AristoTxRef): bool {.gcsafe.}
func level*(db: AristoDbRef): int func level*(db: AristoDbRef): int {.gcsafe.}
proc txBegin*(db: AristoDbRef): Result[AristoTxRef,AristoError] {.gcsafe.}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Private helpers # Private helpers
@ -72,6 +73,7 @@ func to*(tx: AristoTxRef; T: type[AristoDbRef]): T =
## Getter, retrieves the parent database descriptor from argument `tx` ## Getter, retrieves the parent database descriptor from argument `tx`
tx.db tx.db
proc forkTx*( proc forkTx*(
tx: AristoTxRef; # Transaction descriptor tx: AristoTxRef; # Transaction descriptor
dontHashify = false; # Process/fix MPT hashes dontHashify = false; # Process/fix MPT hashes
@ -127,11 +129,9 @@ proc forkTx*(
return err(rc.error) return err(rc.error)
# Set up clone associated to `db` # Set up clone associated to `db`
let txClone = ? db.fork(noToplayer = true) let txClone = ? db.fork(noToplayer = true, noFilter = false)
txClone.top = db.layersCc tx.level # Provide tx level 1 stack txClone.top = db.layersCc tx.level # Provide tx level 1 stack
txClone.stack = @[stackLayer] # Zero level stack txClone.stack = @[stackLayer] # Zero level stack
txClone.roFilter = db.roFilter # No need to copy (done when updated)
txClone.backend = db.backend
txClone.top.txUid = 1 txClone.top.txUid = 1
txClone.txUidGen = 1 txClone.txUidGen = 1
@ -154,43 +154,26 @@ proc forkTop*(
dontHashify = false; # Process/fix MPT hashes dontHashify = false; # Process/fix MPT hashes
): Result[AristoDbRef,AristoError] = ): Result[AristoDbRef,AristoError] =
## Variant of `forkTx()` for the top transaction if there is any. Otherwise ## Variant of `forkTx()` for the top transaction if there is any. Otherwise
## the top layer is cloned, only. ## the top layer is cloned, and an empty transaction is set up. After
## successful fork the returned descriptor has transaction level 1.
## ##
## Use `aristo_desc.forget()` to clean up this descriptor. ## Use `aristo_desc.forget()` to clean up this descriptor.
## ##
if db.txRef.isNil: if db.txRef.isNil:
let dbClone = ? db.fork(noToplayer = true) let dbClone = ? db.fork(noToplayer = true, noFilter = false)
dbClone.top = db.layersCc # Is a deep copy dbClone.top = db.layersCc # Is a deep copy
dbClone.roFilter = db.roFilter # No need to copy contents when updated
dbClone.backend = db.backend
if not dontHashify: if not dontHashify:
dbClone.hashify().isOkOr: dbClone.hashify().isOkOr:
discard dbClone.forget() discard dbClone.forget()
return err(error[1]) return err(error[1])
discard dbClone.txBegin
return ok(dbClone) return ok(dbClone)
# End if()
db.txRef.forkTx dontHashify db.txRef.forkTx dontHashify
proc exec*(
tx: AristoTxRef;
action: AristoDbAction;
dontHashify = false; # Process/fix MPT hashes
): Result[void,AristoError] =
## Execute function argument `action()` on a temporary `tx.forkTx()`
## transaction clone database. After return, the temporary database gets
## destroyed.
##
## If the arguent flag `dontHashify` is passed `true`, the clone database
## will *NOT* be hashified right after construction.
##
let db = ? tx.forkTx dontHashify
db.action()
? db.forget()
ok()
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Public functions: Transaction frame # Public functions: Transaction frame
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -272,7 +255,6 @@ proc commit*(
if 0 < db.stack.len: if 0 < db.stack.len:
db.txRef.txUid = db.getTxUid db.txRef.txUid = db.getTxUid
db.top.txUid = db.txRef.txUid db.top.txUid = db.txRef.txUid
ok() ok()
@ -364,7 +346,6 @@ proc stow*(
delta: LayerDeltaRef(), delta: LayerDeltaRef(),
final: LayerFinalRef(vGen: db.vGen), final: LayerFinalRef(vGen: db.vGen),
txUid: db.top.txUid) txUid: db.top.txUid)
ok() ok()
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -479,7 +479,7 @@ proc ctxMethods(cCtx: AristoCoreDbCtxRef): CoreDbCtxFns =
base = cCtx.base # Will not change and can be captured base = cCtx.base # Will not change and can be captured
db = base.parent # Ditto db = base.parent # Ditto
api = base.api # Ditto api = base.api # Ditto
mpt = base.ctx.mpt # Ditto mpt = cCtx.mpt # Ditto
proc ctxNewTrie( proc ctxNewTrie(
kind: CoreDbSubTrie; kind: CoreDbSubTrie;
@ -724,10 +724,11 @@ func init*(T: type AristoBaseRef; db: CoreDbRef; adb: AristoDbRef): T =
api: AristoApiRef.init()) api: AristoApiRef.init())
# Create initial context # Create initial context
result.ctx = db.bless AristoCoreDbCtxRef( let ctx = AristoCoreDbCtxRef(
base: result, base: result,
mpt: adb) mpt: adb)
result.ctx.methods = result.ctx.ctxMethods ctx.methods = ctx.ctxMethods
result.ctx = db.bless ctx
when CoreDbEnableApiProfiling: when CoreDbEnableApiProfiling:
let profApi = AristoApiProfRef.init(result.api, adb.backend) let profApi = AristoApiProfRef.init(result.api, adb.backend)

View File

@ -71,7 +71,6 @@ proc validateMethodsDesc(fns: CoreDbMptFns) =
proc validateMethodsDesc(fns: CoreDbAccFns) = proc validateMethodsDesc(fns: CoreDbAccFns) =
doAssert not fns.backendFn.isNil doAssert not fns.backendFn.isNil
doAssert not fns.newMptFn.isNil
doAssert not fns.fetchFn.isNil doAssert not fns.fetchFn.isNil
doAssert not fns.deleteFn.isNil doAssert not fns.deleteFn.isNil
doAssert not fns.stoFlushFn.isNil doAssert not fns.stoFlushFn.isNil

View File

@ -1,5 +1,5 @@
# nimbus-eth1 # nimbus-eth1
# Copyright (c) 2023 Status Research & Development GmbH # Copyright (c) 2023-2024 Status Research & Development GmbH
# Licensed under either of # Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
# http://www.apache.org/licenses/LICENSE-2.0) # http://www.apache.org/licenses/LICENSE-2.0)
@ -20,7 +20,8 @@ import
./kvt_desc/desc_backend, ./kvt_desc/desc_backend,
"."/[kvt_desc, kvt_layers] "."/[kvt_desc, kvt_layers]
func isTop*(tx: KvtTxRef): bool func isTop*(tx: KvtTxRef): bool {.gcsafe.}
proc txBegin*(db: KvtDbRef): Result[KvtTxRef,KvtError] {.gcsafe.}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Private helpers # Private helpers
@ -113,31 +114,20 @@ proc forkTx*(tx: KvtTxRef): Result[KvtDbRef,KvtError] =
proc forkTop*(db: KvtDbRef): Result[KvtDbRef,KvtError] = proc forkTop*(db: KvtDbRef): Result[KvtDbRef,KvtError] =
## Variant of `forkTx()` for the top transaction if there is any. Otherwise ## Variant of `forkTx()` for the top transaction if there is any. Otherwise
## the top layer is cloned, only. ## the top layer is cloned, and an empty transaction is set up. After
## successful fork the returned descriptor has transaction level 1.
## ##
## Use `kvt_desc.forget()` to clean up this descriptor. ## Use `kvt_desc.forget()` to clean up this descriptor.
## ##
if db.txRef.isNil: if db.txRef.isNil:
let dbClone = ? db.fork() let dbClone = ? db.fork()
dbClone.top = db.layersCc dbClone.top = db.layersCc
discard dbClone.txBegin
return ok(dbClone) return ok(dbClone)
db.txRef.forkTx() db.txRef.forkTx()
proc exec*(
tx: KvtTxRef;
action: KvtDbAction;
): Result[void,KvtError] =
## Execute function argument `action()` on a temporary `tx.forkTx()`
## transaction database. After return, the temporary database gets
## destroyed.
##
let db = ? tx.forkTx()
db.action()
? db.forget()
ok()
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Public functions: Transaction frame # Public functions: Transaction frame
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -191,6 +191,11 @@ proc dbTriplet(w: LeafQuartet; rdbPath: string): Result[DbTriplet,AristoError] =
let dx = [db, db.forkTop.value, db.forkTop.value] let dx = [db, db.forkTop.value, db.forkTop.value]
xCheck dx[0].nForked == 2 xCheck dx[0].nForked == 2
# Reduce unwanted tx layers
for n in 1 ..< dx.len:
check dx[n].level == 1
check dx[n].txTop.value.commit.isOk
# Clause (9) from `aristo/README.md` example # Clause (9) from `aristo/README.md` example
for n in 0 ..< dx.len: for n in 0 ..< dx.len:
let report = dx[n].mergeList w[n+1] let report = dx[n].mergeList w[n+1]

View File

@ -217,7 +217,7 @@ proc hashify*(
noisy: bool; noisy: bool;
): Result[void,(VertexID,AristoError)] = ): Result[void,(VertexID,AristoError)] =
when declared(aristo_hashify.noisy): when declared(aristo_hashify.noisy):
aristo_hashify.exec(aristo_hashify.hashify(db), noisy) aristo_hashify.exec(noisy, aristo_hashify.hashify(db))
else: else:
aristo_hashify.hashify(db) aristo_hashify.hashify(db)
@ -230,7 +230,7 @@ proc delete*(
noisy: bool; noisy: bool;
): Result[bool,(VertexID,AristoError)] = ): Result[bool,(VertexID,AristoError)] =
when declared(aristo_delete.noisy): when declared(aristo_delete.noisy):
aristo_delete.exec(aristo_delete.delete(db, root, path, accPath), noisy) aristo_delete.exec(noisy, aristo_delete.delete(db, root, path, accPath))
else: else:
aristo_delete.delete(db, root, path, accPath) aristo_delete.delete(db, root, path, accPath)
@ -241,7 +241,7 @@ proc delete*(
noisy: bool; noisy: bool;
): Result[bool,(VertexID,AristoError)] = ): Result[bool,(VertexID,AristoError)] =
when declared(aristo_delete.noisy): when declared(aristo_delete.noisy):
aristo_delete.exec(aristo_delete.delete(db, lty, accPath), noisy) aristo_delete.exec(noisy, aristo_delete.delete(db, lty, accPath))
else: else:
aristo_delete.delete(db, lty, accPath) aristo_delete.delete(db, lty, accPath)
@ -252,7 +252,7 @@ proc delTree*(
noisy: bool; noisy: bool;
): Result[void,(VertexID,AristoError)] = ): Result[void,(VertexID,AristoError)] =
when declared(aristo_delete.noisy): when declared(aristo_delete.noisy):
aristo_delete.exec(aristo_delete.delTree(db, root, accPath), noisy) aristo_delete.exec(noisy, aristo_delete.delTree(db, root, accPath))
else: else:
aristo_delete.delTree(db, root, accPath) aristo_delete.delTree(db, root, accPath)
@ -266,7 +266,7 @@ proc merge*(
noisy: bool; noisy: bool;
): Result[bool, AristoError] = ): Result[bool, AristoError] =
when declared(aristo_merge.noisy): when declared(aristo_merge.noisy):
aristo_merge.exec(aristo_merge.merge(db, root, path, data, accPath), noisy) aristo_merge.exec(noisy, aristo_merge.merge(db, root, path, data, accPath))
else: else:
aristo_merge.merge(db, root, path, data, accPath) aristo_merge.merge(db, root, path, data, accPath)
@ -278,7 +278,7 @@ proc mergePayload*(
noisy: bool; noisy: bool;
): Result[Hike,AristoError] = ): Result[Hike,AristoError] =
when declared(aristo_merge.noisy): when declared(aristo_merge.noisy):
aristo_merge.exec(aristo_merge.mergePayload(db, lty, pyl, accPath), noisy) aristo_merge.exec(noisy, aristo_merge.mergePayload(db, lty, pyl, accPath))
else: else:
aristo_merge.mergePayload(db, lty, pyl, accPath) aristo_merge.mergePayload(db, lty, pyl, accPath)