reduce tx naming overload (#2952)

* if it's a db function, use `txFrame...`
* if it's not a db function, don't use `txFrame...`
This commit is contained in:
Jacek Sieka 2024-12-18 17:03:51 +01:00 committed by GitHub
parent 7bbb0f4421
commit d45d03ce0c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 147 additions and 342 deletions

View File

@ -42,7 +42,7 @@ proc processBlock(
## implementations (but can be savely removed, as well.)
## variant of `processBlock()` where the `header` argument is explicitely set.
template header: Header = blk.header
var dbTx = vmState.com.db.ctx.newTransaction()
var dbTx = vmState.com.db.ctx.txFrameBegin()
defer: dbTx.dispose()
let com = vmState.com
@ -89,7 +89,7 @@ proc getVmState(c: ChainRef, header: Header):
# intended to accepts invalid block
proc setBlock*(c: ChainRef; blk: Block): Result[void, string] =
template header: Header = blk.header
let dbTx = c.db.ctx.newTransaction()
let dbTx = c.db.ctx.txFrameBegin()
defer: dbTx.dispose()
# Needed for figuring out whether KVT cleanup is due (see at the end)

View File

@ -140,7 +140,7 @@ proc validateBlock(c: ForkedChainRef,
parent: Header,
blk: Block,
updateCursor: bool = true): Result[void, string] =
let dbTx = c.db.ctx.newTransaction()
let dbTx = c.db.ctx.txFrameBegin()
defer:
dbTx.dispose()
@ -170,7 +170,7 @@ proc replaySegment*(c: ForkedChainRef, target: Hash32) =
prevHash = chain[^1].header.parentHash
c.stagingTx.rollback()
c.stagingTx = c.db.ctx.newTransaction()
c.stagingTx = c.db.ctx.txFrameBegin()
c.cursorHeader = c.baseHeader
for i in countdown(chain.high, chain.low):
c.validateBlock(c.cursorHeader, chain[i],
@ -432,7 +432,7 @@ proc updateHeadIfNecessary(c: ForkedChainRef, pvarc: PivotArc) =
if c.cursorHash != pvarc.cursor.hash:
if not c.stagingTx.isNil:
c.stagingTx.rollback()
c.stagingTx = c.db.ctx.newTransaction()
c.stagingTx = c.db.ctx.txFrameBegin()
c.replaySegment(pvarc.pvHash)
c.trimCursorArc(pvarc)
@ -442,7 +442,7 @@ proc updateHeadIfNecessary(c: ForkedChainRef, pvarc: PivotArc) =
if c.stagingTx.isNil:
# setHead below don't go straight to db
c.stagingTx = c.db.ctx.newTransaction()
c.stagingTx = c.db.ctx.txFrameBegin()
c.setHead(pvarc)
@ -511,7 +511,7 @@ proc importBlock*(c: ForkedChainRef, blk: Block): Result[void, string] =
# Try to import block to canonical or side chain.
# return error if the block is invalid
if c.stagingTx.isNil:
c.stagingTx = c.db.ctx.newTransaction()
c.stagingTx = c.db.ctx.txFrameBegin()
template header(): Header =
blk.header
@ -521,7 +521,7 @@ proc importBlock*(c: ForkedChainRef, blk: Block): Result[void, string] =
if header.parentHash == c.baseHash:
c.stagingTx.rollback()
c.stagingTx = c.db.ctx.newTransaction()
c.stagingTx = c.db.ctx.txFrameBegin()
return c.validateBlock(c.baseHeader, blk)
if header.parentHash notin c.blocks:
@ -601,7 +601,7 @@ proc forkChoice*(c: ForkedChainRef,
# Write segment from base+1 to newBase into database
c.stagingTx.rollback()
c.stagingTx = c.db.ctx.newTransaction()
c.stagingTx = c.db.ctx.txFrameBegin()
if newBase.pvNumber > c.baseHeader.number:
c.replaySegment(newBase.pvHash)
@ -616,7 +616,7 @@ proc forkChoice*(c: ForkedChainRef,
if c.stagingTx.isNil:
# replaySegment or setHead below don't
# go straight to db
c.stagingTx = c.db.ctx.newTransaction()
c.stagingTx = c.db.ctx.txFrameBegin()
# Move chain state forward to current head
if newBase.pvNumber < pvarc.pvNumber:

View File

@ -117,6 +117,9 @@ proc persistBlock*(p: var Persister, blk: Block): Result[void, string] =
let c = p.c
if p.dbTx == nil:
p.dbTx = p.c.db.ctx.txFrameBegin()
# Full validation means validating the state root at every block and
# performing the more expensive hash computations on the block itself, ie
# verifying that the transaction and receipts roots are valid - when not

View File

@ -292,7 +292,7 @@ proc packerVmExec*(xp: TxPoolRef): Result[TxPacker, string]
## Rebuild `packed` bucket by selection items from the `staged` bucket
## after executing them in the VM.
let db = xp.vmState.com.db
let dbTx = db.ctx.newTransaction()
let dbTx = db.ctx.txFrameBegin()
defer: dbTx.dispose()
var pst = xp.vmExecInit.valueOr:

View File

@ -178,7 +178,7 @@ type
## Getter, returns `true` if the argument `tx` referes to the current
## top level transaction.
AristoApiLevelFn* =
AristoApiTxFrameLevelFn* =
proc(db: AristoDbRef;
): int
{.noRaise.}
@ -310,7 +310,7 @@ type
## operations performed for this transactio. The previous transaction
## is returned if there was any.
AristoApiTxBeginFn* =
AristoApiTxFrameBeginFn* =
proc(db: AristoDbRef;
): Result[AristoTxRef,AristoError]
{.noRaise.}
@ -324,13 +324,7 @@ type
## ... continue using db ...
## tx.commit()
AristoApiTxLevelFn* =
proc(tx: AristoTxRef;
): int
{.noRaise.}
## Getter, positive nesting level of transaction argument `tx`
AristoApiTxTopFn* =
AristoApiTxFrameTopFn* =
proc(db: AristoDbRef;
): Result[AristoTxRef,AristoError]
{.noRaise.}
@ -358,7 +352,7 @@ type
hasStorageData*: AristoApiHasStorageDataFn
isTop*: AristoApiIsTopFn
level*: AristoApiLevelFn
txFrameLevel*: AristoApiTxFrameLevelFn
mergeAccountRecord*: AristoApiMergeAccountRecordFn
mergeStorageData*: AristoApiMergeStorageDataFn
@ -373,9 +367,8 @@ type
pathAsBlob*: AristoApiPathAsBlobFn
persist*: AristoApiPersistFn
rollback*: AristoApiRollbackFn
txBegin*: AristoApiTxBeginFn
txLevel*: AristoApiTxLevelFn
txTop*: AristoApiTxTopFn
txFrameBegin*: AristoApiTxFrameBeginFn
txFrameTop*: AristoApiTxFrameTopFn
AristoApiProfNames* = enum
@ -414,9 +407,8 @@ type
AristoApiProfPathAsBlobFn = "pathAsBlob"
AristoApiProfPersistFn = "persist"
AristoApiProfRollbackFn = "rollback"
AristoApiProfTxBeginFn = "txBegin"
AristoApiProfTxLevelFn = "txLevel"
AristoApiProfTxTopFn = "txTop"
AristoApiProfTxFrameBeginFn = "txFrameBegin"
AristoApiProfTxFrameTopFn = "txFrameTop"
AristoApiProfBeGetVtxFn = "be/getVtx"
AristoApiProfBeGetKeyFn = "be/getKey"
@ -471,9 +463,8 @@ when AutoValidateApiHooks:
doAssert not api.pathAsBlob.isNil
doAssert not api.persist.isNil
doAssert not api.rollback.isNil
doAssert not api.txBegin.isNil
doAssert not api.txLevel.isNil
doAssert not api.txTop.isNil
doAssert not api.txFrameBegin.isNil
doAssert not api.txFrameTop.isNil
proc validate(prf: AristoApiProfRef) =
prf.AristoApiRef.validate
@ -520,7 +511,7 @@ func init*(api: var AristoApiObj) =
api.hasStorageData = hasStorageData
api.isTop = isTop
api.level = level
api.txFrameLevel = txFrameLevel
api.mergeAccountRecord = mergeAccountRecord
api.mergeStorageData = mergeStorageData
@ -533,9 +524,8 @@ func init*(api: var AristoApiObj) =
api.pathAsBlob = pathAsBlob
api.persist = persist
api.rollback = rollback
api.txBegin = txBegin
api.txLevel = txLevel
api.txTop = txTop
api.txFrameBegin = txFrameBegin
api.txFrameTop = txFrameTop
when AutoValidateApiHooks:
api.validate
@ -564,7 +554,7 @@ func dup*(api: AristoApiRef): AristoApiRef =
hasStorageData: api.hasStorageData,
isTop: api.isTop,
level: api.level,
txFrameLevel: api.txFrameLevel,
mergeAccountRecord: api.mergeAccountRecord,
mergeStorageData: api.mergeStorageData,
@ -577,9 +567,8 @@ func dup*(api: AristoApiRef): AristoApiRef =
pathAsBlob: api.pathAsBlob,
persist: api.persist,
rollback: api.rollback,
txBegin: api.txBegin,
txLevel: api.txLevel,
txTop: api.txTop)
txFrameBegin: api.txFrameBegin,
txFrameTop: api.txFrameTop)
when AutoValidateApiHooks:
result.validate
@ -730,20 +719,15 @@ func init*(
AristoApiProfRollbackFn.profileRunner:
result = api.rollback(a)
profApi.txBegin =
profApi.txFrameBegin =
proc(a: AristoDbRef): auto =
AristoApiProfTxBeginFn.profileRunner:
result = api.txBegin(a)
AristoApiProfTxFrameBeginFn.profileRunner:
result = api.txFrameBegin(a)
profApi.txLevel =
proc(a: AristoTxRef): auto =
AristoApiProfTxLevelFn.profileRunner:
result = api.txLevel(a)
profApi.txTop =
profApi.txFrameTop =
proc(a: AristoDbRef): auto =
AristoApiProfTxTopFn.profileRunner:
result = api.txTop(a)
AristoApiProfTxFrameTopFn.profileRunner:
result = api.txFrameTop(a)
let beDup = be.dup()
if beDup.isNil:

View File

@ -232,8 +232,8 @@ type
TxArgStaleTx
TxArgsUseless
TxBackendNotWritable
TxLevelTooDeep
TxLevelUseless
TxFrameLevelTooDeep
TxFrameLevelUseless
TxNoPendingTx
TxNotFound
TxNotTopTx

View File

@ -18,26 +18,7 @@ import
./aristo_tx/[tx_frame, tx_stow],
./aristo_desc
# ------------------------------------------------------------------------------
# Public functions, getters
# ------------------------------------------------------------------------------
func txTop*(db: AristoDbRef): Result[AristoTxRef,AristoError] =
## Getter, returns top level transaction if there is any.
db.txFrameTop()
func isTop*(tx: AristoTxRef): bool =
## Getter, returns `true` if the argument `tx` referes to the current top
## level transaction.
tx.txFrameIsTop()
func txLevel*(tx: AristoTxRef): int =
## Getter, positive nesting level of transaction argument `tx`
tx.txFrameLevel()
func level*(db: AristoDbRef): int =
## Getter, non-negative nesting level (i.e. number of pending transactions)
db.txFrameLevel()
export tx_frame
# ------------------------------------------------------------------------------
# Public functions
@ -51,51 +32,6 @@ func to*(tx: AristoTxRef; T: type[AristoDbRef]): T =
# Public functions: Transaction frame
# ------------------------------------------------------------------------------
proc txBegin*(db: AristoDbRef): Result[AristoTxRef,AristoError] =
## Starts a new transaction.
##
## Example:
## ::
## proc doSomething(db: AristoDbRef) =
## let tx = db.begin
## defer: tx.rollback()
## ... continue using db ...
## tx.commit()
##
db.txFrameBegin()
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 transactio. The previous transaction is returned if
## there was any.
##
tx.txFrameRollback()
proc commit*(
tx: AristoTxRef; # Top transaction on database
): Result[void,AristoError] =
## Given a *top level* handle, this function accepts all database operations
## performed through this handle and merges it to the previous layer. The
## previous transaction is returned if there was any.
##
tx.txFrameCommit()
proc collapse*(
tx: AristoTxRef; # Top transaction on database
commit: bool; # Commit if `true`, otherwise roll back
): Result[void,AristoError] =
## Iterated application of `commit()` or `rollback()` performing the
## something similar to
## ::
## while true:
## discard tx.commit() # ditto for rollback()
## if db.txTop.isErr: break
## tx = db.txTop.value
##
tx.txFrameCollapse commit
# ------------------------------------------------------------------------------
# Public functions: save to database
# ------------------------------------------------------------------------------
@ -119,20 +55,7 @@ proc persist*(
## 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.)
##
db.txStow(nxtSid, persistent=true)
proc stow*(
db: AristoDbRef; # Database
): Result[void,AristoError] =
## This function is similar to `persist()` stopping short of performing the
## final step storing on the persistent database. It fails if there is a
## pending transaction.
##
## The function merges all staged data from the top layer cache onto the
## backend stage area and leaves it there. This function can be seen as
## a sort of a bottom level transaction `commit()`.
##
db.txStow(nxtSid=0u64, persistent=false)
db.txPersist(nxtSid)
# ------------------------------------------------------------------------------
# End

View File

@ -17,14 +17,14 @@ import
results,
".."/[aristo_desc, aristo_layers]
func txFrameIsTop*(tx: AristoTxRef): bool
func isTop*(tx: AristoTxRef): bool
# ------------------------------------------------------------------------------
# Private helpers
# ------------------------------------------------------------------------------
func getDbDescFromTopTx(tx: AristoTxRef): Result[AristoDbRef,AristoError] =
if not tx.txFrameIsTop():
if not tx.isTop():
return err(TxNotTopTx)
let db = tx.db
if tx.level != db.stack.len:
@ -48,15 +48,11 @@ func txFrameTop*(db: AristoDbRef): Result[AristoTxRef,AristoError] =
else:
ok(db.txRef)
func txFrameIsTop*(tx: AristoTxRef): bool =
func isTop*(tx: AristoTxRef): bool =
## Getter, returns `true` if the argument `tx` referes to the current top
## level transaction.
tx.db.txRef == tx and tx.db.top.txUid == tx.txUid
func txFrameLevel*(tx: AristoTxRef): int =
## Getter, positive nesting level of transaction argument `tx`
tx.level
func txFrameLevel*(db: AristoDbRef): int =
## Getter, non-negative nesting level (i.e. number of pending transactions)
if not db.txRef.isNil:
@ -95,7 +91,7 @@ proc txFrameBegin*(db: AristoDbRef): Result[AristoTxRef,AristoError] =
ok db.txRef
proc txFrameRollback*(
proc rollback*(
tx: AristoTxRef; # Top transaction on database
): Result[void,AristoError] =
## Given a *top level* handle, this function discards all database operations
@ -112,7 +108,7 @@ proc txFrameRollback*(
ok()
proc txFrameCommit*(
proc commit*(
tx: AristoTxRef; # Top transaction on database
): Result[void,AristoError] =
## Given a *top level* handle, this function accepts all database operations
@ -139,7 +135,7 @@ proc txFrameCommit*(
ok()
proc txFrameCollapse*(
proc collapse*(
tx: AristoTxRef; # Top transaction on database
commit: bool; # Commit if `true`, otherwise roll back
): Result[void,AristoError] =
@ -148,8 +144,8 @@ proc txFrameCollapse*(
## ::
## while true:
## discard tx.commit() # ditto for rollback()
## if db.txTop.isErr: break
## tx = db.txTop.value
## if db.txFrameTop.isErr: break
## tx = db.txFrameTop.value
##
let db = ? tx.getDbDescFromTopTx()
@ -162,7 +158,7 @@ proc txFrameCollapse*(
# Public iterators
# ------------------------------------------------------------------------------
iterator txFrameWalk*(tx: AristoTxRef): (int,AristoTxRef,LayerRef,AristoError) =
iterator walk*(tx: AristoTxRef): (int,AristoTxRef,LayerRef,AristoError) =
## Walk down the transaction stack chain.
let db = tx.db
var tx = tx

View File

@ -8,7 +8,7 @@
# at your option. This file may not be copied, modified, or distributed
# except according to those terms.
## Aristo DB -- Transaction stow/save helper
## Aristo DB -- Transaction save helper
## =========================================
##
{.push raises: [].}
@ -22,15 +22,14 @@ import
# Private functions
# ------------------------------------------------------------------------------
proc txStowOk*(
proc txPersistOk*(
db: AristoDbRef; # Database
persistent: bool; # Stage only unless `true`
): Result[void,AristoError] =
if not db.txRef.isNil:
return err(TxPendingTx)
if 0 < db.stack.len:
return err(TxStackGarbled)
if persistent and not db.deltaPersistentOk():
if not db.deltaPersistentOk():
return err(TxBackendNotWritable)
ok()
@ -38,14 +37,13 @@ proc txStowOk*(
# Public functions
# ------------------------------------------------------------------------------
proc txStow*(
proc txPersist*(
db: AristoDbRef; # Database
nxtSid: uint64; # Next state ID (aka block number)
persistent: bool; # Stage only unless `true`
): Result[void,AristoError] =
## Worker for `stow()` and `persist()` variants.
## Worker for `persist()` variants.
##
? db.txStowOk persistent
? db.txPersistOk()
if not db.top.isEmpty():
# Note that `deltaMerge()` will return the `db.top` argument if the
@ -57,9 +55,8 @@ proc txStow*(
# New empty top layer
db.top = LayerRef(vTop: db.balancer.vTop)
if persistent:
# Merge/move `balancer` into persistent tables (unless missing)
? db.deltaPersistent nxtSid
# Merge/move `balancer` into persistent tables (unless missing)
? db.deltaPersistent nxtSid
ok()

View File

@ -107,7 +107,7 @@ proc persistent*(
if rc.isOk or rc.error == TxPersistDelayed:
# The latter clause is OK: Piggybacking on `Aristo` backend
discard
elif CoreDbKvtRef(db.ctx).call(level, db.ctx.kvt) != 0:
elif CoreDbKvtRef(db.ctx).call(txFrameLevel, db.ctx.kvt) != 0:
result = err(rc.error.toError($api, TxPending))
break body
else:
@ -651,37 +651,30 @@ proc recast*(
# Public transaction related methods
# ------------------------------------------------------------------------------
proc level*(db: CoreDbRef): int =
proc txFrameLevel*(db: CoreDbRef): int =
## Retrieve transaction level (zero if there is no pending transaction).
##
db.setTrackNewApi BaseLevelFn
result = CoreDbAccRef(db.ctx).call(level, db.ctx.mpt)
result = CoreDbAccRef(db.ctx).call(txFrameLevel, db.ctx.mpt)
db.ifTrackNewApi: debug logTxt, api, elapsed, result
proc newTransaction*(ctx: CoreDbCtxRef): CoreDbTxRef =
proc txFrameBegin*(ctx: CoreDbCtxRef): CoreDbTxRef =
## Constructor
##
ctx.setTrackNewApi BaseNewTxFn
let
kTx = CoreDbKvtRef(ctx).call(txBegin, ctx.kvt).valueOr:
kTx = CoreDbKvtRef(ctx).call(txFrameBegin, ctx.kvt).valueOr:
raiseAssert $api & ": " & $error
aTx = CoreDbAccRef(ctx).call(txBegin, ctx.mpt).valueOr:
aTx = CoreDbAccRef(ctx).call(txFrameBegin, ctx.mpt).valueOr:
raiseAssert $api & ": " & $error
result = ctx.bless CoreDbTxRef(kTx: kTx, aTx: aTx)
ctx.ifTrackNewApi:
let newLevel = CoreDbAccRef(ctx).call(level, ctx.mpt)
debug logTxt, api, elapsed, newLevel
proc level*(tx: CoreDbTxRef): int =
## Print positive transaction level for argument `tx`
##
tx.setTrackNewApi TxLevelFn
result = CoreDbAccRef(tx.ctx).call(txLevel, tx.aTx)
tx.ifTrackNewApi: debug logTxt, api, elapsed, result
proc commit*(tx: CoreDbTxRef) =
tx.setTrackNewApi TxCommitFn:
let prvLevel {.used.} = CoreDbAccRef(tx.ctx).call(txLevel, tx.aTx)
let prvLevel {.used.} = CoreDbAccRef(tx.ctx).call(level, tx.aTx)
CoreDbAccRef(tx.ctx).call(commit, tx.aTx).isOkOr:
raiseAssert $api & ": " & $error
CoreDbKvtRef(tx.ctx).call(commit, tx.kTx).isOkOr:
@ -690,7 +683,7 @@ proc commit*(tx: CoreDbTxRef) =
proc rollback*(tx: CoreDbTxRef) =
tx.setTrackNewApi TxRollbackFn:
let prvLevel {.used.} = CoreDbAccRef(tx.ctx).call(txLevel, tx.aTx)
let prvLevel {.used.} = CoreDbAccRef(tx.ctx).call(level, tx.aTx)
CoreDbAccRef(tx.ctx).call(rollback, tx.aTx).isOkOr:
raiseAssert $api & ": " & $error
CoreDbKvtRef(tx.ctx).call(rollback, tx.kTx).isOkOr:
@ -699,7 +692,7 @@ proc rollback*(tx: CoreDbTxRef) =
proc dispose*(tx: CoreDbTxRef) =
tx.setTrackNewApi TxDisposeFn:
let prvLevel {.used.} = CoreDbAccRef(tx.ctx).call(txLevel, tx.aTx)
let prvLevel {.used.} = CoreDbAccRef(tx.ctx).call(level, tx.aTx)
if CoreDbAccRef(tx.ctx).call(isTop, tx.aTx):
CoreDbAccRef(tx.ctx).call(rollback, tx.aTx).isOkOr:
raiseAssert $api & ": " & $error

View File

@ -52,7 +52,7 @@ type
BaseFinishFn = "finish"
BaseLevelFn = "level"
BasePushCaptureFn = "pushCapture"
BaseNewTxFn = "newTransaction"
BaseNewTxFn = "txFrameBegin"
BasePersistentFn = "persistent"
BaseStateBlockNumberFn = "stateBlockNumber"
BaseVerifyFn = "verify"
@ -77,7 +77,7 @@ type
TxCommitFn = "commit"
TxDisposeFn = "dispose"
TxLevelFn = "level"
TxFrameLevelFn = "level"
TxRollbackFn = "rollback"
TxSaveDisposeFn = "safeDispose"

View File

@ -52,14 +52,14 @@ type
KvtApiHasKeyRcFn* = proc(db: KvtDbRef,
key: openArray[byte]): Result[bool,KvtError] {.noRaise.}
KvtApiIsTopFn* = proc(tx: KvtTxRef): bool {.noRaise.}
KvtApiLevelFn* = proc(db: KvtDbRef): int {.noRaise.}
KvtApiTxFrameLevelFn* = proc(db: KvtDbRef): int {.noRaise.}
KvtApiPutFn* = proc(db: KvtDbRef,
key, data: openArray[byte]): Result[void,KvtError] {.noRaise.}
KvtApiRollbackFn* = proc(tx: KvtTxRef): Result[void,KvtError] {.noRaise.}
KvtApiPersistFn* = proc(db: KvtDbRef): Result[void,KvtError] {.noRaise.}
KvtApiToKvtDbRefFn* = proc(tx: KvtTxRef): KvtDbRef {.noRaise.}
KvtApiTxBeginFn* = proc(db: KvtDbRef): Result[KvtTxRef,KvtError] {.noRaise.}
KvtApiTxTopFn* =
KvtApiTxFrameBeginFn* = proc(db: KvtDbRef): Result[KvtTxRef,KvtError] {.noRaise.}
KvtApiTxFrameTopFn* =
proc(db: KvtDbRef): Result[KvtTxRef,KvtError] {.noRaise.}
KvtApiRef* = ref KvtApiObj
@ -73,13 +73,13 @@ type
len*: KvtApiLenFn
hasKeyRc*: KvtApiHasKeyRcFn
isTop*: KvtApiIsTopFn
level*: KvtApiLevelFn
txFrameLevel*: KvtApiTxFrameLevelFn
put*: KvtApiPutFn
rollback*: KvtApiRollbackFn
persist*: KvtApiPersistFn
toKvtDbRef*: KvtApiToKvtDbRefFn
txBegin*: KvtApiTxBeginFn
txTop*: KvtApiTxTopFn
txFrameBegin*: KvtApiTxFrameBeginFn
txFrameTop*: KvtApiTxFrameTopFn
KvtApiProfNames* = enum
@ -98,8 +98,8 @@ type
KvtApiProfRollbackFn = "rollback"
KvtApiProfPersistFn = "persist"
KvtApiProfToKvtDbRefFn = "toKvtDbRef"
KvtApiProfTxBeginFn = "txBegin"
KvtApiProfTxTopFn = "txTop"
KvtApiProfTxFrameBeginFn = "txFrameBegin"
KvtApiProfTxFrameTopFn = "txFrameTop"
KvtApiProfBeGetKvpFn = "be/getKvp"
KvtApiProfBeLenKvpFn = "be/lenKvp"
@ -123,13 +123,13 @@ when AutoValidateApiHooks:
doAssert not api.get.isNil
doAssert not api.hasKeyRc.isNil
doAssert not api.isTop.isNil
doAssert not api.level.isNil
doAssert not api.txFrameLevel.isNil
doAssert not api.put.isNil
doAssert not api.rollback.isNil
doAssert not api.persist.isNil
doAssert not api.toKvtDbRef.isNil
doAssert not api.txBegin.isNil
doAssert not api.txTop.isNil
doAssert not api.txFrameBegin.isNil
doAssert not api.txFrameTop.isNil
proc validate(prf: KvtApiProfRef) =
prf.KvtApiRef.validate
@ -161,13 +161,13 @@ func init*(api: var KvtApiObj) =
api.len = len
api.hasKeyRc = hasKeyRc
api.isTop = isTop
api.level = level
api.txFrameLevel = txFrameLevel
api.put = put
api.rollback = rollback
api.persist = persist
api.toKvtDbRef = toKvtDbRef
api.txBegin = txBegin
api.txTop = txTop
api.txFrameBegin = txFrameBegin
api.txFrameTop = txFrameTop
when AutoValidateApiHooks:
api.validate
@ -184,16 +184,15 @@ func dup*(api: KvtApiRef): KvtApiRef =
len: api.len,
hasKeyRc: api.hasKeyRc,
isTop: api.isTop,
level: api.level,
txFrameLevel: api.txFrameLevel,
put: api.put,
rollback: api.rollback,
persist: api.persist,
toKvtDbRef: api.toKvtDbRef,
txBegin: api.txBegin,
txTop: api.txTop)
txFrameBegin: api.txFrameBegin,
txFrameTop: api.txFrameTop)
when AutoValidateApiHooks:
result.validate
# ------------------------------------------------------------------------------
# Public profile API constuctor
# ------------------------------------------------------------------------------
@ -281,15 +280,15 @@ func init*(
KvtApiProfToKvtDbRefFn.profileRunner:
result = api.toKvtDbRef(a)
profApi.txBegin =
profApi.txFrameBegin =
proc(a: KvtDbRef): auto =
KvtApiProfTxBeginFn.profileRunner:
result = api.txBegin(a)
KvtApiProfTxFrameBeginFn.profileRunner:
result = api.txFrameBegin(a)
profApi.txTop =
profApi.txFrameTop =
proc(a: KvtDbRef): auto =
KvtApiProfTxTopFn.profileRunner:
result = api.txTop(a)
KvtApiProfTxFrameTopFn.profileRunner:
result = api.txFrameTop(a)
let beDup = be.dup()
if beDup.isNil:

View File

@ -33,8 +33,8 @@ type
# Transaction wrappers
TxArgStaleTx
TxBackendNotWritable
TxLevelTooDeep
TxLevelUseless
TxFrameLevelTooDeep
TxFrameLevelUseless
TxNoPendingTx
TxNotTopTx
TxPendingTx

View File

@ -185,11 +185,11 @@ proc putEndTriggeredFn(db: RdbBackendRef): PutEndFn =
when extraTraceMessages:
debug logTxt "putEndTriggeredFn: failed",
error=hdl.error, info=hdl.info
# The error return code will signal a problem to the `txStow()`
# The error return code will signal a problem to the `txPersist()`
# function which was called by `writeEvCb()` below.
return err(hdl.error)
# Commit the session. This will be acknowledged by the `txStow()`
# Commit the session. This will be acknowledged by the `txPersist()`
# function which was called by `writeEvCb()` below.
ok()
@ -229,22 +229,22 @@ proc writeEvCb(db: RdbBackendRef): RdbWriteEventCb =
# Publish session argument
db.rdb.session = ws
# Execute delayed session. Note the the `txStow()` function is located
# Execute delayed session. Note the the `txPersist()` function is located
# in `tx_stow.nim`. This module `tx_stow.nim` is also imported by
# `kvt_tx.nim` which contains `persist() `. So the logic goes:
# ::
# kvt_tx.persist() --> registers a delayed write request rather
# than excuting tx_stow.txStow()
# than excuting tx_stow.txPersist()
#
# // the backend owner (i.e. Aristo) will start a write cycle and
# // invoke the envent handler rocks_db.writeEvCb()
# rocks_db.writeEvCb() --> calls tx_stow.txStow()
# rocks_db.writeEvCb() --> calls tx_stow.txPersist()
#
# tx_stow.txStow() --> calls rocks_db.putBegTriggeredFn()
# tx_stow.txPersist() --> calls rocks_db.putBegTriggeredFn()
# calls rocks_db.putKvpFn()
# calls rocks_db.putEndTriggeredFn()
#
let rc = db.rdb.delayedPersist.txStow(persistent=true)
let rc = db.rdb.delayedPersist.txPersist()
if rc.isErr:
error "writeEventCb(): persist() failed", error=rc.error
return false

View File

@ -19,26 +19,8 @@ import
./kvt_init/memory_only,
./kvt_desc
# ------------------------------------------------------------------------------
# Public functions, getters
# ------------------------------------------------------------------------------
export tx_frame
func txTop*(db: KvtDbRef): Result[KvtTxRef,KvtError] =
## Getter, returns top level transaction if there is any.
db.txFrameTop()
func isTop*(tx: KvtTxRef): bool =
## Getter, returns `true` if the argument `tx` referes to the current top
## level transaction.
tx.txFrameIsTop()
func txLevel*(tx: KvtTxRef): int =
## Getter, positive nesting level of transaction argument `tx`
tx.txFrameLevel()
func level*(db: KvtDbRef): int =
## Getter, non-negative nesting level (i.e. number of pending transactions)
db.txFrameLevel()
# ------------------------------------------------------------------------------
# Public functions
@ -52,55 +34,6 @@ func toKvtDbRef*(tx: KvtTxRef): KvtDbRef =
## Same as `.to(KvtDbRef)`
tx.db
# ------------------------------------------------------------------------------
# Public functions: Transaction frame
# ------------------------------------------------------------------------------
proc txBegin*(db: KvtDbRef): Result[KvtTxRef,KvtError] =
## Starts a new transaction.
##
## Example:
## ::
## proc doSomething(db: KvtDbRef) =
## let tx = db.txBegin
## defer: tx.rollback()
## ... continue using db ...
## tx.commit()
##
db.txFrameBegin()
proc rollback*(
tx: KvtTxRef; # Top transaction on database
): Result[void,KvtError] =
## Given a *top level* handle, this function discards all database operations
## performed for this transactio. The previous transaction is returned if
## there was any.
##
tx.txFrameRollback()
proc commit*(
tx: KvtTxRef; # Top transaction on database
): Result[void,KvtError] =
## Given a *top level* handle, this function accepts all database operations
## performed through this handle and merges it to the previous layer. The
## previous transaction is returned if there was any.
##
tx.txFrameCommit()
proc collapse*(
tx: KvtTxRef; # Top transaction on database
commit: bool; # Commit if `true`, otherwise roll back
): Result[void,KvtError] =
## Iterated application of `commit()` or `rollback()` performing the
## something similar to
## ::
## while true:
## discard tx.commit() # ditto for rollback()
## if db.topTx.isErr: break
## tx = db.topTx.value
##
tx.txFrameCollapse commit
# ------------------------------------------------------------------------------
# Public functions: save database
# ------------------------------------------------------------------------------
@ -121,24 +54,11 @@ proc persist*(
##
# Register for saving if piggybacked on remote database
if db.backend.kind == BackendRdbTriggered:
? db.txStowOk(persistent=true)
? db.txPersistOk()
? db.backend.setWrReqFn db
return err(TxPersistDelayed)
db.txStow(persistent=true)
proc stow*(
db: KvtDbRef; # Database
): Result[void,KvtError] =
## This function is similar to `persist()` stopping short of performing the
## final step storing on the persistent database. It fails if there is a
## pending transaction.
##
## The function merges all staged data from the top layer cache onto the
## backend stage area and leaves it there. This function can be seen as
## a sort of a bottom level transaction `commit()`.
##
db.txStow(persistent=false)
db.txPersist()
# ------------------------------------------------------------------------------
# End

View File

@ -18,14 +18,14 @@ import
results,
".."/[kvt_desc, kvt_layers]
func txFrameIsTop*(tx: KvtTxRef): bool
func isTop*(tx: KvtTxRef): bool
# ------------------------------------------------------------------------------
# Private helpers
# ------------------------------------------------------------------------------
func getDbDescFromTopTx(tx: KvtTxRef): Result[KvtDbRef,KvtError] =
if not tx.txFrameIsTop():
if not tx.isTop():
return err(TxNotTopTx)
let db = tx.db
if tx.level != db.stack.len:
@ -49,15 +49,11 @@ func txFrameTop*(db: KvtDbRef): Result[KvtTxRef,KvtError] =
else:
ok(db.txRef)
func txFrameIsTop*(tx: KvtTxRef): bool =
func isTop*(tx: KvtTxRef): bool =
## Getter, returns `true` if the argument `tx` referes to the current top
## level transaction.
tx.db.txRef == tx and tx.db.top.txUid == tx.txUid
func txFrameLevel*(tx: KvtTxRef): int =
## Getter, positive nesting level of transaction argument `tx`
tx.level
func txFrameLevel*(db: KvtDbRef): int =
## Getter, non-negative nesting level (i.e. number of pending transactions)
if not db.txRef.isNil:
@ -93,7 +89,7 @@ proc txFrameBegin*(db: KvtDbRef): Result[KvtTxRef,KvtError] =
ok db.txRef
proc txFrameRollback*(
proc rollback*(
tx: KvtTxRef; # Top transaction on database
): Result[void,KvtError] =
## Given a *top level* handle, this function discards all database operations
@ -110,7 +106,7 @@ proc txFrameRollback*(
ok()
proc txFrameCommit*(
proc commit*(
tx: KvtTxRef; # Top transaction on database
): Result[void,KvtError] =
## Given a *top level* handle, this function accepts all database operations
@ -135,7 +131,7 @@ proc txFrameCommit*(
ok()
proc txFrameCollapse*(
proc collapse*(
tx: KvtTxRef; # Top transaction on database
commit: bool; # Commit if `true`, otherwise roll back
): Result[void,KvtError] =

View File

@ -8,7 +8,7 @@
# at your option. This file may not be copied, modified, or distributed
# except according to those terms.
## Kvt DB -- Transaction stow/save helper
## Kvt DB -- Transaction save helper
## ======================================
##
{.push raises: [].}
@ -23,22 +23,20 @@ import
# Public functions
# ------------------------------------------------------------------------------
proc txStowOk*(
proc txPersistOk*(
db: KvtDbRef; # Database
persistent: bool; # Stage only unless `true`
): Result[void,KvtError] =
## Verify that `txStow()` can go ahead
## Verify that `txPersist()` can go ahead
if not db.txRef.isNil:
return err(TxPendingTx)
if 0 < db.stack.len:
return err(TxStackGarbled)
if persistent and not db.deltaPersistentOk():
if not db.deltaPersistentOk():
return err(TxBackendNotWritable)
ok()
proc txStow*(
proc txPersist*(
db: KvtDbRef; # Database
persistent: bool; # Stage only unless `true`
): Result[void,KvtError] =
## The function saves the data from the top layer cache into the
## backend database.
@ -46,7 +44,7 @@ proc txStow*(
## If there is no backend the function returns immediately with an error.
## The same happens if there is a pending transaction.
##
? db.txStowOk persistent
? db.txPersistOk()
if 0 < db.top.sTab.len:
# Note that `deltaMerge()` will return the `db.top` argument if the
@ -58,9 +56,8 @@ proc txStow*(
# New empty top layer
db.top = LayerRef()
if persistent:
# Move `balancer` data into persistent tables
? db.deltaPersistent()
# Move `balancer` data into persistent tables
? db.deltaPersistent()
ok()

View File

@ -81,7 +81,7 @@ proc dbStoreSyncStateLayout*(ctx: BeaconCtxRef; info: static[string]) =
# While executing blocks there are frequent save cycles. Otherwise, an
# extra save request might help to pick up an interrupted sync session.
if ctx.db.level() == 0 and ctx.stash.len == 0:
if ctx.db.txFrameLevel() == 0 and ctx.stash.len == 0:
let number = ctx.db.getSavedStateBlockNumber()
ctx.db.persistent(number).isOkOr:
raiseAssert info & " persistent() failed: " & $$error
@ -165,9 +165,9 @@ proc dbHeadersStash*(
## ..
##
let
txLevel = ctx.db.level()
txFrameLevel = ctx.db.txFrameLevel()
last = first + revBlobs.len.uint64 - 1
if 0 < txLevel:
if 0 < txFrameLevel:
# Need to cache it because FCU has blocked writing through to disk.
for n,data in revBlobs:
ctx.stash[last - n.uint64] = data

View File

@ -38,7 +38,7 @@ proc rpcCallEvm*(args: TransactionArgs,
let vmState = ? BaseVMState.new(topHeader, com)
let params = ? toCallParams(vmState, args, globalGasCap, header.baseFeePerGas)
var dbTx = com.db.ctx.newTransaction()
var dbTx = com.db.ctx.txFrameBegin()
defer: dbTx.dispose() # always dispose state changes
ok(runComputation(params, CallResult))
@ -50,7 +50,7 @@ proc rpcCallEvm*(args: TransactionArgs,
const globalGasCap = 0 # TODO: globalGasCap should configurable by user
let params = ? toCallParams(vmState, args, globalGasCap, header.baseFeePerGas)
var dbTx = com.db.ctx.newTransaction()
var dbTx = com.db.ctx.txFrameBegin()
defer: dbTx.dispose() # always dispose state changes
ok(runComputation(params, CallResult))
@ -75,7 +75,7 @@ proc rpcEstimateGas*(args: TransactionArgs,
hi : GasInt = GasInt args.gas.get(0.Quantity)
cap: GasInt
var dbTx = com.db.ctx.newTransaction()
var dbTx = com.db.ctx.txFrameBegin()
defer: dbTx.dispose() # always dispose state changes
# Determine the highest gas limit can be used during the estimation.

View File

@ -123,7 +123,7 @@ suite "Aristo compute":
check:
db.mergeAccountRecord(k, v) == Result[bool, AristoError].ok(true)
check db.txStow(1, true).isOk()
check db.txPersist(1).isOk()
check db.computeKeys(root).isOk()

View File

@ -213,10 +213,7 @@ proc schedStow*(
filterMeter = if db.balancer.isNil: 0
else: db.balancer.sTab.len + db.balancer.kMap.len
persistent = MaxFilterBulk < max(layersMeter, filterMeter)
if persistent:
db.persist()
else:
db.stow()
db.persist()
# ------------------

View File

@ -55,7 +55,7 @@ proc saveToBackend(
xCheckRc rc.error == 0
block:
let rc = db.txTop()
let rc = db.txFrameTop()
xCheckRc rc.error == 0
tx = rc.value
@ -72,7 +72,7 @@ proc saveToBackend(
xCheckRc rc.error == 0
block:
let rc = db.txTop()
let rc = db.txFrameTop()
xCheckErr rc.value.level < 0 # force error
block:
@ -80,7 +80,7 @@ proc saveToBackend(
xCheckRc rc.error == 0
# Update layers to original level
tx = db.txBegin().value.to(AristoDbRef).txBegin().value
tx = db.txFrameBegin().value.to(AristoDbRef).txFrameBegin().value
true
@ -120,7 +120,7 @@ proc testMergeProofAndKvpList*(
# ps = PartStateRef.init(db)
# # Start transaction (double frame for testing)
# tx = ps.db.txBegin().value.to(AristoDbRef).txBegin().value
# tx = ps.db.txFrameBegin().value.to(AristoDbRef).txFrameBegin().value
# xCheck tx.isTop()
# # Update root

View File

@ -109,7 +109,7 @@ proc randomisedLeafs(
proc innerCleanUp(db: var AristoDbRef): bool {.discardable.} =
## Defer action
if not db.isNil:
let rx = db.txTop()
let rx = db.txFrameTop()
if rx.isOk:
let rc = rx.value.collapse(commit=false)
xCheckRc rc.error == 0
@ -140,7 +140,7 @@ proc saveToBackend(
xCheckRc rc.error == 0
block:
let rc = db.txTop()
let rc = db.txFrameTop()
xCheckRc rc.error == 0
tx = rc.value
@ -157,7 +157,7 @@ proc saveToBackend(
xCheckRc rc.error == 0
block:
let rc = db.txTop()
let rc = db.txFrameTop()
xCheckErr rc.value.level < 0 # force error
block:
@ -170,7 +170,7 @@ proc saveToBackend(
noisy.say "***", "saveToBackend (8)", " debugID=", debugID
# Update layers to original level
tx = db.txBegin().value.to(AristoDbRef).txBegin().value
tx = db.txFrameBegin().value.to(AristoDbRef).txFrameBegin().value
true
@ -268,8 +268,8 @@ proc testTxMergeAndDeleteOneByOne*(
# AristoDbRef.init(MemBackendRef)
# # Start transaction (double frame for testing)
# xCheck db.txTop.isErr
# var tx = db.txBegin().value.to(AristoDbRef).txBegin().value
# xCheck db.txFrameTop.isErr
# var tx = db.txFrameBegin().value.to(AristoDbRef).txFrameBegin().value
# xCheck tx.isTop()
# xCheck tx.level == 2
@ -375,8 +375,8 @@ proc testTxMergeAndDeleteSubTree*(
# AristoDbRef.init(MemBackendRef)
# # Start transaction (double frame for testing)
# xCheck db.txTop.isErr
# var tx = db.txBegin().value.to(AristoDbRef).txBegin().value
# xCheck db.txFrameTop.isErr
# var tx = db.txFrameBegin().value.to(AristoDbRef).txFrameBegin().value
# xCheck tx.isTop()
# xCheck tx.level == 2

View File

@ -116,7 +116,7 @@ proc forkedChainMain*() =
blk2 = cc.makeBlk(2, blk1)
blk3 = cc.makeBlk(3, blk2)
dbTx = cc.db.ctx.newTransaction()
dbTx = cc.db.ctx.txFrameBegin()
blk4 = cc.makeBlk(4, blk3)
blk5 = cc.makeBlk(5, blk4)
blk6 = cc.makeBlk(6, blk5)

View File

@ -212,7 +212,7 @@ proc runTrial3Survive(env: TestEnv, ledger: LedgerRef; inx: int; noisy = false)
let eAddr = env.txs[inx].getRecipient
block:
let dbTx = env.xdb.ctx.newTransaction()
let dbTx = env.xdb.ctx.txFrameBegin()
block:
let accTx = ledger.beginSavepoint
@ -228,7 +228,7 @@ proc runTrial3Survive(env: TestEnv, ledger: LedgerRef; inx: int; noisy = false)
dbTx.rollback()
block:
let dbTx = env.xdb.ctx.newTransaction()
let dbTx = env.xdb.ctx.txFrameBegin()
block:
let accTx = ledger.beginSavepoint
@ -247,7 +247,7 @@ proc runTrial4(env: TestEnv, ledger: LedgerRef; inx: int; rollback: bool) =
let eAddr = env.txs[inx].getRecipient
block:
let dbTx = env.xdb.ctx.newTransaction()
let dbTx = env.xdb.ctx.txFrameBegin()
block:
let accTx = ledger.beginSavepoint
@ -277,7 +277,7 @@ proc runTrial4(env: TestEnv, ledger: LedgerRef; inx: int; rollback: bool) =
dbTx.commit()
block:
let dbTx = env.xdb.ctx.newTransaction()
let dbTx = env.xdb.ctx.txFrameBegin()
block:
let accTx = ledger.beginSavepoint
@ -350,14 +350,14 @@ proc runLedgerTransactionTests(noisy = true) =
test &"Run {env.txi.len} two-step trials with rollback":
for n in env.txi:
let dbTx = env.xdb.ctx.newTransaction()
let dbTx = env.xdb.ctx.txFrameBegin()
defer: dbTx.dispose()
let ledger = env.com.getLedger()
env.runTrial2ok(ledger, n)
test &"Run {env.txi.len} three-step trials with rollback":
for n in env.txi:
let dbTx = env.xdb.ctx.newTransaction()
let dbTx = env.xdb.ctx.txFrameBegin()
defer: dbTx.dispose()
let ledger = env.com.getLedger()
env.runTrial3(ledger, n, rollback = true)
@ -365,21 +365,21 @@ proc runLedgerTransactionTests(noisy = true) =
test &"Run {env.txi.len} three-step trials with extra db frame rollback" &
" throwing Exceptions":
for n in env.txi:
let dbTx = env.xdb.ctx.newTransaction()
let dbTx = env.xdb.ctx.txFrameBegin()
defer: dbTx.dispose()
let ledger = env.com.getLedger()
env.runTrial3Survive(ledger, n, noisy)
test &"Run {env.txi.len} tree-step trials without rollback":
for n in env.txi:
let dbTx = env.xdb.ctx.newTransaction()
let dbTx = env.xdb.ctx.txFrameBegin()
defer: dbTx.dispose()
let ledger = env.com.getLedger()
env.runTrial3(ledger, n, rollback = false)
test &"Run {env.txi.len} four-step trials with rollback and db frames":
for n in env.txi:
let dbTx = env.xdb.ctx.newTransaction()
let dbTx = env.xdb.ctx.txFrameBegin()
defer: dbTx.dispose()
let ledger = env.com.getLedger()
env.runTrial4(ledger, n, rollback = true)