nimbus-eth1/execution_chain/db/aristo/aristo_tx_frame.nim
Jacek Sieka 42bb640443
Simplify shared rocksdb instance / write batch handling (#3063)
By introducing the "shared rocksdb instance" concept to the backend, we
can remove the "piggybacking" mode , thus reducing the complexity of
database initialisation and opening the possibility of extending how
write batching works across kvt/aristo.

The change makes explicit the hidden shared state that was previously
hiding in closures and provides the first step towards simplifying the
"commit/persist" interface of coredb, preparing it for optimizations to
reduce the "layering tax" that `forked-layers` introduced.
2025-02-14 09:40:22 +01:00

138 lines
4.3 KiB
Nim

# nimbus-eth1
# Copyright (c) 2023-2025 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 frames helper
## ======================================
##
{.push raises: [].}
import
results,
./[aristo_desc, aristo_layers]
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
proc txFrameBegin*(db: AristoDbRef, parent: AristoTxRef): Result[AristoTxRef,AristoError] =
## Starts a new transaction.
##
## Example:
## ::
## proc doSomething(db: AristoDbRef) =
## let tx = db.begin
## defer: tx.rollback()
## ... continue using db ...
## tx.commit()
##
let parent = if parent == nil:
db.txRef
else:
parent
let
vTop = parent.layer.vTop
layer = LayerRef(vTop: vTop, cTop: vTop)
ok AristoTxRef(
db: db,
parent: parent,
layer: layer)
proc baseTxFrame*(db: AristoDbRef): AristoTxRef=
db.txRef
proc rollback*(
tx: AristoTxRef; # Top transaction on database
): Result[void,AristoError] =
## Given a *top level* handle, this function discards all database operations
## performed for this transaction.
# TODO Everyone using this txref should repoint their parent field
let vTop = tx.layer[].cTop
tx.layer[] = Layer(vTop: vTop, cTop: vTop)
ok()
proc commit*(
tx: AristoTxRef; # Top transaction on database
): Result[void,AristoError] =
## This function pushes all changes done in this frame to its parent
##
# TODO Everyone using this txref should repoint their parent field
doAssert tx.parent != nil, "should not commit the base tx"
# A rollback after commit should reset to the new vTop!
tx.layer[].cTop = tx.layer[].vTop
mergeAndReset(tx.parent.layer[], tx.layer[])
ok()
proc txFramePersist*(
db: AristoDbRef; # Database
batch: PutHdlRef;
nxtSid = 0u64; # Next state ID (aka block number)
) =
## Persistently store data onto backend database. If the system is running
## without a database backend, the function returns immediately with an
## error.
##
## The function merges all data staged in `txFrame` and merges it onto the
## backend database. `txFrame` becomes the new `baseTxFrame`.
##
## Any parent frames of `txFrame` become invalid after this operation.
##
## If the argument `nxtSid` is passed non-zero, it will be the ID for the
## next recovery journal record. If non-zero, this ID must be greater than
## all previous IDs (e.g. block number when stowing after block execution.)
##
let be = db.backend
doAssert not be.isNil, "Persisting to backend requires ... a backend!"
let lSst = SavedState(
key: emptyRoot, # placeholder for more
serial: nxtSid)
# Store structural single trie entries
for rvid, vtx in db.txRef.layer.sTab:
db.txRef.layer.kMap.withValue(rvid, key) do:
be.putVtxFn(batch, rvid, vtx, key[])
do:
be.putVtxFn(batch, rvid, vtx, default(HashKey))
be.putTuvFn(batch, db.txRef.layer.vTop)
be.putLstFn(batch, lSst)
# TODO above, we only prepare the changes to the database but don't actually
# write them to disk - the code below that updates the frame should
# really run after things have been written (to maintain sync betweeen
# in-memory and on-disk state)
# Copy back updated payloads
for accPath, vtx in db.txRef.layer.accLeaves:
db.accLeaves.put(accPath, vtx)
for mixPath, vtx in db.txRef.layer.stoLeaves:
db.stoLeaves.put(mixPath, vtx)
# Done with txRef, all saved to backend
db.txRef.layer.cTop = db.txRef.layer.vTop
db.txRef.layer.sTab.clear()
db.txRef.layer.kMap.clear()
db.txRef.layer.accLeaves.clear()
db.txRef.layer.stoLeaves.clear()
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------