Simple stupid key-value table companion for Aristo DB (#1746)
why: Additional tables needed for the `CoreDB` object with separate key-value table and MPT. details: + Stripped down copy of Aristo DB to have a similar look'n feel. Otherwise it is just a posh way for accessing `Table` objects or `RocksDB` data. + No unit tests yet, will be tested on the go.
This commit is contained in:
parent
8e46953390
commit
dda049cd43
|
@ -0,0 +1,28 @@
|
|||
# nimbus-eth1
|
||||
# Copyright (c) 2021 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 -- Standard interface
|
||||
## ============================
|
||||
##
|
||||
{.push raises: [].}
|
||||
|
||||
import kvt/[
|
||||
kvt_constants, kvt_init, kvt_tx, kvt_utils]
|
||||
export
|
||||
kvt_constants, kvt_init, kvt_tx, kvt_utils
|
||||
|
||||
import
|
||||
kvt/kvt_desc
|
||||
export
|
||||
KvtDbRef,
|
||||
KvtError,
|
||||
isValid
|
||||
|
||||
# End
|
|
@ -0,0 +1,17 @@
|
|||
# nimbus-eth1
|
||||
# Copyright (c) 2021 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
|
||||
../aristo/aristo_constants
|
||||
|
||||
export
|
||||
EmptyBlob
|
||||
|
||||
# End
|
|
@ -0,0 +1,62 @@
|
|||
# nimbus-eth1
|
||||
# Copyright (c) 2021 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 -- key-value table
|
||||
## =========================
|
||||
##
|
||||
{.push raises: [].}
|
||||
|
||||
import
|
||||
std/tables,
|
||||
eth/common,
|
||||
./kvt_constants,
|
||||
./kvt_desc/[desc_error, desc_structural]
|
||||
|
||||
from ./kvt_desc/desc_backend
|
||||
import BackendRef
|
||||
|
||||
# Not auto-exporting backend
|
||||
export
|
||||
kvt_constants, desc_error, desc_structural
|
||||
|
||||
type
|
||||
KvtTxRef* = ref object
|
||||
## Transaction descriptor
|
||||
db*: KvtDbRef ## Database descriptor
|
||||
parent*: KvtTxRef ## Previous transaction
|
||||
txUid*: uint ## Unique ID among transactions
|
||||
level*: int ## Stack index for this transaction
|
||||
|
||||
KvtDbRef* = ref KvtDbObj
|
||||
KvtDbObj* = object
|
||||
## Three tier database object supporting distributed instances.
|
||||
top*: LayerRef ## Database working layer, mutable
|
||||
stack*: seq[LayerRef] ## Stashed immutable parent layers
|
||||
backend*: BackendRef ## Backend database (may well be `nil`)
|
||||
|
||||
txRef*: KvtTxRef ## Latest active transaction
|
||||
txUidGen*: uint ## Tx-relative unique number generator
|
||||
|
||||
KvtDbAction* = proc(db: KvtDbRef) {.gcsafe, raises: [CatchableError].}
|
||||
## Generic call back function/closure.
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public helpers
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
func getOrVoid*(tab: Table[Blob,Blob]; w: Blob): Blob =
|
||||
tab.getOrDefault(w, EmptyBlob)
|
||||
|
||||
func isValid*(key: Blob): bool =
|
||||
key != EmptyBlob
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
|
@ -0,0 +1,71 @@
|
|||
# nimbus-eth1
|
||||
# Copyright (c) 2021 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 -- backend data types
|
||||
## ============================
|
||||
##
|
||||
|
||||
{.push raises: [].}
|
||||
|
||||
import
|
||||
eth/common,
|
||||
results,
|
||||
./desc_error
|
||||
|
||||
type
|
||||
GetKvpFn* =
|
||||
proc(key: Blob): Result[Blob,KvtError] {.gcsafe, raises: [].}
|
||||
## Generic backend database retrieval function
|
||||
|
||||
# -------------
|
||||
|
||||
PutHdlRef* = ref object of RootRef
|
||||
## Persistent database transaction frame handle. This handle is used to
|
||||
## wrap any of `PutVtxFn`, `PutKeyFn`, and `PutIdgFn` into and atomic
|
||||
## transaction frame. These transaction frames must not be interleaved
|
||||
## by any library function using the backend.
|
||||
|
||||
PutBegFn* =
|
||||
proc(): PutHdlRef {.gcsafe, raises: [].}
|
||||
## Generic transaction initialisation function
|
||||
|
||||
PutKvpFn* =
|
||||
proc(hdl: PutHdlRef; kvps: openArray[(Blob,Blob)]) {.gcsafe, raises: [].}
|
||||
## Generic backend database bulk storage function.
|
||||
|
||||
PutEndFn* =
|
||||
proc(hdl: PutHdlRef): Result[void,KvtError] {.gcsafe, raises: [].}
|
||||
## Generic transaction termination function
|
||||
|
||||
# -------------
|
||||
|
||||
CloseFn* =
|
||||
proc(flush: bool) {.gcsafe, raises: [].}
|
||||
## Generic destructor for the `Kvt DB` backend. The argument `flush`
|
||||
## indicates that a full database deletion is requested. If passed
|
||||
## `false` the outcome might differ depending on the type of backend
|
||||
## (e.g. in-memory backends would flush on close.)
|
||||
|
||||
# -------------
|
||||
|
||||
BackendRef* = ref object of RootRef
|
||||
## Backend interface.
|
||||
|
||||
getKvpFn*: GetKvpFn ## Read key-value pair
|
||||
|
||||
putBegFn*: PutBegFn ## Start bulk store session
|
||||
putKvpFn*: PutKvpFn ## Bulk store key-value pairs
|
||||
putEndFn*: PutEndFn ## Commit bulk store session
|
||||
|
||||
closeFn*: CloseFn ## Generic destructor
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
|
@ -0,0 +1,42 @@
|
|||
# nimbus-eth1
|
||||
# Copyright (c) 2021 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.
|
||||
|
||||
type
|
||||
KvtError* = enum
|
||||
NothingSerious = 0
|
||||
GenericError
|
||||
|
||||
GetNotFound
|
||||
KeyInvalid
|
||||
DataInvalid
|
||||
|
||||
# RocksDB backend
|
||||
RdbBeCantCreateDataDir
|
||||
RdbBeCantCreateBackupDir
|
||||
RdbBeCantCreateTmpDir
|
||||
RdbBeDriverInitError
|
||||
RdbBeDriverGetError
|
||||
RdbBeDriverDelError
|
||||
RdbBeCreateSstWriter
|
||||
RdbBeOpenSstWriter
|
||||
RdbBeAddSstWriter
|
||||
RdbBeFinishSstWriter
|
||||
RdbBeIngestSstWriter
|
||||
|
||||
# Transaction wrappers
|
||||
TxArgStaleTx
|
||||
TxBackendNotWritable
|
||||
TxNoPendingTx
|
||||
TxPendingTx
|
||||
TxNotTopTx
|
||||
TxStackGarbled
|
||||
TxStackUnderflow
|
||||
|
||||
# End
|
|
@ -0,0 +1,40 @@
|
|||
# nimbus-eth1
|
||||
# Copyright (c) 2021 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 -- structural data types
|
||||
## ===============================
|
||||
##
|
||||
{.push raises: [].}
|
||||
|
||||
import
|
||||
std/tables,
|
||||
eth/common
|
||||
|
||||
type
|
||||
LayerRef* = ref object
|
||||
## Kvt database layer structures. Any layer holds the full
|
||||
## change relative to the backend.
|
||||
tab*: Table[Blob,Blob] ## Structural table
|
||||
txUid*: uint ## Transaction identifier if positive
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public helpers, miscellaneous functions
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc dup*(layer: LayerRef): LayerRef =
|
||||
## Duplicate layer.
|
||||
result = LayerRef(
|
||||
txUid: layer.txUid)
|
||||
for (k,v) in layer.tab.pairs:
|
||||
result.tab[k] = v
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
|
@ -0,0 +1,21 @@
|
|||
# nimbus-eth1
|
||||
# Copyright (c) 2021 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.
|
||||
|
||||
## Constructors for Key-Value Table DB
|
||||
## ====================================
|
||||
##
|
||||
{.push raises: [].}
|
||||
|
||||
import
|
||||
./kvt_init/memory_only
|
||||
export
|
||||
memory_only
|
||||
|
||||
# End
|
|
@ -0,0 +1,61 @@
|
|||
# nimbus-eth1
|
||||
# Copyright (c) 2021 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.
|
||||
|
||||
{.push raises: [].}
|
||||
|
||||
import
|
||||
../../aristo/aristo_init/init_common,
|
||||
../kvt_desc,
|
||||
../kvt_desc/desc_backend
|
||||
|
||||
export
|
||||
BackendType # borrowed from Aristo
|
||||
|
||||
const
|
||||
verifyIxId = true # and false
|
||||
## Enforce session tracking
|
||||
|
||||
type
|
||||
TypedBackendRef* = ref object of BackendRef
|
||||
beKind*: BackendType ## Backend type identifier
|
||||
when verifyIxId:
|
||||
txGen: uint ## Transaction ID generator (for debugging)
|
||||
txId: uint ## Active transaction ID (for debugging)
|
||||
|
||||
TypedPutHdlRef* = ref object of PutHdlRef
|
||||
error*: KvtError ## Track error while collecting transaction
|
||||
when verifyIxId:
|
||||
txId: uint ## Transaction ID (for debugging)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public helpers
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc beginSession*(hdl: TypedPutHdlRef; db: TypedBackendRef) =
|
||||
when verifyIxId:
|
||||
doAssert db.txId == 0
|
||||
if db.txGen == 0:
|
||||
db.txGen = 1
|
||||
db.txId = db.txGen
|
||||
hdl.txId = db.txGen
|
||||
db.txGen.inc
|
||||
|
||||
proc verifySession*(hdl: TypedPutHdlRef; db: TypedBackendRef) =
|
||||
when verifyIxId:
|
||||
doAssert db.txId == hdl.txId
|
||||
|
||||
proc finishSession*(hdl: TypedPutHdlRef; db: TypedBackendRef) =
|
||||
when verifyIxId:
|
||||
doAssert db.txId == hdl.txId
|
||||
db.txId = 0
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
|
@ -0,0 +1,149 @@
|
|||
# nimbus-eth1
|
||||
# Copyright (c) 2021 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.
|
||||
|
||||
## In-memory backend for Kvt DB
|
||||
## ============================
|
||||
##
|
||||
## The iterators provided here are currently available only by direct
|
||||
## backend access
|
||||
## ::
|
||||
## import
|
||||
## kvt/kvt_init,
|
||||
## kvt/kvt_init/kvt_memory
|
||||
##
|
||||
## let rc = newKvtDbRef(BackendMemory)
|
||||
## if rc.isOk:
|
||||
## let be = rc.value.to(MemBackendRef)
|
||||
## for (n, key, vtx) in be.walkVtx:
|
||||
## ...
|
||||
##
|
||||
{.push raises: [].}
|
||||
|
||||
import
|
||||
std/tables,
|
||||
chronicles,
|
||||
eth/common,
|
||||
results,
|
||||
../kvt_desc,
|
||||
../kvt_desc/desc_backend,
|
||||
./init_common
|
||||
|
||||
type
|
||||
MemBackendRef* = ref object of TypedBackendRef
|
||||
## Inheriting table so access can be extended for debugging purposes
|
||||
tab: Table[Blob,Blob] ## Structural key-value table
|
||||
|
||||
MemPutHdlRef = ref object of TypedPutHdlRef
|
||||
tab: Table[Blob,Blob]
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Private helpers
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
template logTxt(info: static[string]): static[string] =
|
||||
"MemoryDB " & info
|
||||
|
||||
|
||||
proc newSession(db: MemBackendRef): MemPutHdlRef =
|
||||
new result
|
||||
result.TypedPutHdlRef.beginSession db
|
||||
|
||||
proc getSession(hdl: PutHdlRef; db: MemBackendRef): MemPutHdlRef =
|
||||
hdl.TypedPutHdlRef.verifySession db
|
||||
hdl.MemPutHdlRef
|
||||
|
||||
proc endSession(hdl: PutHdlRef; db: MemBackendRef): MemPutHdlRef =
|
||||
hdl.TypedPutHdlRef.finishSession db
|
||||
hdl.MemPutHdlRef
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Private functions: interface
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc getKvpFn(db: MemBackendRef): GetKvpFn =
|
||||
result =
|
||||
proc(key: Blob): Result[Blob,KvtError] =
|
||||
if key.len == 0:
|
||||
return err(KeyInvalid)
|
||||
let data = db.tab.getOrVoid key
|
||||
if data.isValid:
|
||||
return ok(data)
|
||||
err(GetNotFound)
|
||||
|
||||
# -------------
|
||||
|
||||
proc putBegFn(db: MemBackendRef): PutBegFn =
|
||||
result =
|
||||
proc(): PutHdlRef =
|
||||
db.newSession()
|
||||
|
||||
proc putKvpFn(db: MemBackendRef): PutKvpFn =
|
||||
result =
|
||||
proc(hdl: PutHdlRef; kvps: openArray[(Blob,Blob)]) =
|
||||
let hdl = hdl.getSession db
|
||||
if hdl.error == KvtError(0):
|
||||
for (k,v) in kvps:
|
||||
if k.isValid:
|
||||
hdl.tab[k] = v
|
||||
else:
|
||||
hdl.error = KeyInvalid
|
||||
|
||||
proc putEndFn(db: MemBackendRef): PutEndFn =
|
||||
result =
|
||||
proc(hdl: PutHdlRef): Result[void,KvtError] =
|
||||
let hdl = hdl.endSession db
|
||||
if hdl.error != KvtError(0):
|
||||
debug logTxt "putEndFn: key/value failed", error=hdl.error
|
||||
return err(hdl.error)
|
||||
|
||||
for (k,v) in hdl.tab.pairs:
|
||||
db.tab[k] = v
|
||||
|
||||
ok()
|
||||
|
||||
# -------------
|
||||
|
||||
proc closeFn(db: MemBackendRef): CloseFn =
|
||||
result =
|
||||
proc(ignore: bool) =
|
||||
discard
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public functions
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc memoryBackend*: BackendRef =
|
||||
let db = MemBackendRef(
|
||||
beKind: BackendMemory)
|
||||
|
||||
db.getKvpFn = getKvpFn db
|
||||
|
||||
db.putBegFn = putBegFn db
|
||||
db.putKvpFn = putKvpFn db
|
||||
db.putEndFn = putEndFn db
|
||||
|
||||
db.closeFn = closeFn db
|
||||
|
||||
db
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public iterators (needs direct backend access)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
iterator walk*(
|
||||
be: MemBackendRef;
|
||||
): tuple[key: Blob, data: Blob] =
|
||||
## Walk over all key-value pairs of the database.
|
||||
for (k,v) in be.tab.pairs:
|
||||
yield (k,v)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
|
@ -0,0 +1,88 @@
|
|||
# nimbus-eth1
|
||||
# Copyright (c) 2021 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.
|
||||
|
||||
## Non persistent constructors for Kvt DB
|
||||
## ======================================
|
||||
##
|
||||
{.push raises: [].}
|
||||
|
||||
import
|
||||
std/sets,
|
||||
results,
|
||||
../kvt_desc,
|
||||
../kvt_desc/desc_backend,
|
||||
"."/[init_common, memory_db]
|
||||
|
||||
type
|
||||
VoidBackendRef* = ref object of TypedBackendRef
|
||||
## Dummy descriptor type, will typically used as `nil` reference
|
||||
|
||||
export
|
||||
BackendType,
|
||||
MemBackendRef
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public database constuctors, destructor
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc newKvtDbRef*(
|
||||
backend: static[BackendType];
|
||||
): KvtDbRef =
|
||||
## Simplified prototype for `BackendNone` and `BackendMemory` type backend.
|
||||
##
|
||||
when backend == BackendVoid:
|
||||
KvtDbRef(top: LayerRef())
|
||||
|
||||
elif backend == BackendMemory:
|
||||
KvtDbRef(top: LayerRef(), backend: memoryBackend(qidLayout))
|
||||
|
||||
elif backend == BackendRocksDB:
|
||||
{.error: "Kvt DB backend \"BackendRocksDB\" needs basePath argument".}
|
||||
|
||||
else:
|
||||
{.error: "Unknown/unsupported Kvt DB backend".}
|
||||
|
||||
# -----------------
|
||||
|
||||
proc finish*(db: KvtDbRef; flush = false) =
|
||||
## Backend destructor. The argument `flush` indicates that a full database
|
||||
## deletion is requested. If set `false` the outcome might differ depending
|
||||
## on the type of backend (e.g. the `BackendMemory` backend will always
|
||||
## flush on close.)
|
||||
##
|
||||
## This distructor may be used on already *destructed* descriptors.
|
||||
##
|
||||
if not db.isNil:
|
||||
if not db.backend.isNil:
|
||||
db.backend.closeFn flush
|
||||
db[] = KvtDbObj(top: LayerRef())
|
||||
|
||||
# -----------------
|
||||
|
||||
proc to*[W: TypedBackendRef|MemBackendRef|VoidBackendRef](
|
||||
db: KvtDbRef;
|
||||
T: type W;
|
||||
): T =
|
||||
## Handy helper for lew-level access to some backend functionality
|
||||
db.backend.T
|
||||
|
||||
proc kind*(
|
||||
be: BackendRef;
|
||||
): BackendType =
|
||||
## Retrieves the backend type symbol for a `TypedBackendRef` argument where
|
||||
## `BackendVoid` is returned for the`nil` backend.
|
||||
if be.isNil:
|
||||
BackendVoid
|
||||
else:
|
||||
be.TypedBackendRef.beKind
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
|
@ -0,0 +1,64 @@
|
|||
# nimbus-eth1
|
||||
# Copyright (c) 2021 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.
|
||||
|
||||
## Persistent constructor for Kvt DB
|
||||
## ====================================
|
||||
##
|
||||
## This module automatically pulls in the persistent backend library at the
|
||||
## linking stage (e.g. `rocksdb`) which can be avoided for pure memory DB
|
||||
## applications by importing `./kvt_init/memory_only` (rather than
|
||||
## `./kvt_init/persistent`.)
|
||||
##
|
||||
{.push raises: [].}
|
||||
|
||||
import
|
||||
results,
|
||||
../kvt_desc,
|
||||
"."/[init_common, rocks_db, memory_only]
|
||||
export
|
||||
RdbBackendRef,
|
||||
memory_only
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public database constuctors, destructor
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc newKvtDbRef*(
|
||||
backend: static[BackendType];
|
||||
basePath: string;
|
||||
): Result[KvtDbRef,KvtError] =
|
||||
## Generic constructor, `basePath` argument is ignored for `BackendNone` and
|
||||
## `BackendMemory` type backend database. Also, both of these backends
|
||||
## aways succeed initialising.
|
||||
##
|
||||
when backend == BackendRocksDB:
|
||||
ok KvtDbRef(top: LayerRef(vGen: vGen), backend: ? rocksDbBackend basePath)
|
||||
|
||||
elif backend == BackendVoid:
|
||||
{.error: "Use BackendNone.init() without path argument".}
|
||||
|
||||
elif backend == BackendMemory:
|
||||
{.error: "Use BackendMemory.init() without path argument".}
|
||||
|
||||
else:
|
||||
{.error: "Unknown/unsupported Kvt DB backend".}
|
||||
|
||||
# -----------------
|
||||
|
||||
proc to*[W: RdbBackendRef](
|
||||
db: KvtDbRef;
|
||||
T: type W;
|
||||
): T =
|
||||
## Handy helper for lew-level access to some backend functionality
|
||||
db.backend.T
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
|
@ -0,0 +1,180 @@
|
|||
# nimbus-eth1
|
||||
# Copyright (c) 2021 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.
|
||||
|
||||
## Rocksdb backend for Kvt DB
|
||||
## ==========================
|
||||
##
|
||||
## The iterators provided here are currently available only by direct
|
||||
## backend access
|
||||
## ::
|
||||
## import
|
||||
## kvt/kvt_init,
|
||||
## kvt/kvt_init/kvt_rocksdb
|
||||
##
|
||||
## let rc = KvtDb.init(BackendRocksDB, "/var/tmp")
|
||||
## if rc.isOk:
|
||||
## let be = rc.value.to(RdbBackendRef)
|
||||
## for (n, key, vtx) in be.walkVtx:
|
||||
## ...
|
||||
##
|
||||
{.push raises: [].}
|
||||
|
||||
import
|
||||
chronicles,
|
||||
eth/common,
|
||||
rocksdb,
|
||||
results,
|
||||
../kvt_desc,
|
||||
../kvt_desc/desc_backend,
|
||||
./init_common,
|
||||
./rocks_db/[rdb_desc, rdb_get, rdb_init, rdb_put, rdb_walk]
|
||||
|
||||
logScope:
|
||||
topics = "kvt-backend"
|
||||
|
||||
type
|
||||
RdbBackendRef* = ref object of TypedBackendRef
|
||||
rdb: RdbInst ## Allows low level access to database
|
||||
|
||||
RdbPutHdlRef = ref object of TypedPutHdlRef
|
||||
tab: Table[Blob,Blob] ## Transaction cache
|
||||
|
||||
const
|
||||
extraTraceMessages = false or true
|
||||
## Enabled additional logging noise
|
||||
|
||||
# ----------
|
||||
|
||||
maxOpenFiles = 512 ## Rocks DB setup, open files limit
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Private helpers
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
template logTxt(info: static[string]): static[string] =
|
||||
"RocksDB " & info
|
||||
|
||||
|
||||
proc newSession(db: RdbBackendRef): RdbPutHdlRef =
|
||||
new result
|
||||
result.TypedPutHdlRef.beginSession db
|
||||
|
||||
proc getSession(hdl: PutHdlRef; db: RdbBackendRef): RdbPutHdlRef =
|
||||
hdl.TypedPutHdlRef.verifySession db
|
||||
hdl.RdbPutHdlRef
|
||||
|
||||
proc endSession(hdl: PutHdlRef; db: RdbBackendRef): RdbPutHdlRef =
|
||||
hdl.TypedPutHdlRef.finishSession db
|
||||
hdl.RdbPutHdlRef
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Private functions: interface
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc getKvpFn(db: RdbBackendRef): GetKvpFn =
|
||||
result =
|
||||
proc(key: Blob): Result[Blob,KvtError] =
|
||||
if key.len == 0:
|
||||
return err(KeyInvalid)
|
||||
let rc = db.rdb.get key.toOpenArray(0,key.len-1)
|
||||
if rc.isErr:
|
||||
debug logTxt "getKvpFn() failed", key,
|
||||
error=rc.error[0], info=rc.error[1]
|
||||
return err(rc.error[0])
|
||||
|
||||
# Decode data record
|
||||
if 0 < rc.value.len:
|
||||
return ok(rc.value)
|
||||
|
||||
err(GetNotFound)
|
||||
|
||||
# -------------
|
||||
|
||||
proc putBegFn(db: RdbBackendRef): PutBegFn =
|
||||
result =
|
||||
proc(): PutHdlRef =
|
||||
db.newSession()
|
||||
|
||||
proc putKvpFn(db: RdbBackendRef): PutKvpFn =
|
||||
result =
|
||||
proc(hdl: PutHdlRef; kvps: openArray[(Blob,Blob)]) =
|
||||
let hdl = hdl.getSession db
|
||||
if hdl.error == KvtError(0):
|
||||
for (k,v) in kvps:
|
||||
if k.isValid:
|
||||
hdl.tab[k] = v
|
||||
else:
|
||||
hdl.error = KeyInvalid
|
||||
|
||||
proc putEndFn(db: RdbBackendRef): PutEndFn =
|
||||
result =
|
||||
proc(hdl: PutHdlRef): Result[void,KvtError] =
|
||||
let hdl = hdl.endSession db
|
||||
if hdl.error != KvtError(0):
|
||||
debug logTxt "putEndFn: key/value failed", error=hdl.error
|
||||
return err(hdl.error)
|
||||
let rc = db.rdb.put hdl.tab
|
||||
if rc.isErr:
|
||||
when extraTraceMessages:
|
||||
debug logTxt "putEndFn: failed",
|
||||
error=rc.error[0], info=rc.error[1]
|
||||
return err(rc.error[0])
|
||||
ok()
|
||||
|
||||
|
||||
proc closeFn(db: RdbBackendRef): CloseFn =
|
||||
result =
|
||||
proc(flush: bool) =
|
||||
db.rdb.destroy(flush)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public functions
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc rocksDbBackend*(
|
||||
path: string;
|
||||
): Result[BackendRef,KvtError] =
|
||||
let db = RdbBackendRef(
|
||||
beKind: BackendRocksDB)
|
||||
|
||||
# Initialise RocksDB
|
||||
block:
|
||||
let rc = db.rdb.init(path, maxOpenFiles)
|
||||
if rc.isErr:
|
||||
when extraTraceMessages:
|
||||
trace logTxt "constructor failed",
|
||||
error=rc.error[0], info=rc.error[1]
|
||||
return err(rc.error[0])
|
||||
|
||||
db.getKvpFn = getKvpFn db
|
||||
|
||||
db.putBegFn = putBegFn db
|
||||
db.putKvpFn = putKvpFn db
|
||||
db.putEndFn = putEndFn db
|
||||
|
||||
db.closeFn = closeFn db
|
||||
|
||||
ok db
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public iterators (needs direct backend access)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
iterator walk*(
|
||||
be: RdbBackendRef;
|
||||
): tuple[key: Blob, data: Blob] =
|
||||
## Walk over all key-value pairs of the database.
|
||||
##
|
||||
for (k,v) in be.rdb.walk:
|
||||
yield (k,v)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
|
@ -0,0 +1,37 @@
|
|||
# nimbus-eth1
|
||||
# Copyright (c) 2021 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.
|
||||
|
||||
## Rocks DB internal driver descriptor
|
||||
## ===================================
|
||||
|
||||
{.push raises: [].}
|
||||
|
||||
import
|
||||
rocksdb
|
||||
|
||||
type
|
||||
RdbInst* = object
|
||||
store*: RocksDBInstance ## Rocks DB database handler
|
||||
basePath*: string ## Database directory
|
||||
|
||||
# Low level Rocks DB access for bulk store
|
||||
envOpt*: rocksdb_envoptions_t
|
||||
impOpt*: rocksdb_ingestexternalfileoptions_t
|
||||
|
||||
const
|
||||
BaseFolder* = "nimbus" # Same as for Legacy DB
|
||||
DataFolder* = "kvt" # Legacy DB has "data"
|
||||
BackupFolder* = "khistory" # Legacy DB has "backups"
|
||||
SstCache* = "kbulkput" # Rocks DB bulk load file name in temp folder
|
||||
TempFolder* = "tmp" # Not used with legacy DB (same for Aristo)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
|
@ -0,0 +1,43 @@
|
|||
# nimbus-eth1
|
||||
# Copyright (c) 2021 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.
|
||||
|
||||
## Rocks DB fetch data record
|
||||
## ==========================
|
||||
|
||||
{.push raises: [].}
|
||||
|
||||
import
|
||||
eth/common,
|
||||
rocksdb,
|
||||
results,
|
||||
"../.."/[kvt_constants, kvt_desc],
|
||||
./rdb_desc
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public functions
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc get*(
|
||||
rdb: RdbInst;
|
||||
key: openArray[byte],
|
||||
): Result[Blob,(KvtError,string)] =
|
||||
var res: Blob
|
||||
let onData: DataProc = proc(data: openArray[byte]) =
|
||||
res = @data
|
||||
let rc = rdb.store.get(key, onData)
|
||||
if rc.isErr:
|
||||
return err((RdbBeDriverGetError,rc.error))
|
||||
if not rc.value:
|
||||
res = EmptyBlob
|
||||
ok res
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
|
@ -0,0 +1,107 @@
|
|||
# nimbus-eth1
|
||||
# Copyright (c) 2021 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.
|
||||
|
||||
## Rocksdb constructor/destructor for Kvt DB
|
||||
## =========================================
|
||||
|
||||
{.push raises: [].}
|
||||
|
||||
import
|
||||
std/os,
|
||||
chronicles,
|
||||
rocksdb,
|
||||
results,
|
||||
../../kvt_desc,
|
||||
./rdb_desc
|
||||
|
||||
logScope:
|
||||
topics = "kvt-backend"
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Private helpers
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
template logTxt(info: static[string]): static[string] =
|
||||
"RocksDB/init " & info
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public constructor
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc init*(
|
||||
rdb: var RdbInst;
|
||||
basePath: string;
|
||||
openMax: int;
|
||||
): Result[void,(KvtError,string)] =
|
||||
## Constructor c ode inspired by `RocksStoreRef.init()` from
|
||||
## kvstore_rocksdb.nim
|
||||
let
|
||||
dataDir = basePath / BaseFolder / DataFolder
|
||||
backupsDir = basePath / BaseFolder / BackupFolder
|
||||
tmpDir = basePath / BaseFolder / TempFolder
|
||||
|
||||
try:
|
||||
dataDir.createDir
|
||||
except OSError, IOError:
|
||||
return err((RdbBeCantCreateDataDir, ""))
|
||||
try:
|
||||
backupsDir.createDir
|
||||
except OSError, IOError:
|
||||
return err((RdbBeCantCreateBackupDir, ""))
|
||||
try:
|
||||
tmpDir.createDir
|
||||
except OSError, IOError:
|
||||
return err((RdbBeCantCreateTmpDir, ""))
|
||||
|
||||
let rc = rdb.store.init(
|
||||
dbPath=dataDir, dbBackuppath=backupsDir, readOnly=false,
|
||||
maxOpenFiles=openMax)
|
||||
if rc.isErr:
|
||||
let error = RdbBeDriverInitError
|
||||
debug logTxt "driver failed", dataDir, backupsDir, openMax,
|
||||
error, info=rc.error
|
||||
return err((RdbBeDriverInitError, rc.error))
|
||||
|
||||
# The following is a default setup (subject to change)
|
||||
rdb.impOpt = rocksdb_ingestexternalfileoptions_create()
|
||||
rdb.envOpt = rocksdb_envoptions_create()
|
||||
|
||||
rdb.basePath = basePath
|
||||
ok()
|
||||
|
||||
|
||||
proc destroy*(rdb: var RdbInst; flush: bool) =
|
||||
## Destructor
|
||||
rdb.envOpt.rocksdb_envoptions_destroy()
|
||||
rdb.impOpt.rocksdb_ingestexternalfileoptions_destroy()
|
||||
rdb.store.close()
|
||||
|
||||
let
|
||||
base = rdb.basePath / BaseFolder
|
||||
try:
|
||||
(base / TempFolder).removeDir
|
||||
|
||||
if flush:
|
||||
(base / DataFolder).removeDir
|
||||
|
||||
# Remove the base folder if it is empty
|
||||
block done:
|
||||
for w in base.walkDirRec:
|
||||
# Ignore backup files
|
||||
if 0 < w.len and w[^1] != '~':
|
||||
break done
|
||||
base.removeDir
|
||||
|
||||
except CatchableError:
|
||||
discard
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
|
@ -0,0 +1,198 @@
|
|||
# nimbus-eth1
|
||||
# Copyright (c) 2021 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.
|
||||
|
||||
## Rocks DB store data record
|
||||
## ==========================
|
||||
|
||||
{.push raises: [].}
|
||||
|
||||
import
|
||||
std/[algorithm, os, sequtils, sets, tables],
|
||||
chronicles,
|
||||
eth/common,
|
||||
rocksdb,
|
||||
results,
|
||||
../../kvt_desc,
|
||||
./rdb_desc
|
||||
|
||||
logScope:
|
||||
topics = "kvt-backend"
|
||||
|
||||
type
|
||||
RdbPutSession = object
|
||||
writer: rocksdb_sstfilewriter_t
|
||||
sstPath: string
|
||||
nRecords: int
|
||||
|
||||
const
|
||||
extraTraceMessages = false or true
|
||||
## Enable additional logging noise
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Private helpers
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
template logTxt(info: static[string]): static[string] =
|
||||
"RocksDB/put " & info
|
||||
|
||||
proc getFileSize(fileName: string): int64 =
|
||||
var f: File
|
||||
if f.open fileName:
|
||||
defer: f.close
|
||||
try:
|
||||
result = f.getFileSize
|
||||
except:
|
||||
discard
|
||||
|
||||
proc rmFileIgnExpt(fileName: string) =
|
||||
try:
|
||||
fileName.removeFile
|
||||
except:
|
||||
discard
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Private functions
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc destroy(rps: RdbPutSession) =
|
||||
rps.writer.rocksdb_sstfilewriter_destroy()
|
||||
rps.sstPath.rmFileIgnExpt
|
||||
|
||||
proc begin(
|
||||
rdb: var RdbInst;
|
||||
): Result[RdbPutSession,(KvtError,string)] =
|
||||
## Begin a new bulk load session storing data into a temporary cache file
|
||||
## `fileName`. When finished, this file will bi direcly imported into the
|
||||
## database.
|
||||
var csError: cstring
|
||||
|
||||
var session = RdbPutSession(
|
||||
writer: rocksdb_sstfilewriter_create(rdb.envOpt, rdb.store.options),
|
||||
sstPath: rdb.basePath / BaseFolder / TempFolder / SstCache)
|
||||
|
||||
if session.writer.isNil:
|
||||
return err((RdbBeCreateSstWriter, "Cannot create sst writer session"))
|
||||
|
||||
session.sstPath.rmFileIgnExpt
|
||||
|
||||
session.writer.rocksdb_sstfilewriter_open(
|
||||
session.sstPath.cstring, addr csError)
|
||||
if not csError.isNil:
|
||||
session.destroy()
|
||||
return err((RdbBeOpenSstWriter, $csError))
|
||||
|
||||
ok session
|
||||
|
||||
|
||||
proc add(
|
||||
session: var RdbPutSession;
|
||||
key: openArray[byte];
|
||||
val: openArray[byte];
|
||||
): Result[void,(KvtError,string)] =
|
||||
## Append a record to the SST file. Note that consecutive records must be
|
||||
## strictly increasing.
|
||||
##
|
||||
## This function is a wrapper around `rocksdb_sstfilewriter_add()` or
|
||||
## `rocksdb_sstfilewriter_put()` (stragely enough, there are two functions
|
||||
## with exactly the same impementation code.)
|
||||
var csError: cstring
|
||||
|
||||
session.writer.rocksdb_sstfilewriter_add(
|
||||
cast[cstring](unsafeAddr key[0]), csize_t(key.len),
|
||||
cast[cstring](unsafeAddr val[0]), csize_t(val.len), addr csError)
|
||||
if not csError.isNil:
|
||||
return err((RdbBeAddSstWriter, $csError))
|
||||
|
||||
session.nRecords.inc
|
||||
ok()
|
||||
|
||||
|
||||
proc commit(
|
||||
rdb: var RdbInst;
|
||||
session: RdbPutSession;
|
||||
): Result[void,(KvtError,string)] =
|
||||
## Commit collected and cached data to the database. This function implies
|
||||
## `destroy()` if successful. Otherwise `destroy()` must be called
|
||||
## explicitely, e.g. after error analysis.
|
||||
var csError: cstring
|
||||
|
||||
if 0 < session.nRecords:
|
||||
session.writer.rocksdb_sstfilewriter_finish(addr csError)
|
||||
if not csError.isNil:
|
||||
return err((RdbBeFinishSstWriter, $csError))
|
||||
|
||||
rdb.store.db.rocksdb_ingest_external_file(
|
||||
[session.sstPath].allocCStringArray, 1, rdb.impOpt, addr csError)
|
||||
if not csError.isNil:
|
||||
return err((RdbBeIngestSstWriter, $csError))
|
||||
|
||||
when extraTraceMessages:
|
||||
let fileSize = session.sstPath.getFileSize
|
||||
trace logTxt "finished sst", fileSize
|
||||
|
||||
session.destroy()
|
||||
ok()
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public functions
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc put*(
|
||||
rdb: var RdbInst;
|
||||
tab: Table[Blob,Blob];
|
||||
): Result[void,(KvtError,string)] =
|
||||
|
||||
var session = block:
|
||||
let rc = rdb.begin()
|
||||
if rc.isErr:
|
||||
return err(rc.error)
|
||||
rc.value
|
||||
|
||||
# Vertices with empty table values will be deleted
|
||||
var delKey: HashSet[Blob]
|
||||
|
||||
# Compare `Blob`s as left aligned big endian numbers, right padded with zeros
|
||||
proc cmpBlobs(a, b: Blob): int =
|
||||
let minLen = min(a.len, b.len)
|
||||
for n in 0 ..< minLen:
|
||||
if a[n] != b[n]:
|
||||
return a[n].cmp b[n]
|
||||
if a.len < b.len:
|
||||
return -1
|
||||
if b.len < a.len:
|
||||
return 1
|
||||
|
||||
for key in tab.keys.toSeq.sorted cmpBlobs:
|
||||
let val = tab.getOrVoid key
|
||||
if val.isValid:
|
||||
let rc = session.add(key, val)
|
||||
if rc.isErr:
|
||||
session.destroy()
|
||||
return err(rc.error)
|
||||
else:
|
||||
delKey.incl key
|
||||
|
||||
block:
|
||||
let rc = rdb.commit session
|
||||
if rc.isErr:
|
||||
trace logTxt "commit error", error=rc.error[0], info=rc.error[1]
|
||||
return err(rc.error)
|
||||
|
||||
# Delete vertices after successfully updating vertices with non-zero values.
|
||||
for key in delKey:
|
||||
let rc = rdb.store.del key
|
||||
if rc.isErr:
|
||||
return err((RdbBeDriverDelError,rc.error))
|
||||
|
||||
ok()
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
|
@ -0,0 +1,54 @@
|
|||
# nimbus-eth1
|
||||
# Copyright (c) 2021 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.
|
||||
|
||||
## Rocks DB store data iterator
|
||||
## ============================
|
||||
##
|
||||
{.push raises: [].}
|
||||
|
||||
import
|
||||
std/sequtils,
|
||||
eth/common,
|
||||
rocksdb,
|
||||
./rdb_desc
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public iterators
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
iterator walk*(rdb: RdbInst): tuple[key: Blob, data: Blob] =
|
||||
## Walk over all key-value pairs of the database.
|
||||
##
|
||||
let rit = rdb.store.db.rocksdb_create_iterator(rdb.store.readOptions)
|
||||
defer: rit.rocksdb_iter_destroy()
|
||||
|
||||
rit.rocksdb_iter_seek_to_first()
|
||||
while rit.rocksdb_iter_valid() != 0:
|
||||
var kLen: csize_t
|
||||
let kData = rit.rocksdb_iter_key(addr kLen)
|
||||
|
||||
if not kData.isNil and 0 < kLen:
|
||||
var vLen: csize_t
|
||||
let vData = rit.rocksdb_iter_value(addr vLen)
|
||||
|
||||
if not vData.isNil and 0 < vLen:
|
||||
let
|
||||
key = kData.toOpenArrayByte(0,int(kLen)-1).toSeq
|
||||
data = vData.toOpenArrayByte(0,int(vLen)-1).toSeq
|
||||
yield (key,data)
|
||||
|
||||
# Update Iterator (might overwrite kData/vdata)
|
||||
rit.rocksdb_iter_next()
|
||||
|
||||
# End while
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
|
@ -0,0 +1,18 @@
|
|||
# nimbus-eth1
|
||||
# Copyright (c) 2021 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
|
||||
kvt_init/persistent as init_persistent,
|
||||
kvt_walk/persistent as walk_persistent
|
||||
export
|
||||
init_persistent,
|
||||
walk_persistent
|
||||
|
||||
# End
|
|
@ -0,0 +1,196 @@
|
|||
# nimbus-eth1
|
||||
# Copyright (c) 2021 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 interface
|
||||
## ===============================
|
||||
##
|
||||
{.push raises: [].}
|
||||
|
||||
import
|
||||
std/[sequtils, tables],
|
||||
results,
|
||||
./kvt_desc/desc_backend,
|
||||
./kvt_desc
|
||||
|
||||
func isTop*(tx: KvtTxRef): bool
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Private helpers
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
func getDbDescFromTopTx(tx: KvtTxRef): Result[KvtDbRef,KvtError] =
|
||||
if not tx.isTop():
|
||||
return err(TxNotTopTx)
|
||||
let db = tx.db
|
||||
if tx.level != db.stack.len:
|
||||
return err(TxStackUnderflow)
|
||||
ok db
|
||||
|
||||
proc getTxUid(db: KvtDbRef): uint =
|
||||
if db.txUidGen == high(uint):
|
||||
db.txUidGen = 0
|
||||
db.txUidGen.inc
|
||||
db.txUidGen
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public functions, getters
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
func txTop*(db: KvtDbRef): Result[KvtTxRef,KvtError] =
|
||||
## Getter, returns top level transaction if there is any.
|
||||
if db.txRef.isNil:
|
||||
err(TxNoPendingTx)
|
||||
else:
|
||||
ok(db.txRef)
|
||||
|
||||
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 level*(tx: KvtTxRef): int =
|
||||
## Getter, positive nesting level of transaction argument `tx`
|
||||
tx.level
|
||||
|
||||
func level*(db: KvtDbRef): int =
|
||||
## Getter, non-negative nesting level (i.e. number of pending transactions)
|
||||
if not db.txRef.isNil:
|
||||
result = db.txRef.level
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public functions
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
func to*(tx: KvtTxRef; T: type[KvtDbRef]): T =
|
||||
## Getter, retrieves the parent database descriptor from argument `tx`
|
||||
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.begin
|
||||
## defer: tx.rollback()
|
||||
## ... continue using db ...
|
||||
## tx.commit()
|
||||
##
|
||||
if db.level != db.stack.len:
|
||||
return err(TxStackGarbled)
|
||||
|
||||
db.stack.add db.top.dup # push (save and use top later)
|
||||
db.top.txUid = db.getTxUid()
|
||||
|
||||
db.txRef = KvtTxRef(
|
||||
db: db,
|
||||
txUid: db.top.txUid,
|
||||
parent: db.txRef,
|
||||
level: db.stack.len)
|
||||
|
||||
ok db.txRef
|
||||
|
||||
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.
|
||||
##
|
||||
let db = ? tx.getDbDescFromTopTx()
|
||||
|
||||
# Roll back to previous layer.
|
||||
db.top = db.stack[^1]
|
||||
db.stack.setLen(db.stack.len-1)
|
||||
|
||||
db.txRef = tx.parent
|
||||
ok()
|
||||
|
||||
|
||||
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.
|
||||
##
|
||||
let db = ? tx.getDbDescFromTopTx()
|
||||
|
||||
# Keep top and discard layer below
|
||||
db.top.txUid = db.stack[^1].txUid
|
||||
db.stack.setLen(db.stack.len-1)
|
||||
|
||||
db.txRef = tx.parent
|
||||
ok()
|
||||
|
||||
|
||||
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
|
||||
##
|
||||
let db = ? tx.getDbDescFromTopTx()
|
||||
|
||||
# If commit, then leave the current layer and clear the stack, oterwise
|
||||
# install the stack bottom.
|
||||
if not commit:
|
||||
db.stack[0].swap db.top
|
||||
|
||||
db.top.txUid = 0
|
||||
db.stack.setLen(0)
|
||||
ok()
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public functions: save database
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc stow*(
|
||||
db: KvtDbRef; # Database
|
||||
): Result[void,KvtError] =
|
||||
## The function saves the data from the top layer cache into the
|
||||
## backend database.
|
||||
##
|
||||
## If there is no backend the function returns immediately with an error.
|
||||
## The same happens if there is a pending transaction.
|
||||
##
|
||||
if not db.txRef.isNil:
|
||||
return err(TxPendingTx)
|
||||
if 0 < db.stack.len:
|
||||
return err(TxStackGarbled)
|
||||
|
||||
let be = db.backend
|
||||
if be.isNil:
|
||||
return err(TxBackendNotWritable)
|
||||
|
||||
# Save structural and other table entries
|
||||
let txFrame = be.putBegFn()
|
||||
be.putKvpFn(txFrame, db.top.tab.pairs.toSeq)
|
||||
? be.putEndFn txFrame
|
||||
|
||||
# Delete or clear stack and clear top
|
||||
db.stack.setLen(0)
|
||||
db.top = LayerRef(txUid: db.top.txUid)
|
||||
|
||||
ok()
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
|
@ -0,0 +1,95 @@
|
|||
# nimbus-eth1
|
||||
# Copyright (c) 2021 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 -- Common functions
|
||||
## ==========================
|
||||
##
|
||||
{.push raises: [].}
|
||||
|
||||
import
|
||||
eth/common,
|
||||
results,
|
||||
./kvt_desc/desc_backend,
|
||||
./kvt_desc
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Private helpers
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc getBE*(
|
||||
db: KvtDbRef; # Database
|
||||
key: Blob; # Key of database record
|
||||
): Result[Blob,KvtError] =
|
||||
## For the argument `key` return the associated value from the backend
|
||||
## database if available.
|
||||
##
|
||||
let be = db.backend
|
||||
if not be.isNil:
|
||||
return be.getKvpFn key
|
||||
err(GetNotFound)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public functions, converters
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc put*(
|
||||
db: KvtDbRef; # Database
|
||||
key: Blob; # Key of database record to store
|
||||
data: Blob; # Value of database record to store
|
||||
): Result[void,KvtError] =
|
||||
## For the argument `key` associated the argument `data` as value (which
|
||||
## will be marked in the top layer cache.)
|
||||
if key.len == 0:
|
||||
return err(KeyInvalid)
|
||||
if data.len == 0:
|
||||
return err(DataInvalid)
|
||||
|
||||
db.top.tab[key] = data
|
||||
ok()
|
||||
|
||||
|
||||
proc del*(
|
||||
db: KvtDbRef; # Database
|
||||
key: Blob; # Key of database record to delete
|
||||
): Result[void,KvtError] =
|
||||
## For the argument `key` delete the associated value (which will be marked
|
||||
## in the top layer cache.)
|
||||
if key.len == 0:
|
||||
return err(KeyInvalid)
|
||||
|
||||
let rc = db.getBE(key)
|
||||
if rc.isOk:
|
||||
db.top.tab[key] = EmptyBlob
|
||||
elif rc.error == GetNotFound:
|
||||
db.top.tab.del key
|
||||
else:
|
||||
return err(rc.error)
|
||||
|
||||
ok()
|
||||
|
||||
# ------------
|
||||
|
||||
proc get*(
|
||||
db: KvtDbRef; # Database
|
||||
key: Blob; # Key of database record
|
||||
): Result[Blob,KvtError] =
|
||||
## For the argument `key` return the associated value preferably from the
|
||||
## top layer, or the database otherwise.
|
||||
##
|
||||
if key.len == 0:
|
||||
return err(KeyInvalid)
|
||||
let data = db.top.tab.getOrVoid key
|
||||
if data.isValid:
|
||||
return ok(data)
|
||||
db.getBE key
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
|
@ -0,0 +1,27 @@
|
|||
# Nimbus - Types, data structures and shared utilities used in network sync
|
||||
#
|
||||
# Copyright (c) 2018-2021 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.
|
||||
|
||||
## Backend DB traversal for Kvt DB
|
||||
## ===============================
|
||||
##
|
||||
## This module provides iterators for the memory based backend or the
|
||||
## backend-less database. Do import `kvt_walk/persistent` for the
|
||||
## persistent backend though avoiding to unnecessarily link to the persistent
|
||||
## backend library (e.g. `rocksdb`) when a memory only database is used.
|
||||
##
|
||||
{.push raises: [].}
|
||||
|
||||
import
|
||||
./kvt_walk/memory_only
|
||||
export
|
||||
memory_only
|
||||
|
||||
# End
|
|
@ -0,0 +1,39 @@
|
|||
# Nimbus - Types, data structures and shared utilities used in network sync
|
||||
#
|
||||
# Copyright (c) 2018-2021 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.
|
||||
|
||||
## Iterators for non-persistent backend of the Kvt DB
|
||||
## ==================================================
|
||||
##
|
||||
import
|
||||
eth/common,
|
||||
../kvt_init/[memory_db, memory_only],
|
||||
../kvt_init
|
||||
export
|
||||
memory_db,
|
||||
memory_only
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public iterators (all in one)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
iterator walkAny*(
|
||||
be: MemBackendRef|VoidBackendRef;
|
||||
): tuple[key: Blob, data: Blob] =
|
||||
## Iterate over backend filters.
|
||||
when be isnot VoidBackendRef:
|
||||
mixin walk
|
||||
|
||||
for (k,v) in be.walk:
|
||||
yield (k,v)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
|
@ -0,0 +1,42 @@
|
|||
# Nimbus - Types, data structures and shared utilities used in network sync
|
||||
#
|
||||
# Copyright (c) 2018-2021 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.
|
||||
|
||||
## Iterators for persistent backend of the Kvt DB
|
||||
## ==============================================
|
||||
##
|
||||
## This module automatically pulls in the persistent backend library at the
|
||||
## linking stage (e.g. `rocksdb`) which can be avoided for pure memory DB
|
||||
## applications by importing `./kvt_walk/memory_only` (rather than
|
||||
## `./kvt_walk/persistent`.)
|
||||
##
|
||||
import
|
||||
eth/common,
|
||||
../kvt_init/[rocks_db, persistent],
|
||||
./memory_only
|
||||
export
|
||||
rocks_db,
|
||||
memory_only,
|
||||
persistent
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public iterators (all in one)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
iterator walkAny*(
|
||||
be: RdbBackendRef;
|
||||
): tuple[key: Blob, data: Blob] =
|
||||
## Walk filter slots in fifo order.
|
||||
for (k,v) in be.walk:
|
||||
yield (k,v)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
Loading…
Reference in New Issue