Skip to content

Commit

Permalink
Append state before save() to the Aristo descriptor
Browse files Browse the repository at this point in the history
why:
  This stae was previously returned by the function. Appending it to
  a field of the Aristo descriptor seems easier to handle.
  • Loading branch information
mjfh committed Jul 4, 2023
1 parent 9a30481 commit 278ad8c
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 70 deletions.
26 changes: 16 additions & 10 deletions nimbus/db/aristo/aristo_desc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -35,25 +35,31 @@ export
aristo_types_structural

type
AristoChangeLogRef* = ref object
## Change log: database state before backend saving.
root*: HashKey ## Previous hash key for `VertexID(1)`
leafs*: Table[LeafTie,PayloadRef] ## Changed leafs after merge into backend

AristoLayerRef* = ref object
## Hexary trie database layer structures. Any layer holds the full
## change relative to the backend.
sTab*: Table[VertexID,VertexRef] ## Structural vertex table
lTab*: Table[LeafTie,VertexID] ## Direct access, path to leaf vertex
kMap*: Table[VertexID,HashLabel] ## Merkle hash key mapping
pAmk*: Table[HashLabel,VertexID] ## Reverse `kMap` entries, hash key lookup
pPrf*: HashSet[VertexID] ## Locked vertices (proof nodes)
vGen*: seq[VertexID] ## Unique vertex ID generator
sTab*: Table[VertexID,VertexRef] ## Structural vertex table
lTab*: Table[LeafTie,VertexID] ## Direct access, path to leaf vertex
kMap*: Table[VertexID,HashLabel] ## Merkle hash key mapping
pAmk*: Table[HashLabel,VertexID] ## Reverse `kMap` entries, hash key lookup
pPrf*: HashSet[VertexID] ## Locked vertices (proof nodes)
vGen*: seq[VertexID] ## Unique vertex ID generator

AristoDbRef* = ref AristoDbObj
AristoDbObj* = object
## Set of database layers, supporting transaction frames
top*: AristoLayerRef ## Database working layer, mutable
stack*: seq[AristoLayerRef] ## Stashed immutable parent layers
backend*: AristoBackendRef ## Backend database (may well be `nil`)
top*: AristoLayerRef ## Database working layer, mutable
stack*: seq[AristoLayerRef] ## Stashed immutable parent layers
backend*: AristoBackendRef ## Backend database (may well be `nil`)
history*: seq[AristoChangeLogRef] ## Backend saving history

# Debugging data below, might go away in future
xMap*: Table[HashLabel,VertexID] ## For pretty printing, extends `pAmk`
xMap*: Table[HashLabel,VertexID] ## For pretty printing, extends `pAmk`

# ------------------------------------------------------------------------------
# Public helpers
Expand Down
4 changes: 4 additions & 0 deletions nimbus/db/aristo/aristo_desc/aristo_error.nim
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,12 @@ type

# Save permanently, `save()`
SaveBackendMissing
SaveStateRootMissing
SaveLeafVidRepurposed

# Pop layer, `pop()`
PopStackUnderflow

# Get functions form `aristo_get.nim`
GetLeafNotFound

Expand Down
108 changes: 60 additions & 48 deletions nimbus/db/aristo/aristo_layer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@ import
stew/results,
"."/[aristo_desc, aristo_get, aristo_vid]

type
DeltaHistoryRef* = ref object
## Change history for backend saving
leafs*: Table[LeafTie,PayloadRef] ## Changed leafs after merge into backend

# ------------------------------------------------------------------------------
# Private functions
# ------------------------------------------------------------------------------
Expand All @@ -34,63 +29,78 @@ proc cpy(layer: AristoLayerRef): AristoLayerRef =
# Public functions
# ------------------------------------------------------------------------------

proc push*(db: AristoDbRef) =
## Save a copy of the current delta state on the stack of non-persistent
## state layers.
db.stack.add db.top.cpy
proc retool*(
db: AristoDbRef;
flushStack = false;
): Result[void,AristoError] =
## This function re-initialises the top layer cache to its default value. It
## acts as sort of a `rollback()` without a transaction.
##
if 0 < db.stack.len and not flushStack:
db.top = db.stack[^1].cpy

else:
# Initialise new top layer from the backend
if db.backend.isNil:
db.top = AristoLayerRef()
else:
let rc = db.backend.getIdgFn()
if rc.isErr:
return err(rc.error)
db.top = AristoLayerRef(vGen: rc.value)
if flushStack:
db.stack.setLen(0)

proc pop*(db: AristoDbRef; merge = true): AristoError =
## Remove the current state later and merge it into the next layer if the
## argument `merge` is `true`, and discard it otherwise. The next layer then
## becomes the current state.
##
## Note that merging is sort of a virtual action because the top layer has
## always the full latest non-persistent delta state. This implies that in
## the case of *merging* just the first parent layer will be discarded.
##
if 0 < db.stack.len:
if not merge:
# Roll back to parent layer state
db.top = db.stack[^1]
db.stack.setLen(db.stack.len-1)

