mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-16 20:39:10 +00:00
Modularise slasher backend (#3443)
## Proposed Changes
Enable multiple database backends for the slasher, either MDBX (default) or LMDB. The backend can be selected using `--slasher-backend={lmdb,mdbx}`.
## Additional Info
In order to abstract over the two library's different handling of database lifetimes I've used `Box::leak` to give the `Environment` type a `'static` lifetime. This was the only way I could think of using 100% safe code to construct a self-referential struct `SlasherDB`, where the `OpenDatabases` refers to the `Environment`. I think this is OK, as the `Environment` is expected to live for the life of the program, and both database engines leave the database in a consistent state after each write. The memory claimed for memory-mapping will be freed by the OS and appropriately flushed regardless of whether the `Environment` is actually dropped.
We are depending on two `sigp` forks of `libmdbx-rs` and `lmdb-rs`, to give us greater control over MDBX OS support and LMDB's version.
This commit is contained in:
230
slasher/src/database/interface.rs
Normal file
230
slasher/src/database/interface.rs
Normal file
@@ -0,0 +1,230 @@
|
||||
use crate::{Config, DatabaseBackend, Error};
|
||||
use std::borrow::Cow;
|
||||
use std::marker::PhantomData;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(feature = "lmdb")]
|
||||
use crate::database::lmdb_impl;
|
||||
#[cfg(feature = "mdbx")]
|
||||
use crate::database::mdbx_impl;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Environment {
|
||||
#[cfg(feature = "mdbx")]
|
||||
Mdbx(mdbx_impl::Environment),
|
||||
#[cfg(feature = "lmdb")]
|
||||
Lmdb(lmdb_impl::Environment),
|
||||
Disabled,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RwTransaction<'env> {
|
||||
#[cfg(feature = "mdbx")]
|
||||
Mdbx(mdbx_impl::RwTransaction<'env>),
|
||||
#[cfg(feature = "lmdb")]
|
||||
Lmdb(lmdb_impl::RwTransaction<'env>),
|
||||
Disabled(PhantomData<&'env ()>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Database<'env> {
|
||||
#[cfg(feature = "mdbx")]
|
||||
Mdbx(mdbx_impl::Database<'env>),
|
||||
#[cfg(feature = "lmdb")]
|
||||
Lmdb(lmdb_impl::Database<'env>),
|
||||
Disabled(PhantomData<&'env ()>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OpenDatabases<'env> {
|
||||
pub indexed_attestation_db: Database<'env>,
|
||||
pub indexed_attestation_id_db: Database<'env>,
|
||||
pub attesters_db: Database<'env>,
|
||||
pub attesters_max_targets_db: Database<'env>,
|
||||
pub min_targets_db: Database<'env>,
|
||||
pub max_targets_db: Database<'env>,
|
||||
pub current_epochs_db: Database<'env>,
|
||||
pub proposers_db: Database<'env>,
|
||||
pub metadata_db: Database<'env>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Cursor<'env> {
|
||||
#[cfg(feature = "mdbx")]
|
||||
Mdbx(mdbx_impl::Cursor<'env>),
|
||||
#[cfg(feature = "lmdb")]
|
||||
Lmdb(lmdb_impl::Cursor<'env>),
|
||||
Disabled(PhantomData<&'env ()>),
|
||||
}
|
||||
|
||||
pub type Key<'a> = Cow<'a, [u8]>;
|
||||
pub type Value<'a> = Cow<'a, [u8]>;
|
||||
|
||||
impl Environment {
|
||||
pub fn new(config: &Config) -> Result<Environment, Error> {
|
||||
match config.backend {
|
||||
#[cfg(feature = "mdbx")]
|
||||
DatabaseBackend::Mdbx => mdbx_impl::Environment::new(config).map(Environment::Mdbx),
|
||||
#[cfg(feature = "lmdb")]
|
||||
DatabaseBackend::Lmdb => lmdb_impl::Environment::new(config).map(Environment::Lmdb),
|
||||
DatabaseBackend::Disabled => Err(Error::SlasherDatabaseBackendDisabled),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_databases(&self) -> Result<OpenDatabases, Error> {
|
||||
match self {
|
||||
#[cfg(feature = "mdbx")]
|
||||
Self::Mdbx(env) => env.create_databases(),
|
||||
#[cfg(feature = "lmdb")]
|
||||
Self::Lmdb(env) => env.create_databases(),
|
||||
_ => Err(Error::MismatchedDatabaseVariant),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn begin_rw_txn(&self) -> Result<RwTransaction, Error> {
|
||||
match self {
|
||||
#[cfg(feature = "mdbx")]
|
||||
Self::Mdbx(env) => env.begin_rw_txn().map(RwTransaction::Mdbx),
|
||||
#[cfg(feature = "lmdb")]
|
||||
Self::Lmdb(env) => env.begin_rw_txn().map(RwTransaction::Lmdb),
|
||||
_ => Err(Error::MismatchedDatabaseVariant),
|
||||
}
|
||||
}
|
||||
|
||||
/// List of all files used by the database.
|
||||
pub fn filenames(&self, config: &Config) -> Vec<PathBuf> {
|
||||
match self {
|
||||
#[cfg(feature = "mdbx")]
|
||||
Self::Mdbx(env) => env.filenames(config),
|
||||
#[cfg(feature = "lmdb")]
|
||||
Self::Lmdb(env) => env.filenames(config),
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'env> RwTransaction<'env> {
|
||||
pub fn get<K: AsRef<[u8]> + ?Sized>(
|
||||
&'env self,
|
||||
db: &Database<'env>,
|
||||
key: &K,
|
||||
) -> Result<Option<Cow<'env, [u8]>>, Error> {
|
||||
match (self, db) {
|
||||
#[cfg(feature = "mdbx")]
|
||||
(Self::Mdbx(txn), Database::Mdbx(db)) => txn.get(db, key),
|
||||
#[cfg(feature = "lmdb")]
|
||||
(Self::Lmdb(txn), Database::Lmdb(db)) => txn.get(db, key),
|
||||
_ => Err(Error::MismatchedDatabaseVariant),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put<K: AsRef<[u8]>, V: AsRef<[u8]>>(
|
||||
&mut self,
|
||||
db: &Database,
|
||||
key: K,
|
||||
value: V,
|
||||
) -> Result<(), Error> {
|
||||
match (self, db) {
|
||||
#[cfg(feature = "mdbx")]
|
||||
(Self::Mdbx(txn), Database::Mdbx(db)) => txn.put(db, key, value),
|
||||
#[cfg(feature = "lmdb")]
|
||||
(Self::Lmdb(txn), Database::Lmdb(db)) => txn.put(db, key, value),
|
||||
_ => Err(Error::MismatchedDatabaseVariant),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn del<K: AsRef<[u8]>>(&mut self, db: &Database, key: K) -> Result<(), Error> {
|
||||
match (self, db) {
|
||||
#[cfg(feature = "mdbx")]
|
||||
(Self::Mdbx(txn), Database::Mdbx(db)) => txn.del(db, key),
|
||||
#[cfg(feature = "lmdb")]
|
||||
(Self::Lmdb(txn), Database::Lmdb(db)) => txn.del(db, key),
|
||||
_ => Err(Error::MismatchedDatabaseVariant),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cursor<'a>(&'a mut self, db: &Database) -> Result<Cursor<'a>, Error> {
|
||||
match (self, db) {
|
||||
#[cfg(feature = "mdbx")]
|
||||
(Self::Mdbx(txn), Database::Mdbx(db)) => txn.cursor(db).map(Cursor::Mdbx),
|
||||
#[cfg(feature = "lmdb")]
|
||||
(Self::Lmdb(txn), Database::Lmdb(db)) => txn.cursor(db).map(Cursor::Lmdb),
|
||||
_ => Err(Error::MismatchedDatabaseVariant),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn commit(self) -> Result<(), Error> {
|
||||
match self {
|
||||
#[cfg(feature = "mdbx")]
|
||||
Self::Mdbx(txn) => txn.commit(),
|
||||
#[cfg(feature = "lmdb")]
|
||||
Self::Lmdb(txn) => txn.commit(),
|
||||
_ => Err(Error::MismatchedDatabaseVariant),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'env> Cursor<'env> {
|
||||
/// Return the first key in the current database while advancing the cursor's position.
|
||||
pub fn first_key(&mut self) -> Result<Option<Key>, Error> {
|
||||
match self {
|
||||
#[cfg(feature = "mdbx")]
|
||||
Cursor::Mdbx(cursor) => cursor.first_key(),
|
||||
#[cfg(feature = "lmdb")]
|
||||
Cursor::Lmdb(cursor) => cursor.first_key(),
|
||||
_ => Err(Error::MismatchedDatabaseVariant),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the last key in the current database while advancing the cursor's position.
|
||||
pub fn last_key(&mut self) -> Result<Option<Key>, Error> {
|
||||
match self {
|
||||
#[cfg(feature = "mdbx")]
|
||||
Cursor::Mdbx(cursor) => cursor.last_key(),
|
||||
#[cfg(feature = "lmdb")]
|
||||
Cursor::Lmdb(cursor) => cursor.last_key(),
|
||||
_ => Err(Error::MismatchedDatabaseVariant),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_key(&mut self) -> Result<Option<Key>, Error> {
|
||||
match self {
|
||||
#[cfg(feature = "mdbx")]
|
||||
Cursor::Mdbx(cursor) => cursor.next_key(),
|
||||
#[cfg(feature = "lmdb")]
|
||||
Cursor::Lmdb(cursor) => cursor.next_key(),
|
||||
_ => Err(Error::MismatchedDatabaseVariant),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the key value pair at the current position.
|
||||
pub fn get_current(&mut self) -> Result<Option<(Key, Value)>, Error> {
|
||||
match self {
|
||||
#[cfg(feature = "mdbx")]
|
||||
Cursor::Mdbx(cursor) => cursor.get_current(),
|
||||
#[cfg(feature = "lmdb")]
|
||||
Cursor::Lmdb(cursor) => cursor.get_current(),
|
||||
_ => Err(Error::MismatchedDatabaseVariant),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete_current(&mut self) -> Result<(), Error> {
|
||||
match self {
|
||||
#[cfg(feature = "mdbx")]
|
||||
Cursor::Mdbx(cursor) => cursor.delete_current(),
|
||||
#[cfg(feature = "lmdb")]
|
||||
Cursor::Lmdb(cursor) => cursor.delete_current(),
|
||||
_ => Err(Error::MismatchedDatabaseVariant),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put<K: AsRef<[u8]>, V: AsRef<[u8]>>(&mut self, key: K, value: V) -> Result<(), Error> {
|
||||
match self {
|
||||
#[cfg(feature = "mdbx")]
|
||||
Self::Mdbx(cursor) => cursor.put(key, value),
|
||||
#[cfg(feature = "lmdb")]
|
||||
Self::Lmdb(cursor) => cursor.put(key, value),
|
||||
_ => Err(Error::MismatchedDatabaseVariant),
|
||||
}
|
||||
}
|
||||
}
|
||||
203
slasher/src/database/lmdb_impl.rs
Normal file
203
slasher/src/database/lmdb_impl.rs
Normal file
@@ -0,0 +1,203 @@
|
||||
#![cfg(feature = "lmdb")]
|
||||
|
||||
use crate::{
|
||||
config::MEGABYTE,
|
||||
database::{
|
||||
interface::{Key, OpenDatabases, Value},
|
||||
*,
|
||||
},
|
||||
Config, Error,
|
||||
};
|
||||
use lmdb::{Cursor as _, DatabaseFlags, Transaction, WriteFlags};
|
||||
use lmdb_sys::{MDB_FIRST, MDB_GET_CURRENT, MDB_LAST, MDB_NEXT};
|
||||
use std::borrow::Cow;
|
||||
use std::marker::PhantomData;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Environment {
|
||||
env: lmdb::Environment,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RwTransaction<'env> {
|
||||
txn: lmdb::RwTransaction<'env>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Database<'env> {
|
||||
db: lmdb::Database,
|
||||
_phantom: PhantomData<&'env ()>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Cursor<'env> {
|
||||
cursor: lmdb::RwCursor<'env>,
|
||||
}
|
||||
|
||||
impl Environment {
|
||||
pub fn new(config: &Config) -> Result<Environment, Error> {
|
||||
let env = lmdb::Environment::new()
|
||||
.set_max_dbs(MAX_NUM_DBS as u32)
|
||||
.set_map_size(config.max_db_size_mbs * MEGABYTE)
|
||||
.open_with_permissions(&config.database_path, 0o600)?;
|
||||
Ok(Environment { env })
|
||||
}
|
||||
|
||||
pub fn create_databases(&self) -> Result<OpenDatabases, Error> {
|
||||
let indexed_attestation_db = self
|
||||
.env
|
||||
.create_db(Some(INDEXED_ATTESTATION_DB), Self::db_flags())?;
|
||||
let indexed_attestation_id_db = self
|
||||
.env
|
||||
.create_db(Some(INDEXED_ATTESTATION_ID_DB), Self::db_flags())?;
|
||||
let attesters_db = self.env.create_db(Some(ATTESTERS_DB), Self::db_flags())?;
|
||||
let attesters_max_targets_db = self
|
||||
.env
|
||||
.create_db(Some(ATTESTERS_MAX_TARGETS_DB), Self::db_flags())?;
|
||||
let min_targets_db = self.env.create_db(Some(MIN_TARGETS_DB), Self::db_flags())?;
|
||||
let max_targets_db = self.env.create_db(Some(MAX_TARGETS_DB), Self::db_flags())?;
|
||||
let current_epochs_db = self
|
||||
.env
|
||||
.create_db(Some(CURRENT_EPOCHS_DB), Self::db_flags())?;
|
||||
let proposers_db = self.env.create_db(Some(PROPOSERS_DB), Self::db_flags())?;
|
||||
let metadata_db = self.env.create_db(Some(METADATA_DB), Self::db_flags())?;
|
||||
|
||||
let wrap = |db| {
|
||||
crate::Database::Lmdb(Database {
|
||||
db,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
};
|
||||
|
||||
Ok(OpenDatabases {
|
||||
indexed_attestation_db: wrap(indexed_attestation_db),
|
||||
indexed_attestation_id_db: wrap(indexed_attestation_id_db),
|
||||
attesters_db: wrap(attesters_db),
|
||||
attesters_max_targets_db: wrap(attesters_max_targets_db),
|
||||
min_targets_db: wrap(min_targets_db),
|
||||
max_targets_db: wrap(max_targets_db),
|
||||
current_epochs_db: wrap(current_epochs_db),
|
||||
proposers_db: wrap(proposers_db),
|
||||
metadata_db: wrap(metadata_db),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn begin_rw_txn(&self) -> Result<RwTransaction, Error> {
|
||||
let txn = self.env.begin_rw_txn()?;
|
||||
Ok(RwTransaction { txn })
|
||||
}
|
||||
|
||||
pub fn filenames(&self, config: &Config) -> Vec<PathBuf> {
|
||||
vec![
|
||||
config.database_path.join("data.mdb"),
|
||||
config.database_path.join("lock.mdb"),
|
||||
]
|
||||
}
|
||||
|
||||
fn db_flags() -> DatabaseFlags {
|
||||
DatabaseFlags::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'env> RwTransaction<'env> {
|
||||
pub fn get<K: AsRef<[u8]> + ?Sized>(
|
||||
&'env self,
|
||||
db: &Database<'env>,
|
||||
key: &K,
|
||||
) -> Result<Option<Cow<'env, [u8]>>, Error> {
|
||||
Ok(self.txn.get(db.db, key).optional()?.map(Cow::Borrowed))
|
||||
}
|
||||
|
||||
pub fn put<K: AsRef<[u8]>, V: AsRef<[u8]>>(
|
||||
&mut self,
|
||||
db: &Database,
|
||||
key: K,
|
||||
value: V,
|
||||
) -> Result<(), Error> {
|
||||
self.txn.put(db.db, &key, &value, Self::write_flags())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn del<K: AsRef<[u8]>>(&mut self, db: &Database, key: K) -> Result<(), Error> {
|
||||
self.txn.del(db.db, &key, None)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn cursor<'a>(&'a mut self, db: &Database) -> Result<Cursor<'a>, Error> {
|
||||
let cursor = self.txn.open_rw_cursor(db.db)?;
|
||||
Ok(Cursor { cursor })
|
||||
}
|
||||
|
||||
pub fn commit(self) -> Result<(), Error> {
|
||||
self.txn.commit()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_flags() -> WriteFlags {
|
||||
WriteFlags::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'env> Cursor<'env> {
|
||||
pub fn first_key(&mut self) -> Result<Option<Key>, Error> {
|
||||
let opt_key = self
|
||||
.cursor
|
||||
.get(None, None, MDB_FIRST)
|
||||
.optional()?
|
||||
.and_then(|(key, _)| Some(Cow::Borrowed(key?)));
|
||||
Ok(opt_key)
|
||||
}
|
||||
|
||||
pub fn last_key(&mut self) -> Result<Option<Key<'env>>, Error> {
|
||||
let opt_key = self
|
||||
.cursor
|
||||
.get(None, None, MDB_LAST)
|
||||
.optional()?
|
||||
.and_then(|(key, _)| Some(Cow::Borrowed(key?)));
|
||||
Ok(opt_key)
|
||||
}
|
||||
|
||||
pub fn next_key(&mut self) -> Result<Option<Key<'env>>, Error> {
|
||||
let opt_key = self
|
||||
.cursor
|
||||
.get(None, None, MDB_NEXT)
|
||||
.optional()?
|
||||
.and_then(|(key, _)| Some(Cow::Borrowed(key?)));
|
||||
Ok(opt_key)
|
||||
}
|
||||
|
||||
pub fn get_current(&mut self) -> Result<Option<(Key<'env>, Value<'env>)>, Error> {
|
||||
if let Some((Some(key), value)) = self.cursor.get(None, None, MDB_GET_CURRENT).optional()? {
|
||||
Ok(Some((Cow::Borrowed(key), Cow::Borrowed(value))))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete_current(&mut self) -> Result<(), Error> {
|
||||
self.cursor.del(RwTransaction::write_flags())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn put<K: AsRef<[u8]>, V: AsRef<[u8]>>(&mut self, key: K, value: V) -> Result<(), Error> {
|
||||
self.cursor
|
||||
.put(&key, &value, RwTransaction::write_flags())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Mix-in trait for loading values from LMDB that may or may not exist.
|
||||
pub trait TxnOptional<T, E> {
|
||||
fn optional(self) -> Result<Option<T>, E>;
|
||||
}
|
||||
|
||||
impl<T> TxnOptional<T, Error> for Result<T, lmdb::Error> {
|
||||
fn optional(self) -> Result<Option<T>, Error> {
|
||||
match self {
|
||||
Ok(x) => Ok(Some(x)),
|
||||
Err(lmdb::Error::NotFound) => Ok(None),
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
186
slasher/src/database/mdbx_impl.rs
Normal file
186
slasher/src/database/mdbx_impl.rs
Normal file
@@ -0,0 +1,186 @@
|
||||
#![cfg(feature = "mdbx")]
|
||||
|
||||
use crate::{
|
||||
config::MEGABYTE,
|
||||
database::{
|
||||
interface::{Key, OpenDatabases, Value},
|
||||
*,
|
||||
},
|
||||
Config, Error,
|
||||
};
|
||||
use mdbx::{DatabaseFlags, Geometry, WriteFlags};
|
||||
use std::borrow::Cow;
|
||||
use std::ops::Range;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub const MDBX_GROWTH_STEP: isize = 256 * (1 << 20); // 256 MiB
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Environment {
|
||||
env: mdbx::Environment<mdbx::NoWriteMap>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RwTransaction<'env> {
|
||||
txn: mdbx::Transaction<'env, mdbx::RW, mdbx::NoWriteMap>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Database<'env> {
|
||||
db: mdbx::Database<'env>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Cursor<'env> {
|
||||
cursor: mdbx::Cursor<'env, mdbx::RW>,
|
||||
}
|
||||
|
||||
impl Environment {
|
||||
pub fn new(config: &Config) -> Result<Environment, Error> {
|
||||
let env = mdbx::Environment::new()
|
||||
.set_max_dbs(MAX_NUM_DBS)
|
||||
.set_geometry(Self::geometry(config))
|
||||
.open_with_permissions(&config.database_path, 0o600)?;
|
||||
Ok(Environment { env })
|
||||
}
|
||||
|
||||
pub fn create_databases(&self) -> Result<OpenDatabases, Error> {
|
||||
let txn = self.begin_rw_txn()?;
|
||||
txn.create_db(INDEXED_ATTESTATION_DB)?;
|
||||
txn.create_db(INDEXED_ATTESTATION_ID_DB)?;
|
||||
txn.create_db(ATTESTERS_DB)?;
|
||||
txn.create_db(ATTESTERS_MAX_TARGETS_DB)?;
|
||||
txn.create_db(MIN_TARGETS_DB)?;
|
||||
txn.create_db(MAX_TARGETS_DB)?;
|
||||
txn.create_db(CURRENT_EPOCHS_DB)?;
|
||||
txn.create_db(PROPOSERS_DB)?;
|
||||
txn.create_db(METADATA_DB)?;
|
||||
|
||||
// This is all rather nasty
|
||||
let (_, mut databases) = txn.txn.commit_and_rebind_open_dbs()?;
|
||||
let mut next_db = || {
|
||||
crate::Database::Mdbx(Database {
|
||||
db: databases.remove(0),
|
||||
})
|
||||
};
|
||||
|
||||
Ok(OpenDatabases {
|
||||
indexed_attestation_db: next_db(),
|
||||
indexed_attestation_id_db: next_db(),
|
||||
attesters_db: next_db(),
|
||||
attesters_max_targets_db: next_db(),
|
||||
min_targets_db: next_db(),
|
||||
max_targets_db: next_db(),
|
||||
current_epochs_db: next_db(),
|
||||
proposers_db: next_db(),
|
||||
metadata_db: next_db(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn begin_rw_txn(&self) -> Result<RwTransaction, Error> {
|
||||
let txn = self.env.begin_rw_txn()?;
|
||||
Ok(RwTransaction { txn })
|
||||
}
|
||||
|
||||
pub fn filenames(&self, config: &Config) -> Vec<PathBuf> {
|
||||
vec![
|
||||
config.database_path.join("mdbx.dat"),
|
||||
config.database_path.join("mdbx.lck"),
|
||||
]
|
||||
}
|
||||
|
||||
fn geometry(config: &Config) -> Geometry<Range<usize>> {
|
||||
Geometry {
|
||||
size: Some(0..config.max_db_size_mbs * MEGABYTE),
|
||||
growth_step: Some(MDBX_GROWTH_STEP),
|
||||
shrink_threshold: None,
|
||||
page_size: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'env> RwTransaction<'env> {
|
||||
pub fn create_db(&self, name: &'static str) -> Result<(), Error> {
|
||||
let db = self.txn.create_db(Some(name), Self::db_flags())?;
|
||||
self.txn.prime_for_permaopen(db);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn open_db(&self, name: &'static str) -> Result<Database, Error> {
|
||||
let db = self.txn.open_db(Some(name))?;
|
||||
Ok(Database { db })
|
||||
}
|
||||
|
||||
pub fn get<K: AsRef<[u8]> + ?Sized>(
|
||||
&'env self,
|
||||
db: &Database<'env>,
|
||||
key: &K,
|
||||
) -> Result<Option<Cow<'env, [u8]>>, Error> {
|
||||
Ok(self.txn.get(&db.db, key.as_ref())?)
|
||||
}
|
||||
|
||||
pub fn put<K: AsRef<[u8]>, V: AsRef<[u8]>>(
|
||||
&self,
|
||||
db: &Database,
|
||||
key: K,
|
||||
value: V,
|
||||
) -> Result<(), Error> {
|
||||
self.txn.put(&db.db, key, value, Self::write_flags())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn del<K: AsRef<[u8]>>(&self, db: &Database, key: K) -> Result<(), Error> {
|
||||
self.txn.del(&db.db, key, None)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn cursor<'a>(&'a self, db: &Database) -> Result<Cursor<'a>, Error> {
|
||||
let cursor = self.txn.cursor(&db.db)?;
|
||||
Ok(Cursor { cursor })
|
||||
}
|
||||
|
||||
pub fn commit(self) -> Result<(), Error> {
|
||||
self.txn.commit()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn db_flags() -> DatabaseFlags {
|
||||
DatabaseFlags::default()
|
||||
}
|
||||
|
||||
fn write_flags() -> WriteFlags {
|
||||
WriteFlags::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'env> Cursor<'env> {
|
||||
pub fn first_key(&mut self) -> Result<Option<Cow<'env, [u8]>>, Error> {
|
||||
let opt_key = self.cursor.first()?.map(|(key_bytes, ())| key_bytes);
|
||||
Ok(opt_key)
|
||||
}
|
||||
|
||||
pub fn last_key(&mut self) -> Result<Option<Cow<'env, [u8]>>, Error> {
|
||||
let opt_key = self.cursor.last()?.map(|(key_bytes, ())| key_bytes);
|
||||
Ok(opt_key)
|
||||
}
|
||||
|
||||
pub fn next_key(&mut self) -> Result<Option<Cow<'env, [u8]>>, Error> {
|
||||
let opt_key = self.cursor.next()?.map(|(key_bytes, ())| key_bytes);
|
||||
Ok(opt_key)
|
||||
}
|
||||
|
||||
pub fn get_current(&mut self) -> Result<Option<(Key<'env>, Value<'env>)>, Error> {
|
||||
Ok(self.cursor.get_current()?)
|
||||
}
|
||||
|
||||
pub fn delete_current(&mut self) -> Result<(), Error> {
|
||||
self.cursor.del(RwTransaction::write_flags())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn put<K: AsRef<[u8]>, V: AsRef<[u8]>>(&mut self, key: K, value: V) -> Result<(), Error> {
|
||||
self.cursor
|
||||
.put(key.as_ref(), value.as_ref(), RwTransaction::write_flags())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user