Remove `forkTx` and friends (#2951)

The forking facility has been replaced by ForkedChain - frames and
layers are two other mechanisms that mostly do the same thing at the
aristo level, without quite providing the functionality FC needs - this
cleanup will make that integration easier.
This commit is contained in:
Jacek Sieka 2024-12-18 11:56:46 +01:00 committed by GitHub
parent 45bc6422a0
commit 06a544ac85
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
47 changed files with 67 additions and 1447 deletions

View File

@ -13,7 +13,7 @@
import import
std/tables, std/tables,
eth/eip1559, eth/eip1559,
eth/common/[hashes, accounts, headers, addresses], eth/common/[blocks, hashes, accounts, headers, addresses],
../db/[ledger, core_db], ../db/[ledger, core_db],
../constants, ../constants,
./chain_config ./chain_config

View File

@ -11,7 +11,7 @@
import import
std/[tables, sets], std/[tables, sets],
stint, stint,
eth/common, eth/common/[addresses, transactions],
../utils/mergeutils ../utils/mergeutils
type type
@ -67,9 +67,9 @@ proc add*(ac: var AccessList, address: Address, slot: UInt256) =
proc clear*(ac: var AccessList) {.inline.} = proc clear*(ac: var AccessList) {.inline.} =
ac.slots.clear() ac.slots.clear()
func getAccessList*(ac: AccessList): common.AccessList = func getAccessList*(ac: AccessList): transactions.AccessList =
for address, slots in ac.slots: for address, slots in ac.slots:
result.add common.AccessPair( result.add transactions.AccessPair(
address : address, address : address,
storageKeys: slots.toStorageKeys, storageKeys: slots.toStorageKeys,
) )

View File

@ -115,28 +115,6 @@ type
{.noRaise.} {.noRaise.}
## Fetch the Merkle hash of the storage root related to `accPath`. ## Fetch the Merkle hash of the storage root related to `accPath`.
AristoApiFindTxFn* =
proc(db: AristoDbRef;
rvid: RootedVertexID;
key: HashKey;
): Result[int,AristoError]
{.noRaise.}
## Find the transaction where the vertex with ID `vid` exists and has
## the Merkle hash key `key`. If there is no transaction available,
## search in the filter and then in the backend.
##
## If the above procedure succeeds, an integer indicating the transaction
## level is returned:
##
## * `0` -- top level, current layer
## * `1`,`2`,`..` -- some transaction level further down the stack
## * `-1` -- the filter between transaction stack and database backend
## * `-2` -- the databse backend
##
## A successful return code might be used for the `forkTx()` call for
## creating a forked descriptor that provides the pair `(vid,key)`.
##
AristoApiFinishFn* = AristoApiFinishFn* =
proc(db: AristoDbRef; proc(db: AristoDbRef;
eradicate = false; eradicate = false;
@ -161,32 +139,6 @@ 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()`.)
AristoApiForkTxFn* =
proc(db: AristoDbRef;
backLevel: int;
): Result[AristoDbRef,AristoError]
{.noRaise.}
## Fork a new descriptor obtained from parts of the argument database
## as described by arguments `db` and `backLevel`.
##
## If the argument `backLevel` is non-negative, the forked descriptor
## will provide the database view where the first `backLevel` transaction
## layers are stripped and the remaing layers are squashed into a single
## transaction.
##
## If `backLevel` is `-1`, a database descriptor with empty transaction
## layers will be provided where the `balancer` between database and
## transaction layers are kept in place.
##
## If `backLevel` is `-2`, a database descriptor with empty transaction
## layers will be provided without a `balancer`.
##
## The returned database descriptor will always have transaction level one.
## If there were no transactions that could be squashed, an empty
## transaction is added.
##
## Use `aristo_desc.forget()` to clean up this descriptor.
AristoApiHashifyFn* = AristoApiHashifyFn* =
proc(db: AristoDbRef; proc(db: AristoDbRef;
): Result[void,(VertexID,AristoError)] ): Result[void,(VertexID,AristoError)]
@ -233,14 +185,6 @@ type
## Getter, non-negative nesting level (i.e. number of pending ## Getter, non-negative nesting level (i.e. number of pending
## transactions) ## transactions)
AristoApiNForkedFn* =
proc(db: AristoDbRef;
): int
{.noRaise.}
## Returns the number of non centre descriptors (see comments on
## `reCentre()` for details.) This function is a fast version of
## `db.forked.toSeq.len`.
AristoApiMergeAccountRecordFn* = AristoApiMergeAccountRecordFn* =
proc(db: AristoDbRef; proc(db: AristoDbRef;
accPath: Hash32; accPath: Hash32;
@ -358,23 +302,6 @@ type
## ##
## The argument `nxtSid` will be the ID for the next saved state record. ## The argument `nxtSid` will be the ID for the next saved state record.
AristoApiReCentreFn* =
proc(db: AristoDbRef;
): Result[void,AristoError]
{.noRaise.}
## Re-focus the `db` argument descriptor so that it becomes the centre.
## Nothing is done if the `db` descriptor is the centre, already.
##
## With several descriptors accessing the same backend database there is
## a single one that has write permission for the backend (regardless
## whether there is a backend, at all.) The descriptor entity with write
## permission is called *the centre*.
##
## After invoking `reCentre()`, the argument database `db` can only be
## destructed by `finish()` which also destructs all other descriptors
## accessing the same backend database. Descriptors where `isCentre()`
## returns `false` must be single destructed with `forget()`.
AristoApiRollbackFn* = AristoApiRollbackFn* =
proc(tx: AristoTxRef; proc(tx: AristoTxRef;
): Result[void,AristoError] ): Result[void,AristoError]
@ -425,17 +352,13 @@ type
fetchStorageData*: AristoApiFetchStorageDataFn fetchStorageData*: AristoApiFetchStorageDataFn
fetchStorageRoot*: AristoApiFetchStorageRootFn fetchStorageRoot*: AristoApiFetchStorageRootFn
findTx*: AristoApiFindTxFn
finish*: AristoApiFinishFn finish*: AristoApiFinishFn
forget*: AristoApiForgetFn
forkTx*: AristoApiForkTxFn
hasPathAccount*: AristoApiHasPathAccountFn hasPathAccount*: AristoApiHasPathAccountFn
hasPathStorage*: AristoApiHasPathStorageFn hasPathStorage*: AristoApiHasPathStorageFn
hasStorageData*: AristoApiHasStorageDataFn hasStorageData*: AristoApiHasStorageDataFn
isTop*: AristoApiIsTopFn isTop*: AristoApiIsTopFn
level*: AristoApiLevelFn level*: AristoApiLevelFn
nForked*: AristoApiNForkedFn
mergeAccountRecord*: AristoApiMergeAccountRecordFn mergeAccountRecord*: AristoApiMergeAccountRecordFn
mergeStorageData*: AristoApiMergeStorageDataFn mergeStorageData*: AristoApiMergeStorageDataFn
@ -449,7 +372,6 @@ type
pathAsBlob*: AristoApiPathAsBlobFn pathAsBlob*: AristoApiPathAsBlobFn
persist*: AristoApiPersistFn persist*: AristoApiPersistFn
reCentre*: AristoApiReCentreFn
rollback*: AristoApiRollbackFn rollback*: AristoApiRollbackFn
txBegin*: AristoApiTxBeginFn txBegin*: AristoApiTxBeginFn
txLevel*: AristoApiTxLevelFn txLevel*: AristoApiTxLevelFn
@ -472,10 +394,7 @@ type
AristoApiProfFetchStorageDataFn = "fetchStorageData" AristoApiProfFetchStorageDataFn = "fetchStorageData"
AristoApiProfFetchStorageRootFn = "fetchStorageRoot" AristoApiProfFetchStorageRootFn = "fetchStorageRoot"
AristoApiProfFindTxFn = "findTx"
AristoApiProfFinishFn = "finish" AristoApiProfFinishFn = "finish"
AristoApiProfForgetFn = "forget"
AristoApiProfForkTxFn = "forkTx"
AristoApiProfHasPathAccountFn = "hasPathAccount" AristoApiProfHasPathAccountFn = "hasPathAccount"
AristoApiProfHasPathStorageFn = "hasPathStorage" AristoApiProfHasPathStorageFn = "hasPathStorage"
@ -483,7 +402,6 @@ type
AristoApiProfIsTopFn = "isTop" AristoApiProfIsTopFn = "isTop"
AristoApiProfLevelFn = "level" AristoApiProfLevelFn = "level"
AristoApiProfNForkedFn = "nForked"
AristoApiProfMergeAccountRecordFn = "mergeAccountRecord" AristoApiProfMergeAccountRecordFn = "mergeAccountRecord"
AristoApiProfMergeStorageDataFn = "mergeStorageData" AristoApiProfMergeStorageDataFn = "mergeStorageData"
@ -495,7 +413,6 @@ type
AristoApiProfPathAsBlobFn = "pathAsBlob" AristoApiProfPathAsBlobFn = "pathAsBlob"
AristoApiProfPersistFn = "persist" AristoApiProfPersistFn = "persist"
AristoApiProfReCentreFn = "reCentre"
AristoApiProfRollbackFn = "rollback" AristoApiProfRollbackFn = "rollback"
AristoApiProfTxBeginFn = "txBegin" AristoApiProfTxBeginFn = "txBegin"
AristoApiProfTxLevelFn = "txLevel" AristoApiProfTxLevelFn = "txLevel"
@ -534,10 +451,7 @@ when AutoValidateApiHooks:
doAssert not api.fetchStorageData.isNil doAssert not api.fetchStorageData.isNil
doAssert not api.fetchStorageRoot.isNil doAssert not api.fetchStorageRoot.isNil
doAssert not api.findTx.isNil
doAssert not api.finish.isNil doAssert not api.finish.isNil
doAssert not api.forget.isNil
doAssert not api.forkTx.isNil
doAssert not api.hasPathAccount.isNil doAssert not api.hasPathAccount.isNil
doAssert not api.hasPathStorage.isNil doAssert not api.hasPathStorage.isNil
@ -545,7 +459,6 @@ when AutoValidateApiHooks:
doAssert not api.isTop.isNil doAssert not api.isTop.isNil
doAssert not api.level.isNil doAssert not api.level.isNil
doAssert not api.nForked.isNil
doAssert not api.mergeAccountRecord.isNil doAssert not api.mergeAccountRecord.isNil
doAssert not api.mergeStorageData.isNil doAssert not api.mergeStorageData.isNil
@ -557,7 +470,6 @@ when AutoValidateApiHooks:
doAssert not api.pathAsBlob.isNil doAssert not api.pathAsBlob.isNil
doAssert not api.persist.isNil doAssert not api.persist.isNil
doAssert not api.reCentre.isNil
doAssert not api.rollback.isNil doAssert not api.rollback.isNil
doAssert not api.txBegin.isNil doAssert not api.txBegin.isNil
doAssert not api.txLevel.isNil doAssert not api.txLevel.isNil
@ -601,10 +513,7 @@ func init*(api: var AristoApiObj) =
api.fetchStorageData = fetchStorageData api.fetchStorageData = fetchStorageData
api.fetchStorageRoot = fetchStorageRoot api.fetchStorageRoot = fetchStorageRoot
api.findTx = findTx
api.finish = finish api.finish = finish
api.forget = forget
api.forkTx = forkTx
api.hasPathAccount = hasPathAccount api.hasPathAccount = hasPathAccount
api.hasPathStorage = hasPathStorage api.hasPathStorage = hasPathStorage
@ -612,7 +521,6 @@ func init*(api: var AristoApiObj) =
api.isTop = isTop api.isTop = isTop
api.level = level api.level = level
api.nForked = nForked
api.mergeAccountRecord = mergeAccountRecord api.mergeAccountRecord = mergeAccountRecord
api.mergeStorageData = mergeStorageData api.mergeStorageData = mergeStorageData
@ -624,7 +532,6 @@ func init*(api: var AristoApiObj) =
api.pathAsBlob = pathAsBlob api.pathAsBlob = pathAsBlob
api.persist = persist api.persist = persist
api.reCentre = reCentre
api.rollback = rollback api.rollback = rollback
api.txBegin = txBegin api.txBegin = txBegin
api.txLevel = txLevel api.txLevel = txLevel
@ -650,10 +557,7 @@ func dup*(api: AristoApiRef): AristoApiRef =
fetchStorageData: api.fetchStorageData, fetchStorageData: api.fetchStorageData,
fetchStorageRoot: api.fetchStorageRoot, fetchStorageRoot: api.fetchStorageRoot,
findTx: api.findTx,
finish: api.finish, finish: api.finish,
forget: api.forget,
forkTx: api.forkTx,
hasPathAccount: api.hasPathAccount, hasPathAccount: api.hasPathAccount,
hasPathStorage: api.hasPathStorage, hasPathStorage: api.hasPathStorage,
@ -661,7 +565,6 @@ func dup*(api: AristoApiRef): AristoApiRef =
isTop: api.isTop, isTop: api.isTop,
level: api.level, level: api.level,
nForked: api.nForked,
mergeAccountRecord: api.mergeAccountRecord, mergeAccountRecord: api.mergeAccountRecord,
mergeStorageData: api.mergeStorageData, mergeStorageData: api.mergeStorageData,
@ -673,7 +576,6 @@ func dup*(api: AristoApiRef): AristoApiRef =
pathAsBlob: api.pathAsBlob, pathAsBlob: api.pathAsBlob,
persist: api.persist, persist: api.persist,
reCentre: api.reCentre,
rollback: api.rollback, rollback: api.rollback,
txBegin: api.txBegin, txBegin: api.txBegin,
txLevel: api.txLevel, txLevel: api.txLevel,
@ -753,26 +655,11 @@ func init*(
AristoApiProfFetchStorageRootFn.profileRunner: AristoApiProfFetchStorageRootFn.profileRunner:
result = api.fetchStorageRoot(a, b) result = api.fetchStorageRoot(a, b)
profApi.findTx =
proc(a: AristoDbRef; b: RootedVertexID; c: HashKey): auto =
AristoApiProfFindTxFn.profileRunner:
result = api.findTx(a, b, c)
profApi.finish = profApi.finish =
proc(a: AristoDbRef; b = false) = proc(a: AristoDbRef; b = false) =
AristoApiProfFinishFn.profileRunner: AristoApiProfFinishFn.profileRunner:
api.finish(a, b) api.finish(a, b)
profApi.forget =
proc(a: AristoDbRef): auto =
AristoApiProfForgetFn.profileRunner:
result = api.forget(a)
profApi.forkTx =
proc(a: AristoDbRef; b: int): auto =
AristoApiProfForkTxFn.profileRunner:
result = api.forkTx(a, b)
profApi.hasPathAccount = profApi.hasPathAccount =
proc(a: AristoDbRef; b: Hash32): auto = proc(a: AristoDbRef; b: Hash32): auto =
AristoApiProfHasPathAccountFn.profileRunner: AristoApiProfHasPathAccountFn.profileRunner:
@ -798,11 +685,6 @@ func init*(
AristoApiProfLevelFn.profileRunner: AristoApiProfLevelFn.profileRunner:
result = api.level(a) result = api.level(a)
profApi.nForked =
proc(a: AristoDbRef): auto =
AristoApiProfNForkedFn.profileRunner:
result = api.nForked(a)
profApi.mergeAccountRecord = profApi.mergeAccountRecord =
proc(a: AristoDbRef; b: Hash32; c: AristoAccount): auto = proc(a: AristoDbRef; b: Hash32; c: AristoAccount): auto =
AristoApiProfMergeAccountRecordFn.profileRunner: AristoApiProfMergeAccountRecordFn.profileRunner:
@ -843,11 +725,6 @@ func init*(
AristoApiProfPersistFn.profileRunner: AristoApiProfPersistFn.profileRunner:
result = api.persist(a, b) result = api.persist(a, b)
profApi.reCentre =
proc(a: AristoDbRef): auto =
AristoApiProfReCentreFn.profileRunner:
result = api.reCentre(a)
profApi.rollback = profApi.rollback =
proc(a: AristoTxRef): auto = proc(a: AristoTxRef): auto =
AristoApiProfRollbackFn.profileRunner: AristoApiProfRollbackFn.profileRunner:

View File

@ -16,9 +16,8 @@ import
std/tables, std/tables,
eth/common, eth/common,
results, results,
./aristo_delta/[delta_merge, delta_reverse],
./aristo_desc/desc_backend, ./aristo_desc/desc_backend,
"."/[aristo_desc, aristo_layers] "."/[aristo_desc]
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Public functions, save to backend # Public functions, save to backend
@ -26,13 +25,12 @@ import
proc deltaPersistentOk*(db: AristoDbRef): bool = proc deltaPersistentOk*(db: AristoDbRef): bool =
## Check whether the read-only filter can be merged into the backend ## Check whether the read-only filter can be merged into the backend
not db.backend.isNil and db.isCentre not db.backend.isNil
proc deltaPersistent*( proc deltaPersistent*(
db: AristoDbRef; # Database db: AristoDbRef; # Database
nxtFid = 0u64; # Next filter ID (if any) nxtFid = 0u64; # Next filter ID (if any)
reCentreOk = false;
): Result[void,AristoError] = ): Result[void,AristoError] =
## Resolve (i.e. move) the balancer into the physical backend database. ## Resolve (i.e. move) the balancer into the physical backend database.
## ##
@ -62,32 +60,6 @@ proc deltaPersistent*(
? be.putEndFn(? be.putBegFn()) ? be.putEndFn(? be.putBegFn())
return ok() return ok()
# Make sure that the argument `db` is at the centre so the backend is in
# read-write mode for this peer.
let parent = db.getCentre
if db != parent:
if not reCentreOk:
return err(FilBackendRoMode)
? db.reCentre()
# Always re-centre to `parent` (in case `reCentreOk` was set)
defer: discard parent.reCentre()
# Update forked balancers here do that errors are detected early (if any.)
if 0 < db.nForked:
let rev = db.revFilter(db.balancer).valueOr:
return err(error[1])
if not rev.isEmpty: # Can an empty `rev` happen at all?
var unsharedRevOk = true
for w in db.forked:
if not w.db.balancer.isValid:
unsharedRevOk = false
# The `rev` filter can be modified if one can make sure that it is
# not shared (i.e. only previously merged into the w.db.balancer.)
# Note that it is trivially true for a single fork.
let modLowerOk = w.isLast and unsharedRevOk
w.db.balancer = deltaMerge(
w.db.balancer, modUpperOk=false, rev, modLowerOk=modLowerOk)
let lSst = SavedState( let lSst = SavedState(
key: EMPTY_ROOT_HASH, # placeholder for more key: EMPTY_ROOT_HASH, # placeholder for more
serial: nxtFid) serial: nxtFid)

View File

@ -9,7 +9,6 @@
# except according to those terms. # except according to those terms.
import import
std/tables,
".."/[aristo_desc, aristo_layers] ".."/[aristo_desc, aristo_layers]
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -18,9 +17,7 @@ import
proc deltaMerge*( proc deltaMerge*(
upper: LayerRef; # Think of `top`, `nil` is ok upper: LayerRef; # Think of `top`, `nil` is ok
modUpperOk: bool; # May re-use/modify `upper`
lower: LayerRef; # Think of `balancer`, `nil` is ok lower: LayerRef; # Think of `balancer`, `nil` is ok
modLowerOk: bool; # May re-use/modify `lower`
): LayerRef = ): LayerRef =
## Merge argument `upper` into the `lower` filter instance. ## Merge argument `upper` into the `lower` filter instance.
## ##
@ -29,51 +26,18 @@ proc deltaMerge*(
## ##
if lower.isNil: if lower.isNil:
# Degenerate case: `upper` is void # Degenerate case: `upper` is void
result = upper upper
elif upper.isNil: elif upper.isNil:
# Degenerate case: `lower` is void # Degenerate case: `lower` is void
result = lower lower
elif modLowerOk: else:
# Can modify `lower` which is the prefered action mode but applies only # Can modify `lower` which is the prefered action mode but applies only
# in cases where the `lower` argument is not shared. # in cases where the `lower` argument is not shared.
lower.vTop = upper.vTop lower.vTop = upper.vTop
layersMergeOnto(upper, lower[]) layersMergeOnto(upper, lower[])
result = lower lower
elif not modUpperOk:
# Cannot modify any argument layers.
result = LayerRef(
sTab: lower.sTab, # shallow copy (entries will not be modified)
kMap: lower.kMap,
accLeaves: lower.accLeaves,
stoLeaves: lower.stoLeaves,
vTop: upper.vTop)
layersMergeOnto(upper, result[])
else:
# Otherwise avoid copying some tables by modifying `upper`. This is not
# completely free as the merge direction changes to merging the `lower`
# layer up into the higher prioritised `upper` layer (note that the `lower`
# argument filter is read-only.) Here again, the `upper` argument must not
# be a shared layer/filter.
for (rvid,vtx) in lower.sTab.pairs:
if not upper.sTab.hasKey(rvid):
upper.sTab[rvid] = vtx
for (rvid,key) in lower.kMap.pairs:
if not upper.kMap.hasKey(rvid):
upper.kMap[rvid] = key
for (accPath,leafVtx) in lower.accLeaves.pairs:
if not upper.accLeaves.hasKey(accPath):
upper.accLeaves[accPath] = leafVtx
for (mixPath,leafVtx) in lower.stoLeaves.pairs:
if not upper.stoLeaves.hasKey(mixPath):
upper.stoLeaves[mixPath] = leafVtx
result = upper
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# End # End

View File

@ -1,104 +0,0 @@
# 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, aristo_utils]
# ------------------------------------------------------------------------------
# Private functions
# ------------------------------------------------------------------------------
proc revSubTree(
db: AristoDbRef;
rev: LayerRef;
rvid: RootedVertexID;
): Result[void,(VertexID,AristoError)] =
## Collect subtrees marked for deletion
let
vtx = block:
let rc = db.getVtxUbe rvid
if rc.isOk:
rc.value
elif rc.error == GetVtxNotFound:
VertexRef(nil)
else:
return err((rvid.vid,rc.error))
key = block:
let rc = db.getKeyUbe(rvid, {})
if rc.isOk:
rc.value[0]
elif rc.error == GetKeyNotFound:
VOID_HASH_KEY
else:
return err((rvid.vid,rc.error))
if vtx.isValid:
for vid in vtx.subVids:
? db.revSubTree(rev, (rvid.root,vid))
rev.sTab[rvid] = vtx
if key.isValid:
rev.kMap[rvid] = key
ok()
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
proc revFilter*(
db: AristoDbRef; # Database
filter: LayerRef; # Filter to revert
): Result[LayerRef,(VertexID,AristoError)] =
## Assemble reverse filter for the `filter` argument, i.e. changes to the
## backend that reverse the effect of applying the this read-only filter.
##
## This read-only filter is calculated against the current unfiltered
## backend (excluding optionally installed read-only filter.)
##
let rev = LayerRef()
# Get vid generator state on backend
block:
let rc = db.getTuvUbe()
if rc.isOk:
rev.vTop = rc.value
elif rc.error != GetTuvNotFound:
return err((VertexID(0), rc.error))
# Calculate reverse changes for the `sTab[]` structural table
for rvid in filter.sTab.keys:
let rc = db.getVtxUbe rvid
if rc.isOk:
rev.sTab[rvid] = rc.value
elif rc.error == GetVtxNotFound:
rev.sTab[rvid] = VertexRef(nil)
else:
return err((rvid.vid,rc.error))
# Calculate reverse changes for the `kMap[]` structural table.
for rvid in filter.kMap.keys:
let rc = db.getKeyUbe(rvid, {})
if rc.isOk:
rev.kMap[rvid] = rc.value[0]
elif rc.error == GetKeyNotFound:
rev.kMap[rvid] = VOID_HASH_KEY
else:
return err((rvid.vid,rc.error))
ok(rev)
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------

View File

@ -46,13 +46,6 @@ type
txUid*: uint ## Unique ID among transactions txUid*: uint ## Unique ID among transactions
level*: int ## Stack index for this transaction level*: int ## Stack index for this transaction
DudesRef = ref object
## List of peers accessing the same database. This list is layzily allocated
## and might be kept with a single entry, i.e. so that `{centre} == peers`.
##
centre: AristoDbRef ## Link to peer with write permission
peers: HashSet[AristoDbRef] ## List of all peers
AristoDbRef* = ref object AristoDbRef* = ref object
## Three tier database object supporting distributed instances. ## Three tier database object supporting distributed instances.
top*: LayerRef ## Database working layer, mutable top*: LayerRef ## Database working layer, mutable
@ -62,7 +55,6 @@ type
txRef*: AristoTxRef ## Latest active transaction txRef*: AristoTxRef ## Latest active transaction
txUidGen*: uint ## Tx-relative unique number generator txUidGen*: uint ## Tx-relative unique number generator
dudes: DudesRef ## Related DB descriptors
accLeaves*: LruCache[Hash32, VertexRef] accLeaves*: LruCache[Hash32, VertexRef]
## Account path to payload cache - accounts are frequently accessed by ## Account path to payload cache - accounts are frequently accessed by
@ -160,139 +152,6 @@ func hash*(db: AristoDbRef): Hash =
## Table/KeyedQueue/HashSet mixin ## Table/KeyedQueue/HashSet mixin
cast[pointer](db).hash cast[pointer](db).hash
# ------------------------------------------------------------------------------
# Public functions, `dude` related
# ------------------------------------------------------------------------------
func isCentre*(db: AristoDbRef): bool =
## This function returns `true` is the argument `db` is the centre (see
## comments on `reCentre()` for details.)
##
db.dudes.isNil or db.dudes.centre == db
func getCentre*(db: AristoDbRef): AristoDbRef =
## Get the centre descriptor among all other descriptors accessing the same
## backend database (see comments on `reCentre()` for details.)
##
if db.dudes.isNil: db else: db.dudes.centre
proc reCentre*(db: AristoDbRef): Result[void,AristoError] =
## Re-focus the `db` argument descriptor so that it becomes the centre.
## Nothing is done if the `db` descriptor is the centre, already.
##
## With several descriptors accessing the same backend database there is a
## single one that has write permission for the backend (regardless whether
## there is a backend, at all.) The descriptor entity with write permission
## is called *the centre*.
##
## After invoking `reCentre()`, the argument database `db` can only be
## destructed by `finish()` which also destructs all other descriptors
## accessing the same backend database. Descriptors where `isCentre()`
## returns `false` must be single destructed with `forget()`.
##
if not db.dudes.isNil:
db.dudes.centre = db
ok()
proc fork*(
db: AristoDbRef;
noTopLayer = false;
noFilter = false;
): Result[AristoDbRef,AristoError] =
## 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 `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
## uninitalised and inconsistent (!) descriptor object without top layer.
## This setting avoids some database lookup for cases where the top layer
## is redefined anyway.
##
# Make sure that there is a dudes list
if db.dudes.isNil:
db.dudes = DudesRef(centre: db, peers: @[db].toHashSet)
let clone = AristoDbRef(
dudes: db.dudes,
backend: db.backend,
accLeaves: db.accLeaves,
stoLeaves: db.stoLeaves,
)
if not noFilter:
clone.balancer = db.balancer # Ref is ok here (filters are immutable)
if not noTopLayer:
clone.top = LayerRef.init()
if not db.balancer.isNil:
clone.top.vTop = db.balancer.vTop
else:
let rc = clone.backend.getTuvFn()
if rc.isOk:
clone.top.vTop = rc.value
elif rc.error != GetTuvNotFound:
return err(rc.error)
# Add to peer list of clones
db.dudes.peers.incl clone
ok clone
iterator forked*(db: AristoDbRef): tuple[db: AristoDbRef, isLast: bool] =
## Interate over all non centre descriptors (see comments on `reCentre()`
## for details.)
##
## The second `isLast` yielded loop entry is `true` if the yielded tuple
## is the last entry in the list.
##
if not db.dudes.isNil:
var nLeft = db.dudes.peers.len
for dude in db.dudes.peers.items:
if dude != db.dudes.centre:
nLeft.dec
yield (dude, nLeft == 1)
func nForked*(db: AristoDbRef): int =
## Returns the number of non centre descriptors (see comments on `reCentre()`
## for details.) This function is a fast version of `db.forked.toSeq.len`.
if not db.dudes.isNil:
return db.dudes.peers.len - 1
proc forget*(db: AristoDbRef): Result[void,AristoError] =
## Destruct the non centre argument `db` descriptor (see comments on
## `reCentre()` for details.)
##
## A non centre descriptor should always be destructed after use (see also
## comments on `fork()`.)
##
if db.isCentre:
err(DescNotAllowedOnCentre)
elif db notin db.dudes.peers:
err(DescStaleDescriptor)
else:
db.dudes.peers.excl db # Unlink argument `db` from peers list
ok()
proc forgetOthers*(db: AristoDbRef): Result[void,AristoError] =
## For the centre argument `db` descriptor (see comments on `reCentre()`
## for details), destruct all other descriptors accessing the same backend.
##
if not db.dudes.isNil:
if db.dudes.centre != db:
return err(DescMustBeOnCentre)
db.dudes = DudesRef(nil)
ok()
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Public helpers # Public helpers
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -79,7 +79,6 @@ proc finish*(db: AristoDbRef; eradicate = false) =
## ##
if not db.backend.isNil: if not db.backend.isNil:
db.backend.closeFn eradicate db.backend.closeFn eradicate
discard db.getCentre.forgetOthers()
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# End # End

View File

@ -15,8 +15,8 @@
import import
results, results,
./aristo_tx/[tx_fork, tx_frame, tx_stow], ./aristo_tx/[tx_frame, tx_stow],
"."/[aristo_desc, aristo_get] ./aristo_desc
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Public functions, getters # Public functions, getters
@ -47,124 +47,6 @@ 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*(
db: AristoDbRef;
backLevel: int; # Backward location of transaction
): Result[AristoDbRef,AristoError] =
## Fork a new descriptor obtained from parts of the argument database
## as described by arguments `db` and `backLevel`.
##
## If the argument `backLevel` is non-negative, the forked descriptor will
## provide the database view where the first `backLevel` transaction layers
## are stripped and the remaing layers are squashed into a single transaction.
##
## If `backLevel` is `-1`, a database descriptor with empty transaction
## layers will be provided where the `balancer` between database and
## transaction layers are kept in place.
##
## If `backLevel` is `-2`, a database descriptor with empty transaction
## layers will be provided without a `balancer`.
##
## The returned database descriptor will always have transaction level one.
## If there were no transactions that could be squashed, an empty
## transaction is added.
##
## Use `aristo_desc.forget()` to clean up this descriptor.
##
# Fork top layer (with or without pending transaction)?
if backLevel == 0:
return db.txForkTop()
# Fork bottom layer (=> 0 < db.stack.len)
if backLevel == db.stack.len:
return db.txForkBase()
# Inspect transaction stack
if 0 < backLevel:
var tx = db.txRef
if tx.isNil or db.stack.len < backLevel:
return err(TxLevelTooDeep)
# Fetch tx of level `backLevel` (seed to skip some items)
for _ in 0 ..< backLevel:
tx = tx.parent
if tx.isNil:
return err(TxStackGarbled)
return tx.txFork()
# Plain fork, include `balancer`
if backLevel == -1:
let xb = ? db.fork(noFilter=false)
discard xb.txFrameBegin()
return ok(xb)
# Plain fork, unfiltered backend
if backLevel == -2:
let xb = ? db.fork(noFilter=true)
discard xb.txFrameBegin()
return ok(xb)
err(TxLevelUseless)
proc findTx*(
db: AristoDbRef;
rvid: RootedVertexID; # Pivot vertex (typically `VertexID(1)`)
key: HashKey; # Hash key of pivot vertex
): Result[int,AristoError] =
## Find the transaction where the vertex with ID `vid` exists and has the
## Merkle hash key `key`. If there is no transaction available, search in
## the filter and then in the backend.
##
## If the above procedure succeeds, an integer indicating the transaction
## level integer is returned:
##
## * `0` -- top level, current layer
## * `1`, `2`, ... -- some transaction level further down the stack
## * `-1` -- the filter between transaction stack and database backend
## * `-2` -- the databse backend
##
## A successful return code might be used for the `forkTx()` call for
## creating a forked descriptor that provides the pair `(vid,key)`.
##
if not rvid.isValid or
not key.isValid:
return err(TxArgsUseless)
if db.txRef.isNil:
# Try `(vid,key)` on top layer
let topKey = db.top.kMap.getOrVoid rvid
if topKey == key:
return ok(0)
else:
# Find `(vid,key)` on transaction layers
for (n,tx,layer,error) in db.txRef.txFrameWalk:
if error != AristoError(0):
return err(error)
if layer.kMap.getOrVoid(rvid) == key:
return ok(n)
# Try bottom layer
let botKey = db.stack[0].kMap.getOrVoid rvid
if botKey == key:
return ok(db.stack.len)
# Try `(vid,key)` on balancer
if not db.balancer.isNil:
let roKey = db.balancer.kMap.getOrVoid rvid
if roKey == key:
return ok(-1)
# Try `(vid,key)` on unfiltered backend
block:
let beKey = db.getKeyUbe(rvid, {}).valueOr: (VOID_HASH_KEY, nil)
if beKey[0] == key:
return ok(-2)
err(TxNotFound)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Public functions: Transaction frame # Public functions: Transaction frame
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -1,129 +0,0 @@
# 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.
## Aristo DB -- Transaction fork helpers
## =====================================
##
{.push raises: [].}
import
results,
./tx_frame,
".."/[aristo_desc, aristo_get, aristo_layers]
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
proc txFork*(
tx: AristoTxRef; # Transaction descriptor
): Result[AristoDbRef,AristoError] =
## Clone a transaction into a new DB descriptor accessing the same backend
## database (if any) as the argument `db`. The new descriptor is linked to
## the transaction parent and is fully functional as a forked instance (see
## comments on `aristo_desc.reCentre()` for details.)
##
## 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.)
##
## Use `aristo_desc.forget()` to clean up this descriptor.
##
let db = tx.db
# Verify `tx` argument
if db.txRef == tx:
if db.top.txUid != tx.txUid:
return err(TxArgStaleTx)
elif db.stack.len <= tx.level:
return err(TxArgStaleTx)
elif db.stack[tx.level].txUid != tx.txUid:
return err(TxArgStaleTx)
# Provide new empty stack layer
let stackLayer = block:
let rc = db.getTuvBE()
if rc.isOk:
LayerRef(vTop: rc.value)
elif rc.error == GetTuvNotFound:
LayerRef.init()
else:
return err(rc.error)
# Set up clone associated to `db`
let txClone = ? db.fork(noToplayer = true, noFilter = false)
txClone.top = db.layersCc tx.level # Provide tx level 1 stack
txClone.stack = @[stackLayer] # Zero level stack
txClone.top.txUid = 1
txClone.txUidGen = 1
# Install transaction similar to `tx` on clone
txClone.txRef = AristoTxRef(
db: txClone,
txUid: 1,
level: 1)
ok(txClone)
proc txForkTop*(
db: AristoDbRef;
): Result[AristoDbRef,AristoError] =
## Variant of `forkTx()` for the top transaction if there is any. Otherwise
## 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.
##
if db.txRef.isNil:
let txClone = ? db.fork(noToplayer=true, noFilter=false)
txClone.top = db.layersCc # Is a deep copy
discard txClone.txFrameBegin()
return ok(txClone)
# End if()
db.txRef.txFork()
proc txForkBase*(
db: AristoDbRef;
): Result[AristoDbRef,AristoError] =
## Variant of `forkTx()`, sort of the opposite of `forkTop()`. This is the
## equivalent of top layer forking after all tranactions have been rolled
## back.
##
## Use `aristo_desc.forget()` to clean up this descriptor.
##
if db.txRef.isNil:
return db.txForkTop()
let txClone = ? db.fork(noToplayer=true, noFilter=false)
txClone.top = db.layersCc 0
discard txClone.txFrameBegin()
ok(txClone)
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------

View File

@ -52,8 +52,7 @@ proc txStow*(
# `db.balancer` is `nil`. Also, the `db.balancer` is read-only. In the # `db.balancer` is `nil`. Also, the `db.balancer` is read-only. In the
# case that there are no forked peers one can ignore that restriction as # case that there are no forked peers one can ignore that restriction as
# no balancer is shared. # no balancer is shared.
db.balancer = deltaMerge( db.balancer = deltaMerge(db.top, db.balancer)
db.top, modUpperOk = true, db.balancer, modLowerOk = db.nForked()==0)
# New empty top layer # New empty top layer
db.top = LayerRef(vTop: db.balancer.vTop) db.top = LayerRef(vTop: db.balancer.vTop)

View File

@ -11,9 +11,7 @@
{.push raises: [].} {.push raises: [].}
import import
eth/common,
../../aristo as use_ari, ../../aristo as use_ari,
../../aristo/aristo_desc/desc_identifiers,
../../aristo/[aristo_init/memory_only, aristo_walk], ../../aristo/[aristo_init/memory_only, aristo_walk],
../../kvt as use_kvt, ../../kvt as use_kvt,
../../kvt/[kvt_init/memory_only, kvt_walk], ../../kvt/[kvt_init/memory_only, kvt_walk],
@ -53,41 +51,6 @@ proc newAristoVoidCoreDbRef*(): CoreDbRef =
KvtDbRef.init(use_kvt.VoidBackendRef), KvtDbRef.init(use_kvt.VoidBackendRef),
AristoDbRef.init(use_ari.VoidBackendRef)) AristoDbRef.init(use_ari.VoidBackendRef))
proc newCtxByKey*(
ctx: CoreDbCtxRef;
key: Hash32;
info: static[string];
): CoreDbRc[CoreDbCtxRef] =
const
rvid: RootedVertexID = (VertexID(1),VertexID(1))
let
db = ctx.parent
# Find `(vid,key)` on transaction stack
inx = block:
let rc = db.ariApi.call(findTx, ctx.mpt, rvid, key.to(HashKey))
if rc.isErr:
return err(rc.error.toError info)
rc.value
# Fork MPT descriptor that provides `(vid,key)`
newMpt = block:
let rc = db.ariApi.call(forkTx, ctx.mpt, inx)
if rc.isErr:
return err(rc.error.toError info)
rc.value
# Fork KVT descriptor parallel to `newMpt`
newKvt = block:
let rc = db.kvtApi.call(forkTx, ctx.kvt, inx)
if rc.isErr:
discard db.ariApi.call(forget, newMpt)
return err(rc.error.toError info)
rc.value
# Create new context
ok(db.bless CoreDbCtxRef(kvt: newKvt, mpt: newMpt))
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# End # End
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -12,7 +12,6 @@
import import
chronicles, chronicles,
eth/common,
rocksdb, rocksdb,
results, results,
../../aristo, ../../aristo,

View File

@ -13,15 +13,13 @@
import import
std/typetraits, std/typetraits,
eth/common, eth/common,
"../.."/[constants, errors], "../.."/[constants],
".."/[kvt, aristo], ".."/[kvt, aristo],
./backend/aristo_db,
./base/[api_tracking, base_config, base_desc, base_helpers] ./base/[api_tracking, base_config, base_desc, base_helpers]
export export
CoreDbAccRef, CoreDbAccRef,
CoreDbAccount, CoreDbAccount,
CoreDbApiError,
CoreDbCtxRef, CoreDbCtxRef,
CoreDbErrorCode, CoreDbErrorCode,
CoreDbError, CoreDbError,
@ -70,75 +68,6 @@ proc ctx*(db: CoreDbRef): CoreDbCtxRef =
## ##
db.defCtx db.defCtx
proc newCtxByKey*(ctx: CoreDbCtxRef; root: Hash32): CoreDbRc[CoreDbCtxRef] =
## Create new context derived from a matching transaction of the currently
## active context. If successful, the resulting context has the following
## properties:
##
## * Transaction level is 1
## * The state of the accounts column is equal to the argument `root`
##
## If successful, the resulting descriptor **must** be manually released
## with `forget()` when it is not used, anymore.
##
## Note:
## The underlying `Aristo` backend uses lazy hashing so this function
## might fail simply because there is no computed state when nesting
## the next transaction. If the previous transaction needs to be found,
## then it must called like this:
## ::
## let db = .. # Instantiate CoreDb handle
## ...
## discard db.ctx.getAccounts.state() # Compute state hash
## db.ctx.newTransaction() # Enter new transaction
##
## However, remember that unused hash computations are contle relative
## to processing time.
##
ctx.setTrackNewApi CtxNewCtxByKeyFn
result = ctx.newCtxByKey(root, $api)
ctx.ifTrackNewApi: debug logTxt, api, elapsed, root=($$root), result
proc swapCtx*(ctx: CoreDbCtxRef; db: CoreDbRef): CoreDbCtxRef =
## Activate argument context `ctx` as default and return the previously
## active context. This function goes typically together with `forget()`.
## A valid scenario might look like
## ::
## let db = .. # Instantiate CoreDb handle
## ...
## let ctx = newCtxByKey(..).expect "ctx" # Create new context
## let saved = db.swapCtx ctx # Swap context dandles
## defer: db.swapCtx(saved).forget() # Restore
## ...
##
doAssert not ctx.isNil
assert db.defCtx != ctx # debugging only
db.setTrackNewApi CtxSwapCtxFn
# Swap default context with argument `ctx`
result = db.defCtx
db.defCtx = ctx
# Set read-write access and install
CoreDbAccRef(ctx).call(reCentre, db.ctx.mpt).isOkOr:
raiseAssert $api & " failed: " & $error
CoreDbKvtRef(ctx).call(reCentre, db.ctx.kvt).isOkOr:
raiseAssert $api & " failed: " & $error
doAssert db.defCtx != result
db.ifTrackNewApi: debug logTxt, api, elapsed
proc forget*(ctx: CoreDbCtxRef) =
## Dispose `ctx` argument context and related columns created with this
## context. This function throws an exception `ctx` is the default context.
##
ctx.setTrackNewApi CtxForgetFn
doAssert ctx != ctx.parent.defCtx
CoreDbAccRef(ctx).call(forget, ctx.mpt).isOkOr:
raiseAssert $api & ": " & $error
CoreDbKvtRef(ctx).call(forget, ctx.kvt).isOkOr:
raiseAssert $api & ": " & $error
ctx.ifTrackNewApi: debug logTxt, api, elapsed
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Public base descriptor methods # Public base descriptor methods
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -33,7 +33,6 @@ type
AccClearStorageFn = "clearStorage" AccClearStorageFn = "clearStorage"
AccDeleteFn = "acc/delete" AccDeleteFn = "acc/delete"
AccFetchFn = "acc/fetch" AccFetchFn = "acc/fetch"
AccForgetFn = "acc/forget"
AccHasPathFn = "acc/hasPath" AccHasPathFn = "acc/hasPath"
AccMergeFn = "acc/merge" AccMergeFn = "acc/merge"
AccProofFn = "acc/proof" AccProofFn = "acc/proof"
@ -64,11 +63,8 @@ type
CptPopFn = "pop" CptPopFn = "pop"
CptStopCaptureFn = "stopCapture" CptStopCaptureFn = "stopCapture"
CtxForgetFn = "ctx/forget"
CtxGetAccountsFn = "getAccounts" CtxGetAccountsFn = "getAccounts"
CtxGetGenericFn = "getGeneric" CtxGetGenericFn = "getGeneric"
CtxNewCtxByKeyFn = "newCtxByKey"
CtxSwapCtxFn = "swapCtx"
KvtDelFn = "del" KvtDelFn = "del"
KvtGetFn = "get" KvtGetFn = "get"

View File

@ -12,19 +12,19 @@
import import
std/typetraits, std/typetraits,
eth/common, stint,
../../errors, eth/common/hashes,
../aristo as use_ari, ../aristo as use_ari,
../kvt as use_kvt, ../kvt as use_kvt,
../kvt/[kvt_init/memory_only, kvt_walk],
./base/[api_tracking, base_config, base_desc] ./base/[api_tracking, base_config, base_desc]
export stint, hashes
when CoreDbEnableApiJumpTable: when CoreDbEnableApiJumpTable:
discard discard
else: else:
import import
../aristo/[aristo_desc, aristo_path], ../aristo/[aristo_desc, aristo_path]
../kvt/[kvt_desc, kvt_tx]
when CoreDbEnableApiTracking: when CoreDbEnableApiTracking:
import import
@ -34,20 +34,11 @@ when CoreDbEnableApiTracking:
const const
logTxt = "API" logTxt = "API"
# Annotation helper(s)
{.pragma: apiRaise, gcsafe, raises: [CoreDbApiError].}
template valueOrApiError[U,V](rc: Result[U,V]; info: static[string]): U =
rc.valueOr: raise (ref CoreDbApiError)(msg: info)
template dbType(dsc: CoreDbKvtRef | CoreDbAccRef): CoreDbType = template dbType(dsc: CoreDbKvtRef | CoreDbAccRef): CoreDbType =
dsc.distinctBase.parent.dbType dsc.distinctBase.parent.dbType
# --------------- # ---------------
template kvt(dsc: CoreDbKvtRef): KvtDbRef =
dsc.distinctBase.kvt
template call(api: KvtApiRef; fn: untyped; args: varargs[untyped]): untyped = template call(api: KvtApiRef; fn: untyped; args: varargs[untyped]): untyped =
when CoreDbEnableApiJumpTable: when CoreDbEnableApiJumpTable:
api.fn(args) api.fn(args)
@ -79,25 +70,6 @@ template call(
# Public iterators # Public iterators
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
iterator pairs*(kvt: CoreDbKvtRef): (seq[byte], seq[byte]) {.apiRaise.} =
## Iterator supported on memory DB (otherwise implementation dependent)
##
kvt.setTrackNewApi KvtPairsIt
case kvt.dbType:
of AristoDbMemory:
let p = kvt.call(forkTx, kvt.kvt, 0).valueOrApiError "kvt/pairs()"
defer: discard kvt.call(forget, p)
for (k,v) in use_kvt.MemBackendRef.walkPairs p:
yield (k,v)
of AristoDbVoid:
let p = kvt.call(forkTx, kvt.kvt, 0).valueOrApiError "kvt/pairs()"
defer: discard kvt.call(forget, p)
for (k,v) in use_kvt.VoidBackendRef.walkPairs p:
yield (k,v)
of Ooops, AristoDbRocks:
raiseAssert: "Unsupported database type: " & $kvt.dbType
kvt.ifTrackNewApi: debug logTxt, api, elapsed
iterator slotPairs*(acc: CoreDbAccRef; accPath: Hash32): (seq[byte], UInt256) = iterator slotPairs*(acc: CoreDbAccRef; accPath: Hash32): (seq[byte], UInt256) =
acc.setTrackNewApi AccSlotPairsIt acc.setTrackNewApi AccSlotPairsIt
case acc.dbType: case acc.dbType:

View File

@ -11,7 +11,6 @@
{.push raises: [].} {.push raises: [].}
import import
eth/common,
../aristo, ../aristo,
./backend/aristo_db, ./backend/aristo_db,
./base/base_config, ./base/base_config,
@ -25,7 +24,6 @@ export
base, base,
base_config, base_config,
base_iterators, base_iterators,
common,
core_apps core_apps
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -13,7 +13,6 @@
import import
std/times, std/times,
eth/common,
results, results,
../aristo/aristo_profile, ../aristo/aristo_profile,
./kvt_desc/desc_backend, ./kvt_desc/desc_backend,
@ -46,21 +45,16 @@ type
key: openArray[byte]): Result[void,KvtError] {.noRaise.} key: openArray[byte]): Result[void,KvtError] {.noRaise.}
KvtApiFinishFn* = proc(db: KvtDbRef, eradicate = false) {.noRaise.} KvtApiFinishFn* = proc(db: KvtDbRef, eradicate = false) {.noRaise.}
KvtApiForgetFn* = proc(db: KvtDbRef): Result[void,KvtError] {.noRaise.} KvtApiForgetFn* = proc(db: KvtDbRef): Result[void,KvtError] {.noRaise.}
KvtApiForkTxFn* = proc(db: KvtDbRef,
backLevel: int): Result[KvtDbRef,KvtError] {.noRaise.}
KvtApiGetFn* = proc(db: KvtDbRef, KvtApiGetFn* = proc(db: KvtDbRef,
key: openArray[byte]): Result[seq[byte],KvtError] {.noRaise.} key: openArray[byte]): Result[seq[byte],KvtError] {.noRaise.}
KvtApiLenFn* = proc(db: KvtDbRef, KvtApiLenFn* = proc(db: KvtDbRef,
key: openArray[byte]): Result[int,KvtError] {.noRaise.} key: openArray[byte]): Result[int,KvtError] {.noRaise.}
KvtApiHasKeyRcFn* = proc(db: KvtDbRef, KvtApiHasKeyRcFn* = proc(db: KvtDbRef,
key: openArray[byte]): Result[bool,KvtError] {.noRaise.} key: openArray[byte]): Result[bool,KvtError] {.noRaise.}
KvtApiIsCentreFn* = proc(db: KvtDbRef): bool {.noRaise.}
KvtApiIsTopFn* = proc(tx: KvtTxRef): bool {.noRaise.} KvtApiIsTopFn* = proc(tx: KvtTxRef): bool {.noRaise.}
KvtApiLevelFn* = proc(db: KvtDbRef): int {.noRaise.} KvtApiLevelFn* = proc(db: KvtDbRef): int {.noRaise.}
KvtApiNForkedFn* = proc(db: KvtDbRef): int {.noRaise.}
KvtApiPutFn* = proc(db: KvtDbRef, KvtApiPutFn* = proc(db: KvtDbRef,
key, data: openArray[byte]): Result[void,KvtError] {.noRaise.} key, data: openArray[byte]): Result[void,KvtError] {.noRaise.}
KvtApiReCentreFn* = proc(db: KvtDbRef): Result[void,KvtError] {.noRaise.}
KvtApiRollbackFn* = proc(tx: KvtTxRef): Result[void,KvtError] {.noRaise.} KvtApiRollbackFn* = proc(tx: KvtTxRef): Result[void,KvtError] {.noRaise.}
KvtApiPersistFn* = proc(db: KvtDbRef): Result[void,KvtError] {.noRaise.} KvtApiPersistFn* = proc(db: KvtDbRef): Result[void,KvtError] {.noRaise.}
KvtApiToKvtDbRefFn* = proc(tx: KvtTxRef): KvtDbRef {.noRaise.} KvtApiToKvtDbRefFn* = proc(tx: KvtTxRef): KvtDbRef {.noRaise.}
@ -75,17 +69,12 @@ type
commit*: KvtApiCommitFn commit*: KvtApiCommitFn
del*: KvtApiDelFn del*: KvtApiDelFn
finish*: KvtApiFinishFn finish*: KvtApiFinishFn
forget*: KvtApiForgetFn
forkTx*: KvtApiForkTxFn
get*: KvtApiGetFn get*: KvtApiGetFn
len*: KvtApiLenFn len*: KvtApiLenFn
hasKeyRc*: KvtApiHasKeyRcFn hasKeyRc*: KvtApiHasKeyRcFn
isCentre*: KvtApiIsCentreFn
isTop*: KvtApiIsTopFn isTop*: KvtApiIsTopFn
level*: KvtApiLevelFn level*: KvtApiLevelFn
nForked*: KvtApiNForkedFn
put*: KvtApiPutFn put*: KvtApiPutFn
reCentre*: KvtApiReCentreFn
rollback*: KvtApiRollbackFn rollback*: KvtApiRollbackFn
persist*: KvtApiPersistFn persist*: KvtApiPersistFn
toKvtDbRef*: KvtApiToKvtDbRefFn toKvtDbRef*: KvtApiToKvtDbRefFn
@ -100,17 +89,12 @@ type
KvtApiProfCommitFn = "commit" KvtApiProfCommitFn = "commit"
KvtApiProfDelFn = "del" KvtApiProfDelFn = "del"
KvtApiProfFinishFn = "finish" KvtApiProfFinishFn = "finish"
KvtApiProfForgetFn = "forget"
KvtApiProfForkTxFn = "forkTx"
KvtApiProfGetFn = "get" KvtApiProfGetFn = "get"
KvtApiProfLenFn = "len" KvtApiProfLenFn = "len"
KvtApiProfHasKeyRcFn = "hasKeyRc" KvtApiProfHasKeyRcFn = "hasKeyRc"
KvtApiProfIsCentreFn = "isCentre"
KvtApiProfIsTopFn = "isTop" KvtApiProfIsTopFn = "isTop"
KvtApiProfLevelFn = "level" KvtApiProfLevelFn = "level"
KvtApiProfNForkedFn = "nForked"
KvtApiProfPutFn = "put" KvtApiProfPutFn = "put"
KvtApiProfReCentreFn = "reCentre"
KvtApiProfRollbackFn = "rollback" KvtApiProfRollbackFn = "rollback"
KvtApiProfPersistFn = "persist" KvtApiProfPersistFn = "persist"
KvtApiProfToKvtDbRefFn = "toKvtDbRef" KvtApiProfToKvtDbRefFn = "toKvtDbRef"
@ -136,16 +120,11 @@ when AutoValidateApiHooks:
doAssert not api.commit.isNil doAssert not api.commit.isNil
doAssert not api.del.isNil doAssert not api.del.isNil
doAssert not api.finish.isNil doAssert not api.finish.isNil
doAssert not api.forget.isNil
doAssert not api.forkTx.isNil
doAssert not api.get.isNil doAssert not api.get.isNil
doAssert not api.hasKeyRc.isNil doAssert not api.hasKeyRc.isNil
doAssert not api.isCentre.isNil
doAssert not api.isTop.isNil doAssert not api.isTop.isNil
doAssert not api.level.isNil doAssert not api.level.isNil
doAssert not api.nForked.isNil
doAssert not api.put.isNil doAssert not api.put.isNil
doAssert not api.reCentre.isNil
doAssert not api.rollback.isNil doAssert not api.rollback.isNil
doAssert not api.persist.isNil doAssert not api.persist.isNil
doAssert not api.toKvtDbRef.isNil doAssert not api.toKvtDbRef.isNil
@ -178,17 +157,12 @@ func init*(api: var KvtApiObj) =
api.commit = commit api.commit = commit
api.del = del api.del = del
api.finish = finish api.finish = finish
api.forget = forget
api.forkTx = forkTx
api.get = get api.get = get
api.len = len api.len = len
api.hasKeyRc = hasKeyRc api.hasKeyRc = hasKeyRc
api.isCentre = isCentre
api.isTop = isTop api.isTop = isTop
api.level = level api.level = level
api.nForked = nForked
api.put = put api.put = put
api.reCentre = reCentre
api.rollback = rollback api.rollback = rollback
api.persist = persist api.persist = persist
api.toKvtDbRef = toKvtDbRef api.toKvtDbRef = toKvtDbRef
@ -206,17 +180,12 @@ func dup*(api: KvtApiRef): KvtApiRef =
commit: api.commit, commit: api.commit,
del: api.del, del: api.del,
finish: api.finish, finish: api.finish,
forget: api.forget,
forkTx: api.forkTx,
get: api.get, get: api.get,
len: api.len, len: api.len,
hasKeyRc: api.hasKeyRc, hasKeyRc: api.hasKeyRc,
isCentre: api.isCentre,
isTop: api.isTop, isTop: api.isTop,
level: api.level, level: api.level,
nForked: api.nForked,
put: api.put, put: api.put,
reCentre: api.reCentre,
rollback: api.rollback, rollback: api.rollback,
persist: api.persist, persist: api.persist,
toKvtDbRef: api.toKvtDbRef, toKvtDbRef: api.toKvtDbRef,
@ -267,16 +236,6 @@ func init*(
KvtApiProfFinishFn.profileRunner: KvtApiProfFinishFn.profileRunner:
api.finish(a, b) api.finish(a, b)
profApi.forget =
proc(a: KvtDbRef): auto =
KvtApiProfForgetFn.profileRunner:
result = api.forget(a)
profApi.forkTx =
proc(a: KvtDbRef, b: int): auto =
KvtApiProfForkTxFn.profileRunner:
result = api.forkTx(a, b)
profApi.get = profApi.get =
proc(a: KvtDbRef, b: openArray[byte]): auto = proc(a: KvtDbRef, b: openArray[byte]): auto =
KvtApiProfGetFn.profileRunner: KvtApiProfGetFn.profileRunner:
@ -292,11 +251,6 @@ func init*(
KvtApiProfHasKeyRcFn.profileRunner: KvtApiProfHasKeyRcFn.profileRunner:
result = api.hasKeyRc(a, b) result = api.hasKeyRc(a, b)
profApi.isCentre =
proc(a: KvtDbRef): auto =
KvtApiProfIsCentreFn.profileRunner:
result = api.isCentre(a)
profApi.isTop = profApi.isTop =
proc(a: KvtTxRef): auto = proc(a: KvtTxRef): auto =
KvtApiProfIsTopFn.profileRunner: KvtApiProfIsTopFn.profileRunner:
@ -307,21 +261,11 @@ func init*(
KvtApiProfLevelFn.profileRunner: KvtApiProfLevelFn.profileRunner:
result = api.level(a) result = api.level(a)
profApi.nForked =
proc(a: KvtDbRef): auto =
KvtApiProfNForkedFn.profileRunner:
result = api.nForked(a)
profApi.put = profApi.put =
proc(a: KvtDbRef; b, c: openArray[byte]): auto = proc(a: KvtDbRef; b, c: openArray[byte]): auto =
KvtApiProfPutFn.profileRunner: KvtApiProfPutFn.profileRunner:
result = api.put(a, b, c) result = api.put(a, b, c)
profApi.reCentre =
proc(a: KvtDbRef): auto =
KvtApiProfReCentreFn.profileRunner:
result = api.reCentre(a)
profApi.rollback = profApi.rollback =
proc(a: KvtTxRef): auto = proc(a: KvtTxRef): auto =
KvtApiProfRollbackFn.profileRunner: KvtApiProfRollbackFn.profileRunner:

View File

@ -12,7 +12,6 @@
import import
std/[algorithm, sequtils, strutils, tables], std/[algorithm, sequtils, strutils, tables],
eth/common,
results, results,
stew/byteutils, stew/byteutils,
./kvt_desc/desc_backend, ./kvt_desc/desc_backend,

View File

@ -16,8 +16,7 @@ import
std/tables, std/tables,
results, results,
./kvt_desc, ./kvt_desc,
./kvt_desc/desc_backend, ./kvt_desc/desc_backend
./kvt_delta/[delta_merge, delta_reverse]
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Public functions # Public functions
@ -25,12 +24,11 @@ import
proc deltaPersistentOk*(db: KvtDbRef): bool = proc deltaPersistentOk*(db: KvtDbRef): bool =
## Check whether the balancer filter can be merged into the backend ## Check whether the balancer filter can be merged into the backend
not db.backend.isNil and db.isCentre not db.backend.isNil
proc deltaPersistent*( proc deltaPersistent*(
db: KvtDbRef; # Database db: KvtDbRef; # Database
reCentreOk = false;
): Result[void,KvtError] = ): Result[void,KvtError] =
## Resolve (i.e. move) the backend filter into the physical backend database. ## Resolve (i.e. move) the backend filter into the physical backend database.
## ##
@ -50,32 +48,6 @@ proc deltaPersistent*(
if db.balancer.isNil: if db.balancer.isNil:
return ok() return ok()
# Make sure that the argument `db` is at the centre so the backend is in
# read-write mode for this peer.
let parent = db.getCentre
if db != parent:
if not reCentreOk:
return err(FilBackendRoMode)
? db.reCentre()
# Always re-centre to `parent` (in case `reCentreOk` was set)
defer: discard parent.reCentre()
# Update forked balancers here do that errors are detected early (if any.)
if 0 < db.nForked:
let rev = db.revFilter(db.balancer).valueOr:
return err(error[1])
if 0 < rev.sTab.len: # Can an empty `rev` happen at all?
var unsharedRevOk = true
for w in db.forked:
if not w.db.balancer.isValid:
unsharedRevOk = false
# The `rev` filter can be modified if one can make sure that it is
# not shared (i.e. only previously merged into the w.db.balancer.)
# Note that it is trivially true for a single fork.
let modLowerOk = w.isLast and unsharedRevOk
w.db.balancer = deltaMerge(
w.db.balancer, modUpperOk=false, rev, modLowerOk=modLowerOk)
# Store structural single trie entries # Store structural single trie entries
let writeBatch = ? be.putBegFn() let writeBatch = ? be.putBegFn()
for k,v in db.balancer.sTab: for k,v in db.balancer.sTab:

View File

@ -9,7 +9,6 @@
# except according to those terms. # except according to those terms.
import import
std/tables,
../kvt_desc ../kvt_desc
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -26,9 +25,7 @@ proc layersMergeOnto(src: LayerRef; trg: var LayerObj) =
proc deltaMerge*( proc deltaMerge*(
upper: LayerRef; # Think of `top`, `nil` is ok upper: LayerRef; # Think of `top`, `nil` is ok
modUpperOk: bool; # May re-use/modify `upper`
lower: LayerRef; # Think of `balancer`, `nil` is ok lower: LayerRef; # Think of `balancer`, `nil` is ok
modLowerOk: bool; # May re-use/modify `lower`
): LayerRef = ): LayerRef =
## Merge argument `upper` into the `lower` filter instance. ## Merge argument `upper` into the `lower` filter instance.
## ##
@ -37,33 +34,17 @@ proc deltaMerge*(
## ##
if lower.isNil: if lower.isNil:
# Degenerate case: `upper` is void # Degenerate case: `upper` is void
result = upper upper
elif upper.isNil: elif upper.isNil:
# Degenerate case: `lower` is void # Degenerate case: `lower` is void
result = lower lower
elif modLowerOk: else:
# Can modify `lower` which is the prefered action mode but applies only # Can modify `lower` which is the prefered action mode but applies only
# in cases where the `lower` argument is not shared. # in cases where the `lower` argument is not shared.
layersMergeOnto(upper, lower[]) layersMergeOnto(upper, lower[])
result = lower lower
elif not modUpperOk:
# Cannot modify any argument layers.
result = LayerRef(sTab: lower.sTab)
layersMergeOnto(upper, result[])
else:
# Otherwise avoid copying some tables by modifyinh `upper`. This is not
# completely free as the merge direction changes to merging the `lower`
# layer up into the higher prioritised `upper` layer (note that the `lower`
# argument filter is read-only.) Here again, the `upper` argument must not
# be a shared layer/filter.
for (key,val) in lower.sTab.pairs:
if not upper.sTab.hasKey(key):
upper.sTab[key] = val
result = upper
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# End # End

View File

@ -1,47 +0,0 @@
# 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,
".."/[kvt_desc, kvt_utils]
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
proc revFilter*(
db: KvtDbRef; # Database
filter: LayerRef; # Filter to revert
): Result[LayerRef,(seq[byte],KvtError)] =
## Assemble reverse filter for the `filter` argument, i.e. changes to the
## backend that reverse the effect of applying the this read-only filter.
##
## This read-only filter is calculated against the current unfiltered
## backend (excluding optionally installed read-only filter.)
##
let rev = LayerRef()
# Calculate reverse changes for the `sTab[]` structural table
for key in filter.sTab.keys:
let rc = db.getUbe key
if rc.isOk:
rev.sTab[key] = rc.value
elif rc.error == GetNotFound:
rev.sTab[key] = EmptyBlob
else:
return err((key,rc.error))
ok(rev)
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------

View File

@ -14,9 +14,7 @@
{.push raises: [].} {.push raises: [].}
import import
std/[hashes, sets, tables], std/[hashes, tables],
eth/common,
results,
./kvt_constants, ./kvt_constants,
./kvt_desc/[desc_error, desc_structural] ./kvt_desc/[desc_error, desc_structural]
@ -25,7 +23,7 @@ from ./kvt_desc/desc_backend
# Not auto-exporting backend # Not auto-exporting backend
export export
kvt_constants, desc_error, desc_structural tables, kvt_constants, desc_error, desc_structural
type type
KvtTxRef* = ref object KvtTxRef* = ref object
@ -35,13 +33,6 @@ type
txUid*: uint ## Unique ID among transactions txUid*: uint ## Unique ID among transactions
level*: int ## Stack index for this transaction level*: int ## Stack index for this transaction
DudesRef = ref object
## List of peers accessing the same database. This list is layzily
## allocated and might be kept with a single entry, i.e. so that
## `{centre} == peers`.
centre: KvtDbRef ## Link to peer with write permission
peers: HashSet[KvtDbRef] ## List of all peers
KvtDbRef* = ref object of RootRef KvtDbRef* = ref object of RootRef
## Three tier database object supporting distributed instances. ## Three tier database object supporting distributed instances.
top*: LayerRef ## Database working layer, mutable top*: LayerRef ## Database working layer, mutable
@ -51,7 +42,6 @@ type
txRef*: KvtTxRef ## Latest active transaction txRef*: KvtTxRef ## Latest active transaction
txUidGen*: uint ## Tx-relative unique number generator txUidGen*: uint ## Tx-relative unique number generator
dudes: DudesRef ## Related DB descriptors
# Debugging data below, might go away in future # Debugging data below, might go away in future
xIdGen*: uint64 xIdGen*: uint64
@ -61,17 +51,6 @@ type
KvtDbAction* = proc(db: KvtDbRef) {.gcsafe, raises: [].} KvtDbAction* = proc(db: KvtDbRef) {.gcsafe, raises: [].}
## Generic call back function/closure. ## Generic call back function/closure.
# ------------------------------------------------------------------------------
# Private helpers
# ------------------------------------------------------------------------------
proc canMod(db: KvtDbRef): Result[void,KvtError] =
## Ask for permission before doing nasty stuff
if db.backend.isNil:
ok()
else:
db.backend.canModFn()
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Public helpers # Public helpers
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -98,120 +77,6 @@ func hash*(db: KvtDbRef): Hash =
# Public functions, `dude` related # Public functions, `dude` related
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
func isCentre*(db: KvtDbRef): bool =
## This function returns `true` is the argument `db` is the centre (see
## comments on `reCentre()` for details.)
##
db.dudes.isNil or db.dudes.centre == db
func getCentre*(db: KvtDbRef): KvtDbRef =
## Get the centre descriptor among all other descriptors accessing the same
## backend database (see comments on `reCentre()` for details.)
##
if db.dudes.isNil: db else: db.dudes.centre
proc reCentre*(db: KvtDbRef): Result[void,KvtError] =
## Re-focus the `db` argument descriptor so that it becomes the centre.
## Nothing is done if the `db` descriptor is the centre, already.
##
## With several descriptors accessing the same backend database there is a
## single one that has write permission for the backend (regardless whether
## there is a backend, at all.) The descriptor entity with write permission
## is called *the centre*.
##
## After invoking `reCentre()`, the argument database `db` can only be
## destructed by `finish()` which also destructs all other descriptors
## accessing the same backend database. Descriptors where `isCentre()`
## returns `false` must be single destructed with `forget()`.
##
if not db.dudes.isNil and db.dudes.centre != db:
? db.canMod()
db.dudes.centre = db
ok()
proc fork*(
db: KvtDbRef;
noTopLayer = false;
noFilter = false;
): Result[KvtDbRef,KvtError] =
## 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 `noFilter` is set `true` the function will fork directly
## off the backend database and ignore any filter.
##
# Make sure that there is a dudes list
if db.dudes.isNil:
db.dudes = DudesRef(centre: db, peers: @[db].toHashSet)
let clone = KvtDbRef(
backend: db.backend,
dudes: db.dudes)
if not noFilter:
clone.balancer = db.balancer # Ref is ok here (filters are immutable)
if not noTopLayer:
clone.top = LayerRef.init()
# Add to peer list of clones
db.dudes.peers.incl clone
ok clone
iterator forked*(db: KvtDbRef): tuple[db: KvtDbRef, isLast: bool] =
## Interate over all non centre descriptors (see comments on `reCentre()`
## for details.)
##
## The second `isLast` yielded loop entry is `true` if the yielded tuple
## is the last entry in the list.
if not db.dudes.isNil:
var nLeft = db.dudes.peers.len
for dude in db.dudes.peers.items:
if dude != db.dudes.centre:
nLeft.dec
yield (dude, nLeft == 1)
func nForked*(db: KvtDbRef): int =
## Returns the number of non centre descriptors (see comments on `reCentre()`
## for details.) This function is a fast version of `db.forked.toSeq.len`.
if not db.dudes.isNil:
return db.dudes.peers.len - 1
proc forget*(db: KvtDbRef): Result[void,KvtError] =
## Destruct the non centre argument `db` descriptor (see comments on
## `reCentre()` for details.)
##
## A non centre descriptor should always be destructed after use (see also
## comments on `fork()`.)
##
if db.isCentre:
err(NotAllowedOnCentre)
elif db notin db.dudes.peers:
err(StaleDescriptor)
else:
? db.canMod()
db.dudes.peers.excl db # Unlink argument `db` from peers list
ok()
proc forgetOthers*(db: KvtDbRef): Result[void,KvtError] =
## For the centre argument `db` descriptor (see comments on `reCentre()`
## for details), release all other descriptors accessing the same backend.
##
if not db.dudes.isNil:
if db.dudes.centre != db:
return err(MustBeOnCentre)
? db.canMod()
db.dudes = DudesRef(nil)
ok()
iterator rstack*(db: KvtDbRef): LayerRef = iterator rstack*(db: KvtDbRef): LayerRef =
# Stack in reverse order # Stack in reverse order
for i in 0..<db.stack.len: for i in 0..<db.stack.len:

View File

@ -15,7 +15,6 @@
{.push raises: [].} {.push raises: [].}
import import
eth/common,
results, results,
./desc_error ./desc_error
@ -56,12 +55,6 @@ type
## `false` the outcome might differ depending on the type of backend ## `false` the outcome might differ depending on the type of backend
## (e.g. in-memory backends would eradicate on close.) ## (e.g. in-memory backends would eradicate on close.)
CanModFn* =
proc(): Result[void,KvtError] {.gcsafe, raises: [].}
## This function returns OK if there is nothing to prevent the main
## `KVT` descriptors being modified (e.g. by `reCentre()`) or by
## adding/removing a new peer (e.g. by `fork()` or `forget()`.)
SetWrReqFn* = SetWrReqFn* =
proc(db: RootRef): Result[void,KvtError] {.gcsafe, raises: [].} proc(db: RootRef): Result[void,KvtError] {.gcsafe, raises: [].}
## This function stores a request function for the piggiback mode ## This function stores a request function for the piggiback mode
@ -86,7 +79,6 @@ type
putEndFn*: PutEndFn ## Commit bulk store session putEndFn*: PutEndFn ## Commit bulk store session
closeFn*: CloseFn ## Generic destructor closeFn*: CloseFn ## Generic destructor
canModFn*: CanModFn ## Lock-alike
setWrReqFn*: SetWrReqFn ## Register main descr for write request setWrReqFn*: SetWrReqFn ## Register main descr for write request
@ -97,7 +89,6 @@ proc init*(trg: var BackendObj; src: BackendObj) =
trg.putKvpFn = src.putKvpFn trg.putKvpFn = src.putKvpFn
trg.putEndFn = src.putEndFn trg.putEndFn = src.putEndFn
trg.closeFn = src.closeFn trg.closeFn = src.closeFn
trg.canModFn = src.canModFn
trg.setWrReqFn = src.setWrReqFn trg.setWrReqFn = src.setWrReqFn
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -16,6 +16,8 @@
import import
std/tables std/tables
export tables
type type
LayerRef* = ref LayerObj LayerRef* = ref LayerObj
LayerObj* = object LayerObj* = object

View File

@ -129,11 +129,6 @@ proc closeFn(db: MemBackendRef): CloseFn =
proc(ignore: bool) = proc(ignore: bool) =
discard discard
proc canModFn(db: MemBackendRef): CanModFn =
result =
proc(): Result[void,KvtError] =
ok()
proc setWrReqFn(db: MemBackendRef): SetWrReqFn = proc setWrReqFn(db: MemBackendRef): SetWrReqFn =
result = result =
proc(kvt: RootRef): Result[void,KvtError] = proc(kvt: RootRef): Result[void,KvtError] =
@ -156,16 +151,9 @@ proc memoryBackend*: BackendRef =
db.putEndFn = putEndFn db db.putEndFn = putEndFn db
db.closeFn = closeFn db db.closeFn = closeFn db
db.canModFn = canModFn db
db.setWrReqFn = setWrReqFn db db.setWrReqFn = setWrReqFn db
db db
proc dup*(db: MemBackendRef): MemBackendRef =
## Duplicate descriptor shell as needed for API debugging
new result
init_common.init(result[], db[])
result.mdb = db.mdb
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Public iterators (needs direct backend access) # Public iterators (needs direct backend access)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -73,7 +73,6 @@ proc finish*(db: KvtDbRef; eradicate = false) =
## ##
if not db.backend.isNil: if not db.backend.isNil:
db.backend.closeFn eradicate db.backend.closeFn eradicate
discard db.getCentre.forgetOthers()
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# End # End

View File

@ -28,7 +28,6 @@
import import
chronicles, chronicles,
eth/common,
rocksdb, rocksdb,
results, results,
../../aristo/aristo_init/persistent, ../../aristo/aristo_init/persistent,
@ -153,11 +152,6 @@ proc closeFn(db: RdbBackendRef): CloseFn =
proc(eradicate: bool) = proc(eradicate: bool) =
db.rdb.destroy(eradicate) db.rdb.destroy(eradicate)
proc canModFn(db: RdbBackendRef): CanModFn =
result =
proc(): Result[void,KvtError] =
ok()
proc setWrReqFn(db: RdbBackendRef): SetWrReqFn = proc setWrReqFn(db: RdbBackendRef): SetWrReqFn =
result = result =
proc(kvt: RootRef): Result[void,KvtError] = proc(kvt: RootRef): Result[void,KvtError] =
@ -206,15 +200,6 @@ proc closeTriggeredFn(db: RdbBackendRef): CloseFn =
# Nothing to do here as we do not own the backend # Nothing to do here as we do not own the backend
discard discard
proc canModTriggeredFn(db: RdbBackendRef): CanModFn =
## Variant of `canModFn()` for piggyback write batch
result =
proc(): Result[void,KvtError] =
# Deny modifications/changes if there is a pending write request
if not db.rdb.delayedPersist.isNil:
return err(RdbBeDelayedLocked)
ok()
proc setWrReqTriggeredFn(db: RdbBackendRef): SetWrReqFn = proc setWrReqTriggeredFn(db: RdbBackendRef): SetWrReqFn =
result = result =
proc(kvt: RootRef): Result[void,KvtError] = proc(kvt: RootRef): Result[void,KvtError] =
@ -291,7 +276,6 @@ proc rocksDbKvtBackend*(
db.putEndFn = putEndFn db db.putEndFn = putEndFn db
db.closeFn = closeFn db db.closeFn = closeFn db
db.canModFn = canModFn db
db.setWrReqFn = setWrReqFn db db.setWrReqFn = setWrReqFn db
ok db ok db
@ -321,16 +305,9 @@ proc rocksDbKvtTriggeredBackend*(
db.putEndFn = putEndTriggeredFn db db.putEndFn = putEndTriggeredFn db
db.closeFn = closeTriggeredFn db db.closeFn = closeTriggeredFn db
db.canModFn = canModTriggeredFn db
db.setWrReqFn = setWrReqTriggeredFn db db.setWrReqFn = setWrReqTriggeredFn db
ok db ok db
proc dup*(db: RdbBackendRef): RdbBackendRef =
new result
init_common.init(result[], db[])
result.rdb = db.rdb
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Public iterators (needs direct backend access) # Public iterators (needs direct backend access)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -18,6 +18,8 @@ import
../../kvt_desc, ../../kvt_desc,
rocksdb rocksdb
export rocksdb
type type
RdbInst* = object RdbInst* = object
store*: KvtCfStore ## Rocks DB database handler store*: KvtCfStore ## Rocks DB database handler

View File

@ -14,8 +14,6 @@
{.push raises: [].} {.push raises: [].}
import import
eth/common,
rocksdb,
results, results,
"../.."/[kvt_constants, kvt_desc], "../.."/[kvt_constants, kvt_desc],
./rdb_desc ./rdb_desc

View File

@ -15,7 +15,6 @@
import import
std/[sequtils, os], std/[sequtils, os],
rocksdb,
results, results,
../../../opts, ../../../opts,
../../kvt_desc, ../../kvt_desc,

View File

@ -14,8 +14,6 @@
{.push raises: [].} {.push raises: [].}
import import
eth/common,
rocksdb,
results, results,
../../kvt_desc, ../../kvt_desc,
./rdb_desc ./rdb_desc

View File

@ -14,8 +14,6 @@
{.push raises: [].} {.push raises: [].}
import import
eth/common,
rocksdb,
./rdb_desc ./rdb_desc
const const

View File

@ -12,7 +12,6 @@
import import
std/[sequtils, sets, tables], std/[sequtils, sets, tables],
eth/common,
results, results,
./kvt_desc ./kvt_desc

View File

@ -15,7 +15,7 @@
import import
results, results,
./kvt_tx/[tx_fork, tx_frame, tx_stow], ./kvt_tx/[tx_frame, tx_stow],
./kvt_init/memory_only, ./kvt_init/memory_only,
./kvt_desc ./kvt_desc
@ -52,65 +52,6 @@ func toKvtDbRef*(tx: KvtTxRef): KvtDbRef =
## Same as `.to(KvtDbRef)` ## Same as `.to(KvtDbRef)`
tx.db tx.db
proc forkTx*(
db: KvtDbRef;
backLevel: int; # Backward location of transaction
): Result[KvtDbRef,KvtError] =
## Fork a new descriptor obtained from parts of the argument database
## as described by arguments `db` and `backLevel`.
##
## If the argument `backLevel` is non-negative, the forked descriptor will
## provide the database view where the first `backLevel` transaction layers
## are stripped and the remaing layers are squashed into a single transaction.
##
## If `backLevel` is `-1`, a database descriptor with empty transaction
## layers will be provided where the `roFilter` between database and
## transaction layers are kept in place.
##
## If `backLevel` is `-2`, a database descriptor with empty transaction
## layers will be provided without an `roFilter`.
##
## The returned database descriptor will always have transaction level one.
## If there were no transactions that could be squashed, an empty
## transaction is added.
##
## Use `kvt_desc.forget()` to clean up this descriptor.
##
# Fork top layer (with or without pending transaction)?
if backLevel == 0:
return db.txForkTop()
# Fork bottom layer (=> 0 < db.stack.len)
if backLevel == db.stack.len:
return db.txForkBase()
# Inspect transaction stack
if 0 < backLevel:
var tx = db.txRef
if tx.isNil or db.stack.len < backLevel:
return err(TxLevelTooDeep)
# Fetch tx of level `backLevel` (seed to skip some items)
for _ in 0 ..< backLevel:
tx = tx.parent
if tx.isNil:
return err(TxStackGarbled)
return tx.txFork()
# Plain fork, include `roFilter`
if backLevel == -1:
let xb = ? db.fork(noFilter=false)
discard xb.txFrameBegin()
return ok(xb)
# Plain fork, unfiltered backend
if backLevel == -2:
let xb = ? db.fork(noFilter=true)
discard xb.txFrameBegin()
return ok(xb)
err(TxLevelUseless)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Public functions: Transaction frame # Public functions: Transaction frame
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -1,95 +0,0 @@
# 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.
## Kvt DB -- Transaction fork helpers
## ==================================
##
{.push raises: [].}
import
results,
./tx_frame,
".."/[kvt_desc, kvt_layers]
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
proc txFork*(tx: KvtTxRef): Result[KvtDbRef,KvtError] =
## Clone a transaction into a new DB descriptor accessing the same backend
## (if any) database as the argument `db`. The new descriptor is linked to
## the transaction parent and is fully functional as a forked instance (see
## comments on `kvt_desc.reCentre()` for details.)
##
## 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.)
##
## Use `kvt_desc.forget()` to clean up this descriptor.
##
let db = tx.db
# Verify `tx` argument
if db.txRef == tx:
if db.top.txUid != tx.txUid:
return err(TxArgStaleTx)
elif db.stack.len <= tx.level:
return err(TxArgStaleTx)
elif db.stack[tx.level].txUid != tx.txUid:
return err(TxArgStaleTx)
# Set up clone associated to `db`
let txClone = ? db.fork()
txClone.top = db.layersCc tx.level
txClone.stack = @[LayerRef.init()] # Provide tx level 1 stack
txClone.top.txUid = 1
txClone.txUidGen = 1 # Used value of `txClone.top.txUid`
# Install transaction similar to `tx` on clone
txClone.txRef = KvtTxRef(
db: txClone,
txUid: 1,
level: 1)
ok(txClone)
proc txForkTop*(db: KvtDbRef): Result[KvtDbRef,KvtError] =
## Variant of `forkTx()` for the top transaction if there is any. Otherwise
## 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.
##
if db.txRef.isNil:
let dbClone = ? db.fork(noToplayer=true)
dbClone.top = db.layersCc()
discard dbClone.txFrameBegin()
return ok(dbClone)
db.txRef.txFork
proc txForkBase*(
db: KvtDbRef;
): Result[KvtDbRef,KvtError] =
if db.txRef.isNil:
return db.txForkTop()
let txClone = ? db.fork(noToplayer=true, noFilter=false)
txClone.top = db.layersCc 0
discard txClone.txFrameBegin()
ok(txClone)
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------

View File

@ -53,8 +53,7 @@ proc txStow*(
# `db.balancer` is `nil`. Also, the `db.balancer` is read-only. In the # `db.balancer` is `nil`. Also, the `db.balancer` is read-only. In the
# case that there are no forked peers one can ignore that restriction as # case that there are no forked peers one can ignore that restriction as
# no balancer is shared. # no balancer is shared.
db.balancer = deltaMerge( db.balancer = deltaMerge(db.top, db.balancer)
db.top, modUpperOk = true, db.balancer, modLowerOk = db.nForked()==0)
# New empty top layer # New empty top layer
db.top = LayerRef() db.top = LayerRef()

View File

@ -15,11 +15,12 @@
import import
std/tables, std/tables,
eth/common,
results, results,
./kvt_desc/desc_backend, ./kvt_desc/desc_backend,
"."/[kvt_desc, kvt_layers] "."/[kvt_desc, kvt_layers]
export results
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Public functions, converters # Public functions, converters
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -12,7 +12,6 @@
## ================================================== ## ==================================================
## ##
import import
eth/common,
../kvt_init/[memory_db, memory_only], ../kvt_init/[memory_db, memory_only],
".."/[kvt_desc, kvt_init], ".."/[kvt_desc, kvt_init],
./walk_private ./walk_private

View File

@ -17,7 +17,6 @@
## `./kvt_walk/persistent`.) ## `./kvt_walk/persistent`.)
## ##
import import
eth/common,
../kvt_init/[rocks_db, persistent], ../kvt_init/[rocks_db, persistent],
../kvt_desc, ../kvt_desc,
"."/[memory_only, walk_private] "."/[memory_only, walk_private]

View File

@ -9,8 +9,7 @@
# distributed except according to those terms. # distributed except according to those terms.
import import
std/[sets, tables], std/sets,
eth/common,
".."/[kvt_desc, kvt_init, kvt_layers] ".."/[kvt_desc, kvt_init, kvt_layers]
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -11,7 +11,7 @@
import import
tables, tables,
stint, stint,
eth/common, eth/common/addresses,
../utils/mergeutils ../utils/mergeutils
type type

View File

@ -9,14 +9,6 @@ type
EVMError* = object of CatchableError EVMError* = object of CatchableError
## Base error class for all evm errors. ## Base error class for all evm errors.
BlockNotFound* = object of EVMError
## The block with the given number/hash does not exist.
CanonicalHeadNotFound* = object of EVMError
## The chain has no canonical head.
ValidationError* = object of EVMError ValidationError* = object of EVMError
## Error to signal something does not pass a validation check. ## Error to signal something does not pass a validation check.
CoreDbApiError* = object of CatchableError
## Errors related to `CoreDB` API

View File

@ -229,8 +229,6 @@ proc getBlockContent(oracle: Oracle,
return ok(bc) return ok(bc)
except RlpError as exc: except RlpError as exc:
return err(exc.msg) return err(exc.msg)
except BlockNotFound as exc:
return err(exc.msg)
type type
OracleResult = object OracleResult = object

View File

@ -57,10 +57,14 @@ proc init(
root: common.Hash32; root: common.Hash32;
): T = ): T =
let ctx = block: let ctx = block:
when false:
let rc = com.db.ctx.newCtxByKey(root) let rc = com.db.ctx.newCtxByKey(root)
if rc.isErr: if rc.isErr:
raiseAssert "newCptCtx: " & $$rc.error raiseAssert "newCptCtx: " & $$rc.error
rc.value rc.value
else:
{.warning: "TODO make a temporary context? newCtxByKey has been obsoleted".}
com.db.ctx
T(db: com.db, root: root, cpt: com.db.pushCapture(), ctx: ctx) T(db: com.db, root: root, cpt: com.db.pushCapture(), ctx: ctx)
proc init( proc init(
@ -75,14 +79,18 @@ proc activate(cc: CaptCtxRef): CaptCtxRef {.discardable.} =
## Install/activate new context `cc.ctx`, old one in `cc.restore` ## Install/activate new context `cc.ctx`, old one in `cc.restore`
doAssert not cc.isNil doAssert not cc.isNil
doAssert cc.restore.isNil # otherwise activated, already doAssert cc.restore.isNil # otherwise activated, already
cc.restore = cc.ctx.swapCtx cc.db if true:
raiseAssert "TODO activte context"
# cc.restore = cc.ctx.swapCtx cc.db
cc cc
proc release(cc: CaptCtxRef) = proc release(cc: CaptCtxRef) =
if not cc.restore.isNil: # switch to original context (if any) # if not cc.restore.isNil: # switch to original context (if any)
let ctx = cc.restore.swapCtx(cc.db) # let ctx = cc.restore.swapCtx(cc.db)
doAssert ctx == cc.ctx # doAssert ctx == cc.ctx
cc.ctx.forget() # dispose if true:
raiseAssert "TODO release context"
# cc.ctx.forget() # dispose
cc.cpt.pop() # discard top layer of actions tracer cc.cpt.pop() # discard top layer of actions tracer
# ------------------- # -------------------

View File

@ -105,9 +105,6 @@ proc accountsRunner(
test &"Delete accounts database sub-trees, {accLst.len} lists": test &"Delete accounts database sub-trees, {accLst.len} lists":
check noisy.testTxMergeAndDeleteSubTree(accLst, dbDir) check noisy.testTxMergeAndDeleteSubTree(accLst, dbDir)
test &"Distributed backend balancers {accLst.len} entries":
check noisy.testBalancer(accLst, dbDir)
proc storagesRunner( proc storagesRunner(
noisy = true; noisy = true;
@ -136,9 +133,6 @@ proc storagesRunner(
test &"Delete storage database sub-trees, {stoLst.len} lists": test &"Delete storage database sub-trees, {stoLst.len} lists":
check noisy.testTxMergeAndDeleteSubTree(stoLst, dbDir) check noisy.testTxMergeAndDeleteSubTree(stoLst, dbDir)
test &"Distributed backend balancers {stoLst.len} entries":
check noisy.testBalancer(stoLst, dbDir)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Main function(s) # Main function(s)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -64,9 +64,6 @@ proc dump(pfx: string; dx: varargs[AristoDbRef]): string =
proc dump(dx: varargs[AristoDbRef]): string {.used.} = proc dump(dx: varargs[AristoDbRef]): string {.used.} =
"".dump dx "".dump dx
proc dump(w: DbTriplet): string {.used.} =
"db".dump(w[0], w[1], w[2])
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Private helpers # Private helpers
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -99,7 +96,7 @@ iterator quadripartite(td: openArray[ProofTrieData]): LeafQuartet =
yield [collect[0], collect[1], collect[2], lst] yield [collect[0], collect[1], collect[2], lst]
collect.setLen(0) collect.setLen(0)
proc dbTriplet(w: LeafQuartet; rdbPath: string): Result[DbTriplet,AristoError] = proc dbTriplet(w: LeafQuartet; rdbPath: string): Result[AristoDbRef,AristoError] =
let db = block: let db = block:
if 0 < rdbPath.len: if 0 < rdbPath.len:
let (dbOpts, cfOpts) = DbOptions.init().toRocksDb() let (dbOpts, cfOpts) = DbOptions.init().toRocksDb()
@ -123,28 +120,13 @@ proc dbTriplet(w: LeafQuartet; rdbPath: string): Result[DbTriplet,AristoError] =
xCheckRc rc.error == 0: xCheckRc rc.error == 0:
result = err(rc.error) result = err(rc.error)
let dx = [db, db.forkTx(0).value, db.forkTx(0).value] let dx = db
xCheck dx[0].nForked == 2
# Reduce unwanted tx layers
for n in 1 ..< dx.len:
xCheck dx[n].level == 1
xCheck dx[n].txTop.value.commit.isOk
# Clause (9) from `aristo/README.md` example
for n in 0 ..< dx.len:
let report = dx[n].mergeList w[n+1]
if report.error != 0:
db.finish(eradicate=true)
xCheck (n, report.error) == (n,0)
return ok(dx)
# ---------------------- # ----------------------
proc cleanUp(dx: var DbTriplet) = proc cleanUp(dx: var AristoDbRef) =
if not dx[0].isNil: if not dx.isNil:
dx[0].finish(eradicate=true) dx.finish(eradicate=true)
dx.reset dx.reset
# ---------------------- # ----------------------
@ -227,16 +209,14 @@ proc isDbEq(a, b: LayerRef; db: AristoDbRef; noisy = true): bool =
# ---------------------- # ----------------------
proc checkBeOk( proc checkBeOk(
dx: DbTriplet; dx: AristoDbRef;
forceCache = false; forceCache = false;
noisy = true; noisy = true;
): bool = ): bool =
## .. ## ..
for n in 0 ..< dx.len: let rc = dx.checkBE()
let rc = dx[n].checkBE()
xCheckRc rc.error == (0,0): xCheckRc rc.error == (0,0):
noisy.say "***", "db checkBE failed", noisy.say "***", "db checkBE failed"
" n=", n, "/", dx.len-1
true true
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -267,7 +247,7 @@ proc testBalancer*(
let rc = dbTriplet(w, rdbPath) let rc = dbTriplet(w, rdbPath)
xCheckRc rc.error == 0 xCheckRc rc.error == 0
rc.value rc.value
(db1, db2, db3) = (dx[0], dx[1], dx[2]) db1 = dx
defer: defer:
dx.cleanUp() dx.cleanUp()
@ -279,31 +259,15 @@ proc testBalancer*(
let rc = db1.persist() let rc = db1.persist()
xCheckRc rc.error == 0 xCheckRc rc.error == 0
xCheck db1.balancer == LayerRef(nil) xCheck db1.balancer == LayerRef(nil)
xCheck db2.balancer == db3.balancer
block:
let rc = db2.stow() # non-persistent
xCheckRc rc.error == 0:
noisy.say "*** testDistributedAccess (3)", "n=", n, "db2".dump db2
xCheck db1.balancer == LayerRef(nil)
xCheck db2.balancer != db3.balancer
# Clause (11) from `aristo/README.md` example
discard db2.reCentre()
block:
let rc = db2.persist()
xCheckRc rc.error == 0
xCheck db2.balancer == LayerRef(nil)
# Check/verify backends # Check/verify backends
block: block:
let ok = dx.checkBeOk(noisy=noisy) let ok = dx.checkBeOk(noisy=noisy)
xCheck ok: xCheck ok:
noisy.say "*** testDistributedAccess (4)", "n=", n, "db3".dump db3 noisy.say "*** testDistributedAccess (4)", "n=", n
# Capture filters from clause (11) # Capture filters from clause (11)
c11Filter1 = db1.balancer c11Filter1 = db1.balancer
c11Filter3 = db3.balancer
# Clean up # Clean up
dx.cleanUp() dx.cleanUp()
@ -317,24 +281,10 @@ proc testBalancer*(
let rc = dbTriplet(w, rdbPath) let rc = dbTriplet(w, rdbPath)
xCheckRc rc.error == 0 xCheckRc rc.error == 0
rc.value rc.value
(db1, db2, db3) = (dy[0], dy[1], dy[2]) db1 = dy
defer: defer:
dy.cleanUp() dy.cleanUp()
# Build clause (12) from `aristo/README.md` example
discard db2.reCentre()
block:
let rc = db2.persist()
xCheckRc rc.error == 0
xCheck db2.balancer == LayerRef(nil)
xCheck db1.balancer == db3.balancer
# Clause (13) from `aristo/README.md` example
xCheck not db1.isCentre()
block:
let rc = db1.stow() # non-persistent
xCheckRc rc.error == 0
# Clause (14) from `aristo/README.md` check # Clause (14) from `aristo/README.md` check
let c11Fil1_eq_db1RoFilter = c11Filter1.isDbEq(db1.balancer, db1, noisy) let c11Fil1_eq_db1RoFilter = c11Filter1.isDbEq(db1.balancer, db1, noisy)
xCheck c11Fil1_eq_db1RoFilter: xCheck c11Fil1_eq_db1RoFilter:
@ -342,12 +292,6 @@ proc testBalancer*(
"db1".dump(db1), "db1".dump(db1),
"" ""
# Clause (15) from `aristo/README.md` check
let c11Fil3_eq_db3RoFilter = c11Filter3.isDbEq(db3.balancer, db3, noisy)
xCheck c11Fil3_eq_db3RoFilter:
noisy.say "*** testDistributedAccess (8)", "n=", n,
"db3".dump(db3),
""
# Check/verify backends # Check/verify backends
block: block:
let ok = dy.checkBeOk(noisy=noisy) let ok = dy.checkBeOk(noisy=noisy)