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:
web3-developer 2024-03-12 11:04:46 +08:00 committed by GitHub
parent 1159b0114e
commit 799acf301d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 118 additions and 69 deletions

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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))

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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))

View File

@ -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)

View File

@ -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:

View File

@ -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

2
vendor/nim-rocksdb vendored

@ -1 +1 @@
Subproject commit 5f6282e8d43ae27c5b46542c271a5776d26daaf2 Subproject commit fc2ba4a836b6b47ae1b17d1c45801c7e06585e19