elif merge:
# Accept as-is (there is no parent layer)
discard
proc push*(db: var AristoDbRef) =
## Save the current cache state on the stack and continue working on a copy
## of that state.
##
db.stack.add db.top.cpy

elif db.backend.isNil:
db.top = AristoLayerRef()
proc pop*(
db: AristoDbRef;
merge = true;
): Result[void,(VertexID,AristoError)] =
## Reduce the cache state stack. If the argument `merge` is set `true`, the
## current top layer cache remains active and the top item of the stack is
## discarded. Otherwise if `merge` is set `false`, top item of the stack is
## popped onto current top layer cache (so replacing it.)
##
if db.stack.len == 0:
return err((VertexID(0),PopStackUnderflow))

else:
# Initialise new top layer from the backend
let rc = db.backend.getIdgFn()
if rc.isErr:
return rc.error
db.top = AristoLayerRef(vGen: rc.value)
if not merge:
# Roll back to parent layer state.
db.top = db.stack[^1]
db.stack.setLen(db.stack.len-1)

AristoError(0)
ok()


proc save*(
db: AristoDbRef; # Database to be updated
clear = true; # Clear current top level cache
): Result[DeltaHistoryRef,(VertexID,AristoError)] =
## Save top layer into persistent database. There is no check whether the
## current layer is fully consistent as a Merkle Patricia Tree. It is
## advised to run `hashify()` on the top layer before calling `save()`.
): Result[void,(VertexID,AristoError)] =
## Save the top layer cache onto the persistent database. There is no check
## whether the current layer is fully consistent as a Merkle Patricia Tree.
## It is advised to run `hashify()` on the top layer before calling `save()`.
##
## After successful storage, all parent layers are cleared. The top layer
## is also cleared if the `clear` flag is set `true`.
## After successful storage, all parent layers are cleared as well as the
## the top layer cache.
##
## Upon successful return, the previous state of the backend data is returned
## relative to the changes made.
## Upon successful return, the previous state of the backend data is saved
## as a new entry in `history` field of the argument descriptor `db`.
##
let be = db.backend
if be.isNil:
return err((VertexID(0),SaveBackendMissing))

let hst = DeltaHistoryRef() # Change history
# Get Merkle hash for state root
let key = db.getKey VertexID(1)
if not key.isValid:
return err((VertexID(1),SaveStateRootMissing))

let hst = AristoChangeLogRef(root: key) # Change history, previous state

# Record changed `Leaf` nodes into the history table
for (lky,vid) in db.top.lTab.pairs:
Expand Down Expand Up @@ -122,10 +132,12 @@ proc save*(

# Delete stack and clear top
db.stack.setLen(0)
if clear:
db.top = AristoLayerRef(vGen: db.top.vGen)
db.top = AristoLayerRef(vGen: db.top.vGen)

# Save history
db.history.add hst

ok hst
ok()

# ------------------------------------------------------------------------------
# End
Expand Down
13 changes: 5 additions & 8 deletions tests/test_aristo/test_backend.nim
Original file line number Diff line number Diff line change
Expand Up @@ -196,21 +196,18 @@ proc test_backendConsistency*(
rdbPreSaveBackend = rdb.to(RdbBackendRef).pp(ndb)

# Store onto backend database
let mdbHist = block:
block:
#noisy.say "***", "db-dump\n ", mdb.pp
let rc = mdb.save
if rc.isErr:
check rc.error == (0,0)
return
rc.value

if doRdbOk:
let rdbHist = block:
let rc = rdb.save
if rc.isErr:
check rc.error == (0,0)
return
rc.value
let rc = rdb.save
if rc.isErr:
check rc.error == (0,0)
return

if not ndb.top.verify(mdb.to(MemBackendRef), noisy):
when true and false:
Expand Down
6 changes: 2 additions & 4 deletions tests/test_aristo/test_merge.nim
Original file line number Diff line number Diff line change
Expand Up @@ -208,12 +208,11 @@ proc test_mergeKvpList*(
check rc == Result[void,(VertexID,AristoError)].ok()
return

let rdbHist = block:
block:
let rc = db.save
if rc.isErr:
check rc.error == (0,0)
return
rc.value

when true and false:
noisy.say "*** kvp(5)", "<", n, "/", lstLen-1, ">",
Expand Down Expand Up @@ -351,12 +350,11 @@ proc test_mergeProofAndKvpList*(
check rc.error == (VertexID(0),AristoError(0))
return

let rdbHist = block:
block:
let rc = db.save
if rc.isErr:
check rc.error == (0,0)
return
rc.value

when true and false:
noisy.say "***", "proofs(5) <", n, "/", lstLen-1, ">",
Expand Down

0 comments on commit 278ad8c

Please sign in to comment.