Skip to content

Commit

Permalink
Aristo db implement distributed backend access (#1688)
Browse files Browse the repository at this point in the history
* Fix hashing algorithm

why:
  Particular case where a sub-tree is on the backend, linked by an
  Extension vertex to the top level.

* Update backend verification to report `dirty` top layer

* Implement distributed merge of backend filters

* Implement distributed backend access management

details:
  Implemented and tested as described in chapter 5 of the `README.md`
  file.
  • Loading branch information
mjfh authored Aug 17, 2023
1 parent 3f0506b commit 3078c20
Show file tree
Hide file tree
Showing 13 changed files with 1,069 additions and 153 deletions.
18 changes: 11 additions & 7 deletions nimbus/db/aristo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ should be allocated in the structural table associated with the zero key.
db | stack[n] | |
desc | : | | optional passive delta layers, handled by
obj | stack[1] | | transaction management (can be used to
| | stack[0] | | successively replace the top layer)
| | stack[0] | | successively recover the top layer)
| +----------+ v
| +----------+
| | roFilter | optional read-only backend filter
Expand Down Expand Up @@ -449,11 +449,15 @@ Nevertheless, *(8)* can alse be transformed by committing and saving *tx2*
| ø, ø | tx2+PBE
| tx3, ~tx2 |

As *(11)* and *(13)* represent the same API, one has
As *(11)* and *(13)* represent the same API, one has

tx2+PBE == tx1+(tx2+~tx1)+PBE because of the middle rows (14)
~tx2 == ~tx1+~(tx2+~tx1) because of (14) (15)
tx2+PBE =~ tx1+(tx2+~tx1)+PBE because of the middle rows (14)
~tx2 =~ ~tx1+~(tx2+~tx1) because of (14) (15)

which shows some distributive property in *(14)* and commutative property in
*(15)* for this example. In particulat it might be handy for testing/verifying
against this example.
which looks like some distributive property in *(14)* and commutative
property in *(15)* for this example (but it is not straight algebraically.)
The *=~* operator above indicates that the representations are equivalent in
the sense that they have the same effect on the backend database (looks a
bit like residue classes.)

