Added support for namespaces to RocksDb kvstore. (#2066)
* Add new RocksNamespaceRef type and remove backups and readonly support from RocksDb KvStore. * Bump nim-rocksdb to fc2ba4a836b6b47ae1b17d1c45801c7e06585e19 * Fix tests. * Fix copyright notice.
This commit is contained in:
parent
1159b0114e
commit
799acf301d
|
@ -236,7 +236,6 @@ type
|
||||||
|
|
||||||
# RocksDB backend
|
# RocksDB backend
|
||||||
RdbBeCantCreateDataDir
|
RdbBeCantCreateDataDir
|
||||||
RdbBeCantCreateBackupDir
|
|
||||||
RdbBeCantCreateTmpDir
|
RdbBeCantCreateTmpDir
|
||||||
RdbBeDriverInitError
|
RdbBeDriverInitError
|
||||||
RdbBeDriverGetError
|
RdbBeDriverGetError
|
||||||
|
@ -268,7 +267,7 @@ type
|
||||||
AccVtxUnsupported
|
AccVtxUnsupported
|
||||||
AccNodeUnsupported
|
AccNodeUnsupported
|
||||||
PayloadTypeUnsupported
|
PayloadTypeUnsupported
|
||||||
|
|
||||||
# Miscelaneous handy helpers
|
# Miscelaneous handy helpers
|
||||||
AccRootUnacceptable
|
AccRootUnacceptable
|
||||||
MptContextMissing
|
MptContextMissing
|
||||||
|
|
|
@ -41,7 +41,6 @@ type
|
||||||
const
|
const
|
||||||
BaseFolder* = "nimbus" # Same as for Legacy DB
|
BaseFolder* = "nimbus" # Same as for Legacy DB
|
||||||
DataFolder* = "aristo" # Legacy DB has "data"
|
DataFolder* = "aristo" # Legacy DB has "data"
|
||||||
BackupFolder* = "aristo-history" # Legacy DB has "backups"
|
|
||||||
SstCache* = "bulkput" # Rocks DB bulk load file name in temp folder
|
SstCache* = "bulkput" # Rocks DB bulk load file name in temp folder
|
||||||
TempFolder* = "tmp" # No `tmp` directory used with legacy DB
|
TempFolder* = "tmp" # No `tmp` directory used with legacy DB
|
||||||
|
|
||||||
|
@ -55,9 +54,6 @@ func baseDir*(rdb: RdbInst): string =
|
||||||
func dataDir*(rdb: RdbInst): string =
|
func dataDir*(rdb: RdbInst): string =
|
||||||
rdb.baseDir / DataFolder
|
rdb.baseDir / DataFolder
|
||||||
|
|
||||||
func backupsDir*(rdb: RdbInst): string =
|
|
||||||
rdb.basePath / BaseFolder / BackupFolder
|
|
||||||
|
|
||||||
func cacheDir*(rdb: RdbInst): string =
|
func cacheDir*(rdb: RdbInst): string =
|
||||||
rdb.dataDir / TempFolder
|
rdb.dataDir / TempFolder
|
||||||
|
|
||||||
|
|
|
@ -47,16 +47,11 @@ proc init*(
|
||||||
|
|
||||||
let
|
let
|
||||||
dataDir = rdb.dataDir
|
dataDir = rdb.dataDir
|
||||||
backupsDir = rdb.backupsDir
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
dataDir.createDir
|
dataDir.createDir
|
||||||
except OSError, IOError:
|
except OSError, IOError:
|
||||||
return err((RdbBeCantCreateDataDir, ""))
|
return err((RdbBeCantCreateDataDir, ""))
|
||||||
try:
|
|
||||||
backupsDir.createDir
|
|
||||||
except OSError, IOError:
|
|
||||||
return err((RdbBeCantCreateBackupDir, ""))
|
|
||||||
try:
|
try:
|
||||||
rdb.cacheDir.createDir
|
rdb.cacheDir.createDir
|
||||||
except OSError, IOError:
|
except OSError, IOError:
|
||||||
|
@ -68,7 +63,7 @@ proc init*(
|
||||||
let rc = openRocksDb(dataDir, dbOpts)
|
let rc = openRocksDb(dataDir, dbOpts)
|
||||||
if rc.isErr:
|
if rc.isErr:
|
||||||
let error = RdbBeDriverInitError
|
let error = RdbBeDriverInitError
|
||||||
debug logTxt "driver failed", dataDir, backupsDir, openMax,
|
debug logTxt "driver failed", dataDir, openMax,
|
||||||
error, info=rc.error
|
error, info=rc.error
|
||||||
return err((RdbBeDriverInitError, rc.error))
|
return err((RdbBeDriverInitError, rc.error))
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
|
||||||
import
|
import
|
||||||
std/os,
|
std/[os, sequtils],
|
||||||
stew/results,
|
stew/results,
|
||||||
rocksdb,
|
rocksdb,
|
||||||
eth/db/kvstore
|
eth/db/kvstore
|
||||||
|
@ -22,46 +22,43 @@ const maxOpenFiles = 512
|
||||||
|
|
||||||
type
|
type
|
||||||
RocksStoreRef* = ref object of RootObj
|
RocksStoreRef* = ref object of RootObj
|
||||||
db: RocksDbRef
|
db: RocksDbReadWriteRef
|
||||||
backupEngine: BackupEngineRef
|
|
||||||
readOnly: bool
|
|
||||||
|
|
||||||
proc readOnly*(store: RocksStoreRef): bool =
|
RocksNamespaceRef* = ref object of RootObj
|
||||||
store.readOnly
|
colFamily: ColFamilyReadWrite
|
||||||
|
|
||||||
proc readOnlyDb*(store: RocksStoreRef): RocksDbReadOnlyRef =
|
# ------------------------------------------------------------------------------
|
||||||
doAssert store.readOnly
|
# RocksStoreRef procs
|
||||||
store.db.RocksDbReadOnlyRef
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
proc readWriteDb*(store: RocksStoreRef): RocksDbReadWriteRef =
|
proc rocksDb*(store: RocksStoreRef): RocksDbReadWriteRef =
|
||||||
doAssert not store.readOnly
|
store.db
|
||||||
store.db.RocksDbReadWriteRef
|
|
||||||
|
|
||||||
template validateCanWriteAndGet(store: RocksStoreRef): RocksDbReadWriteRef =
|
proc get*(
|
||||||
if store.readOnly:
|
store: RocksStoreRef,
|
||||||
raiseAssert "Unimplemented"
|
key: openArray[byte],
|
||||||
store.db.RocksDbReadWriteRef
|
onData: kvstore.DataProc): KvResult[bool] =
|
||||||
|
|
||||||
proc get*(store: RocksStoreRef, key: openArray[byte], onData: kvstore.DataProc): KvResult[bool] =
|
|
||||||
store.db.get(key, onData)
|
store.db.get(key, onData)
|
||||||
|
|
||||||
proc find*(store: RocksStoreRef, prefix: openArray[byte], onFind: kvstore.KeyValueProc): KvResult[int] =
|
proc find*(
|
||||||
|
store: RocksStoreRef,
|
||||||
|
prefix: openArray[byte],
|
||||||
|
onFind: kvstore.KeyValueProc): KvResult[int] =
|
||||||
raiseAssert "Unimplemented"
|
raiseAssert "Unimplemented"
|
||||||
|
|
||||||
proc put*(store: RocksStoreRef, key, value: openArray[byte]): KvResult[void] =
|
proc put*(store: RocksStoreRef, key, value: openArray[byte]): KvResult[void] =
|
||||||
store.validateCanWriteAndGet().put(key, value)
|
store.db.put(key, value)
|
||||||
|
|
||||||
proc contains*(store: RocksStoreRef, key: openArray[byte]): KvResult[bool] =
|
proc contains*(store: RocksStoreRef, key: openArray[byte]): KvResult[bool] =
|
||||||
store.db.keyExists(key)
|
store.db.keyExists(key)
|
||||||
|
|
||||||
proc del*(store: RocksStoreRef, key: openArray[byte]): KvResult[bool] =
|
proc del*(store: RocksStoreRef, key: openArray[byte]): KvResult[bool] =
|
||||||
let db = store.validateCanWriteAndGet()
|
|
||||||
|
|
||||||
let exists = ? db.keyExists(key)
|
let exists = ? store.db.keyExists(key)
|
||||||
if not exists:
|
if not exists:
|
||||||
return ok(false)
|
return ok(false)
|
||||||
|
|
||||||
let res = db.delete(key)
|
let res = store.db.delete(key)
|
||||||
if res.isErr():
|
if res.isErr():
|
||||||
return err(res.error())
|
return err(res.error())
|
||||||
|
|
||||||
|
@ -72,32 +69,74 @@ proc clear*(store: RocksStoreRef): KvResult[bool] =
|
||||||
|
|
||||||
proc close*(store: RocksStoreRef) =
|
proc close*(store: RocksStoreRef) =
|
||||||
store.db.close()
|
store.db.close()
|
||||||
store.backupEngine.close()
|
|
||||||
|
|
||||||
proc init*(
|
proc init*(
|
||||||
T: type RocksStoreRef,
|
T: type RocksStoreRef,
|
||||||
basePath: string,
|
basePath: string,
|
||||||
name: string,
|
name: string,
|
||||||
readOnly = false): KvResult[T] =
|
namespaces = @["default"]): KvResult[T] =
|
||||||
|
|
||||||
let
|
let dataDir = basePath / name / "data"
|
||||||
dataDir = basePath / name / "data"
|
|
||||||
backupsDir = basePath / name / "backups"
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
createDir(dataDir)
|
createDir(dataDir)
|
||||||
createDir(backupsDir)
|
|
||||||
except OSError, IOError:
|
except OSError, IOError:
|
||||||
return err("rocksdb: cannot create database directory")
|
return err("RocksStoreRef: cannot create database directory")
|
||||||
|
|
||||||
let backupEngine = ? openBackupEngine(backupsDir)
|
|
||||||
|
|
||||||
let dbOpts = defaultDbOptions()
|
let dbOpts = defaultDbOptions()
|
||||||
dbOpts.setMaxOpenFiles(maxOpenFiles)
|
dbOpts.setMaxOpenFiles(maxOpenFiles)
|
||||||
|
|
||||||
if readOnly:
|
let db = ? openRocksDb(dataDir, dbOpts,
|
||||||
let readOnlyDb = ? openRocksDbReadOnly(dataDir, dbOpts)
|
columnFamilies = namespaces.mapIt(initColFamilyDescriptor(it)))
|
||||||
ok(T(db: readOnlyDb, backupEngine: backupEngine, readOnly: true))
|
ok(T(db: db))
|
||||||
else:
|
|
||||||
let readWriteDb = ? openRocksDb(dataDir, dbOpts)
|
# ------------------------------------------------------------------------------
|
||||||
ok(T(db: readWriteDb, backupEngine: backupEngine, readOnly: false))
|
# RocksNamespaceRef procs
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
proc name*(store: RocksNamespaceRef): string =
|
||||||
|
store.colFamily.name
|
||||||
|
|
||||||
|
proc get*(
|
||||||
|
ns: RocksNamespaceRef,
|
||||||
|
key: openArray[byte],
|
||||||
|
onData: kvstore.DataProc): KvResult[bool] =
|
||||||
|
ns.colFamily.get(key, onData)
|
||||||
|
|
||||||
|
proc find*(
|
||||||
|
ns: RocksNamespaceRef,
|
||||||
|
prefix: openArray[byte],
|
||||||
|
onFind: kvstore.KeyValueProc): KvResult[int] =
|
||||||
|
raiseAssert "Unimplemented"
|
||||||
|
|
||||||
|
proc put*(ns: RocksNamespaceRef, key, value: openArray[byte]): KvResult[void] =
|
||||||
|
ns.colFamily.put(key, value)
|
||||||
|
|
||||||
|
proc contains*(ns: RocksNamespaceRef, key: openArray[byte]): KvResult[bool] =
|
||||||
|
ns.colFamily.keyExists(key)
|
||||||
|
|
||||||
|
proc del*(ns: RocksNamespaceRef, key: openArray[byte]): KvResult[bool] =
|
||||||
|
let exists = ? ns.colFamily.keyExists(key)
|
||||||
|
if not exists:
|
||||||
|
return ok(false)
|
||||||
|
|
||||||
|
let res = ns.colFamily.delete(key)
|
||||||
|
if res.isErr():
|
||||||
|
return err(res.error())
|
||||||
|
|
||||||
|
ok(true)
|
||||||
|
|
||||||
|
proc clear*(ns: RocksNamespaceRef): KvResult[bool] =
|
||||||
|
raiseAssert "Unimplemented"
|
||||||
|
|
||||||
|
proc close*(ns: RocksNamespaceRef) =
|
||||||
|
# To close the database, call close on RocksStoreRef.
|
||||||
|
raiseAssert "Unimplemented"
|
||||||
|
|
||||||
|
proc openNamespace*(
|
||||||
|
store: RocksStoreRef,
|
||||||
|
name: string): KvResult[RocksNamespaceRef] =
|
||||||
|
doAssert not store.db.isClosed()
|
||||||
|
|
||||||
|
let cf = ? store.db.withColFamily(name)
|
||||||
|
ok(RocksNamespaceRef(colFamily: cf))
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# nimbus-eth1
|
# nimbus-eth1
|
||||||
# Copyright (c) 2023 Status Research & Development GmbH
|
# Copyright (c) 2023-2024 Status Research & Development GmbH
|
||||||
# Licensed under either of
|
# Licensed under either of
|
||||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0)
|
# http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
@ -19,7 +19,6 @@ type
|
||||||
|
|
||||||
# RocksDB backend
|
# RocksDB backend
|
||||||
RdbBeCantCreateDataDir
|
RdbBeCantCreateDataDir
|
||||||
RdbBeCantCreateBackupDir
|
|
||||||
RdbBeCantCreateTmpDir
|
RdbBeCantCreateTmpDir
|
||||||
RdbBeDriverInitError
|
RdbBeDriverInitError
|
||||||
RdbBeDriverGetError
|
RdbBeDriverGetError
|
||||||
|
|
|
@ -31,7 +31,6 @@ type
|
||||||
const
|
const
|
||||||
BaseFolder* = "nimbus" # Same as for Legacy DB
|
BaseFolder* = "nimbus" # Same as for Legacy DB
|
||||||
DataFolder* = "kvt" # Legacy DB has "data"
|
DataFolder* = "kvt" # Legacy DB has "data"
|
||||||
BackupFolder* = "kvt-history" # Legacy DB has "backups"
|
|
||||||
SstCache* = "bulkput" # Rocks DB bulk load file name in temp folder
|
SstCache* = "bulkput" # Rocks DB bulk load file name in temp folder
|
||||||
TempFolder* = "tmp" # No `tmp` directory used with legacy DB
|
TempFolder* = "tmp" # No `tmp` directory used with legacy DB
|
||||||
|
|
||||||
|
@ -45,9 +44,6 @@ func baseDir*(rdb: RdbInst): string =
|
||||||
func dataDir*(rdb: RdbInst): string =
|
func dataDir*(rdb: RdbInst): string =
|
||||||
rdb.baseDir / DataFolder
|
rdb.baseDir / DataFolder
|
||||||
|
|
||||||
func backupsDir*(rdb: RdbInst): string =
|
|
||||||
rdb.basePath / BaseFolder / BackupFolder
|
|
||||||
|
|
||||||
func cacheDir*(rdb: RdbInst): string =
|
func cacheDir*(rdb: RdbInst): string =
|
||||||
rdb.dataDir / TempFolder
|
rdb.dataDir / TempFolder
|
||||||
|
|
||||||
|
|
|
@ -47,16 +47,11 @@ proc init*(
|
||||||
|
|
||||||
let
|
let
|
||||||
dataDir = rdb.dataDir
|
dataDir = rdb.dataDir
|
||||||
backupsDir = rdb.backupsDir
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
dataDir.createDir
|
dataDir.createDir
|
||||||
except OSError, IOError:
|
except OSError, IOError:
|
||||||
return err((RdbBeCantCreateDataDir, ""))
|
return err((RdbBeCantCreateDataDir, ""))
|
||||||
try:
|
|
||||||
backupsDir.createDir
|
|
||||||
except OSError, IOError:
|
|
||||||
return err((RdbBeCantCreateBackupDir, ""))
|
|
||||||
try:
|
try:
|
||||||
rdb.cacheDir.createDir
|
rdb.cacheDir.createDir
|
||||||
except OSError, IOError:
|
except OSError, IOError:
|
||||||
|
@ -68,7 +63,7 @@ proc init*(
|
||||||
let rc = openRocksDb(dataDir, dbOpts)
|
let rc = openRocksDb(dataDir, dbOpts)
|
||||||
if rc.isErr:
|
if rc.isErr:
|
||||||
let error = RdbBeDriverInitError
|
let error = RdbBeDriverInitError
|
||||||
debug logTxt "driver failed", dataDir, backupsDir, openMax,
|
debug logTxt "driver failed", dataDir, openMax,
|
||||||
error, info=rc.error
|
error, info=rc.error
|
||||||
return err((RdbBeDriverInitError, rc.error))
|
return err((RdbBeDriverInitError, rc.error))
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ proc lastError*(rbl: RockyBulkLoadRef): string =
|
||||||
|
|
||||||
proc store*(rbl: RockyBulkLoadRef): RocksDbReadWriteRef =
|
proc store*(rbl: RockyBulkLoadRef): RocksDbReadWriteRef =
|
||||||
## Provide the diecriptor for backend functions as defined in `rocksdb`.
|
## Provide the diecriptor for backend functions as defined in `rocksdb`.
|
||||||
rbl.db.readWriteDb()
|
rbl.db.rocksDb()
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Public functions
|
# Public functions
|
||||||
|
@ -154,7 +154,7 @@ proc finish*(
|
||||||
|
|
||||||
var filePath = rbl.filePath.cstring
|
var filePath = rbl.filePath.cstring
|
||||||
if csError.isNil:
|
if csError.isNil:
|
||||||
rbl.db.readWriteDb().cPtr.rocksdb_ingest_external_file(
|
rbl.db.rocksDb.cPtr.rocksdb_ingest_external_file(
|
||||||
cast[cstringArray](filePath.addr), 1,
|
cast[cstringArray](filePath.addr), 1,
|
||||||
rbl.importOption,
|
rbl.importOption,
|
||||||
cast[cstringArray](csError.addr))
|
cast[cstringArray](csError.addr))
|
||||||
|
|
|
@ -18,8 +18,12 @@ import
|
||||||
../../nimbus/db/kvstore_rocksdb,
|
../../nimbus/db/kvstore_rocksdb,
|
||||||
eth/../tests/db/test_kvstore
|
eth/../tests/db/test_kvstore
|
||||||
|
|
||||||
suite "RocksStoreRef":
|
suite "KvStore RocksDb Tests":
|
||||||
test "KvStore interface":
|
const
|
||||||
|
NS_DEFAULT = "default"
|
||||||
|
NS_OTHER = "other"
|
||||||
|
|
||||||
|
test "RocksStoreRef KvStore interface":
|
||||||
let tmp = getTempDir() / "nimbus-test-db"
|
let tmp = getTempDir() / "nimbus-test-db"
|
||||||
removeDir(tmp)
|
removeDir(tmp)
|
||||||
|
|
||||||
|
@ -28,3 +32,29 @@ suite "RocksStoreRef":
|
||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
testKvStore(kvStore db, false, false)
|
testKvStore(kvStore db, false, false)
|
||||||
|
|
||||||
|
test "RocksNamespaceRef KvStore interface - default namespace":
|
||||||
|
let tmp = getTempDir() / "nimbus-test-db"
|
||||||
|
removeDir(tmp)
|
||||||
|
|
||||||
|
let db = RocksStoreRef.init(tmp, "test")[]
|
||||||
|
defer:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
let defaultNs = db.openNamespace(NS_DEFAULT)[]
|
||||||
|
testKvStore(kvStore defaultNs, false, false)
|
||||||
|
|
||||||
|
test "RocksNamespaceRef KvStore interface - multiple namespace":
|
||||||
|
let tmp = getTempDir() / "nimbus-test-db"
|
||||||
|
removeDir(tmp)
|
||||||
|
|
||||||
|
let db = RocksStoreRef.init(tmp, "test",
|
||||||
|
namespaces = @[NS_DEFAULT, NS_OTHER])[]
|
||||||
|
defer:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
let defaultNs = db.openNamespace(NS_DEFAULT)[]
|
||||||
|
testKvStore(kvStore defaultNs, false, false)
|
||||||
|
|
||||||
|
let otherNs = db.openNamespace(NS_OTHER)[]
|
||||||
|
testKvStore(kvStore otherNs, false, false)
|
||||||
|
|
|
@ -57,7 +57,7 @@ proc walkAllDb(
|
||||||
## Walk over all key-value pairs of the database (`RocksDB` only.)
|
## Walk over all key-value pairs of the database (`RocksDB` only.)
|
||||||
let
|
let
|
||||||
rop = rocksdb_readoptions_create()
|
rop = rocksdb_readoptions_create()
|
||||||
rit = rocky.readWriteDb.cPtr.rocksdb_create_iterator(rop)
|
rit = rocky.rocksdb.cPtr.rocksdb_create_iterator(rop)
|
||||||
|
|
||||||
rit.rocksdb_iter_seek_to_first()
|
rit.rocksdb_iter_seek_to_first()
|
||||||
while rit.rocksdb_iter_valid() != 0:
|
while rit.rocksdb_iter_valid() != 0:
|
||||||
|
|
|
@ -137,7 +137,7 @@ proc test_dbTimingRockySetup*(
|
||||||
let
|
let
|
||||||
rdb = cdb.backend.toRocksStoreRef
|
rdb = cdb.backend.toRocksStoreRef
|
||||||
rop = rocksdb_readoptions_create()
|
rop = rocksdb_readoptions_create()
|
||||||
rit = rdb.readWriteDb().cPtr.rocksdb_create_iterator(rop)
|
rit = rdb.rocksDb.cPtr.rocksdb_create_iterator(rop)
|
||||||
check not rit.isNil
|
check not rit.isNil
|
||||||
|
|
||||||
var
|
var
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 5f6282e8d43ae27c5b46542c271a5776d26daaf2
|
Subproject commit fc2ba4a836b6b47ae1b17d1c45801c7e06585e19
|
Loading…
Reference in New Issue