It might be handy for testing/verifying an implementation using this example.
3 changes: 3 additions & 0 deletions nimbus/db/aristo/aristo_check/check_be.nim
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ proc checkBE*[T: RdbBackendRef|MemBackendRef|VoidBackendRef](

# Check cache against backend
if cache:
if db.top.dirty:
return err((VertexID(0),CheckBeCacheIsDirty))

# Check structural table
for (vid,vtx) in db.top.sTab.pairs:
# A `kMap[]` entry must exist.
Expand Down
22 changes: 16 additions & 6 deletions nimbus/db/aristo/aristo_debug.nim
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ proc ppSTab(
"{" & sTab.sortedKeys
.mapIt((it, sTab.getOrVoid it))
.mapIt("(" & it[0].ppVid & "," & it[1].ppVtx(db,it[0]) & ")")
.join("," & indent.toPfx(1)) & "}"
.join(indent.toPfx(2)) & "}"

proc ppLTab(
lTab: Table[LeafTie,VertexID];
Expand All @@ -210,7 +210,7 @@ proc ppLTab(
"{" & lTab.sortedKeys
.mapIt((it, lTab.getOrVoid it))
.mapIt("(" & it[0].ppLeafTie(db) & "," & it[1].ppVid & ")")
.join("," & indent.toPfx(1)) & "}"
.join(indent.toPfx(2)) & "}"

proc ppPPrf(pPrf: HashSet[VertexID]): string =
"{" & pPrf.sortedKeys.mapIt(it.ppVid).join(",") & "}"
Expand Down Expand Up @@ -324,9 +324,11 @@ proc ppFilter(fl: AristoFilterRef; db: AristoDbRef; indent: int): string =
pfx1 = indent.toPfx(1)
pfx2 = indent.toPfx(2)
result = "<filter>"
if db.roFilter.isNil:
if fl.isNil:
result &= " n/a"
return
result &= pfx & "trg(" & fl.trg.ppKey & ")"
result &= pfx & "src(" & fl.src.ppKey & ")"
result &= pfx & "vGen" & pfx1 & "["
if fl.vGen.isSome:
result &= fl.vGen.unsafeGet.mapIt(it.ppVid).join(",")
Expand Down Expand Up @@ -361,7 +363,7 @@ proc ppBeOnly[T](be: T; db: AristoDbRef; indent: int): string =

proc ppBe[T](be: T; db: AristoDbRef; indent: int): string =
## backend + filter
db.roFilter.ppFilter(db, indent) & indent.toPfx & be.ppBeOnly(db,indent)
db.roFilter.ppFilter(db, indent+1) & indent.toPfx & be.ppBeOnly(db,indent+1)

proc ppLayer(
layer: AristoLayerRef;
Expand All @@ -374,8 +376,8 @@ proc ppLayer(
indent = 4;
): string =
let
pfx1 = indent.toPfx
pfx2 = indent.toPfx(1)
pfx1 = indent.toPfx(1)
pfx2 = indent.toPfx(2)
nOKs = sTabOk.ord + lTabOk.ord + kMapOk.ord + pPrfOk.ord + vGenOk.ord
tagOk = 1 < nOKs
var
Expand All @@ -392,6 +394,8 @@ proc ppLayer(
rc

if not layer.isNil:
if 2 < nOKs:
result &= "<layer>".doPrefix(false)
if vGenOk:
let
tLen = layer.vGen.len
Expand Down Expand Up @@ -613,6 +617,12 @@ proc pp*(
): string =
db.top.pp(db, xTabOk=xTabOk, kMapOk=kMapOk, other=other, indent=indent)

proc pp*(
filter: AristoFilterRef;
db = AristoDbRef();
indent = 4;
): string =
filter.ppFilter(db, indent)

proc pp*(
be: TypedBackendRef;
Expand Down
25 changes: 20 additions & 5 deletions nimbus/db/aristo/aristo_desc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
## Aristo DB -- a Patricia Trie with labeled edges
## ===============================================
##
## These data structures allows to overlay the *Patricia Trie* with *Merkel
## These data structures allow to overlay the *Patricia Trie* with *Merkel
## Trie* hashes. See the `README.md` in the `aristo` folder for documentation.
##
## Some semantic explanations;
Expand All @@ -22,7 +22,7 @@
{.push raises: [].}

import
std/tables,
std/[hashes, sets, tables],
eth/common,
./aristo_constants,
./aristo_desc/[
Expand All @@ -31,8 +31,8 @@ import
from ./aristo_desc/aristo_types_backend
import AristoBackendRef

# Not auto-exporting backend
export
# Not auto-exporting backend
aristo_constants, aristo_error, aristo_types_identifiers,
aristo_types_structural

Expand All @@ -44,20 +44,31 @@ type
txUid*: uint ## Unique ID among transactions
level*: int ## Stack index for this transaction

AristoDudesRef* = ref object
case rwOk*: bool
of true:
roDudes*: HashSet[AristoDbRef] ## Read-only peers
else:
rwDb*: AristoDbRef ## Link to writable descriptor

AristoDbRef* = ref AristoDbObj
AristoDbObj* = object
## Set of database layers, supporting transaction frames
## Three tier database object supporting distributed instances.
top*: AristoLayerRef ## Database working layer, mutable
stack*: seq[AristoLayerRef] ## Stashed immutable parent layers
roFilter*: AristoFilterRef ## Apply read filter (locks writing)
backend*: AristoBackendRef ## Backend database (may well be `nil`)

txRef*: AristoTxRef ## Latest active transaction
txUidGen*: uint ## Tx-relative unique number generator
dudes*: AristoDudesRef ## Related DB descriptors

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

AristoDbAction* = proc(db: AristoDbRef) {.gcsafe, raises: [CatchableError].}
## Generic call back function/closure.

# ------------------------------------------------------------------------------
# Public helpers
# ------------------------------------------------------------------------------
Expand Down Expand Up @@ -98,9 +109,13 @@ func isValid*(vid: VertexID): bool =
# Public functions, miscellaneous
# ------------------------------------------------------------------------------

# Hash set helper
func hash*(db: AristoDbRef): Hash =
## Table/KeyedQueue/HashSet mixin
cast[pointer](db).hash

# Note that the below `init()` function cannot go into
# `aristo_types_identifiers` as this would result in a circular import.

func init*(key: var HashKey; data: openArray[byte]): bool =
## Import argument `data` into `key` which must have length either `32`, or
## `0`. The latter case is equivalent to an all zero byte array of size `32`.
Expand Down
10 changes: 8 additions & 2 deletions nimbus/db/aristo/aristo_desc/aristo_error.nim
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ type
HashifyCannotComplete
HashifyCannotHashRoot
HashifyExistingHashMismatch
HashifyLeafToRootAllFailed
HashifyDownVtxlevelExceeded
HashifyDownVtxLeafUnexpected
HashifyRootHashMismatch
HashifyRootVidMismatch
HashifyVidCircularDependence
Expand Down Expand Up @@ -131,6 +132,7 @@ type
CheckBeKeyMismatch
CheckBeGarbledVGen

CheckBeCacheIsDirty
CheckBeCacheKeyMissing
CheckBeCacheKeyNonEmpty
CheckBeCacheVidUnsynced
Expand Down Expand Up @@ -167,9 +169,12 @@ type
DelVidStaleVtx

# Functions from `aristo_filter.nim`
FilRoBackendOrMissing
FilStateRootMissing
FilStateRootMismatch
FilPrettyPointlessLayer
FilDudeFilterUpdateError
FilNotReadOnlyDude

# Get functions form `aristo_get.nim`
GetLeafNotFound
Expand All @@ -192,8 +197,9 @@ type

# Transaction wrappers
TxArgStaleTx
TxBackendMissing
TxRoBackendOrMissing
TxNoPendingTx
TxPendingTx
TxNotTopTx
TxStackGarbled
TxStackUnderflow
Expand Down
10 changes: 10 additions & 0 deletions nimbus/db/aristo/aristo_desc/aristo_types_structural.nim
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,16 @@ proc dup*(layer: AristoLayerRef): AristoLayerRef =
for (k,v) in layer.sTab.pairs:
result.sTab[k] = v.dup

proc dup*(filter: AristoFilterRef): AristoFilterRef =
## Duplicate layer.
result = AristoFilterRef(
src: filter.src,
kMap: filter.kMap,
vGen: filter.vGen,
trg: filter.trg)
for (k,v) in filter.sTab.pairs:
result.sTab[k] = v.dup

proc to*(node: NodeRef; T: type VertexRef): T =
## Extract a copy of the `VertexRef` part from a `NodeRef`.
node.VertexRef.dup
Expand Down
Loading

0 comments on commit 3078c20

Please sign in to comment.