feat: phase out environment trait (#5439)

This commit is contained in:
Matthias Seitz
2023-11-15 17:42:34 +01:00
committed by GitHub
parent de0cca2488
commit dc72cad838
19 changed files with 212 additions and 227 deletions

View File

@ -17,7 +17,7 @@ use clap::Parser;
use reth_db::{
cursor::DbCursorRO, database::Database, open_db_read_only, table::Table, transaction::DbTx,
AccountChangeSet, AccountHistory, AccountsTrie, BlockBodyIndices, BlockOmmers,
BlockWithdrawals, Bytecodes, CanonicalHeaders, DatabaseEnvRO, HashedAccount, HashedStorage,
BlockWithdrawals, Bytecodes, CanonicalHeaders, DatabaseEnv, HashedAccount, HashedStorage,
HeaderNumbers, HeaderTD, Headers, PlainAccountState, PlainStorageState, PruneCheckpoints,
Receipts, StorageChangeSet, StorageHistory, StoragesTrie, SyncStage, SyncStageProgress, Tables,
TransactionBlock, Transactions, TxHashNumber, TxSenders,
@ -58,7 +58,7 @@ impl Command {
///
/// The discrepancies and extra elements, along with a brief summary of the diff results are
/// then written to a file in the output directory.
pub fn execute(self, tool: &DbTool<'_, DatabaseEnvRO>) -> eyre::Result<()> {
pub fn execute(self, tool: &DbTool<'_, DatabaseEnv>) -> eyre::Result<()> {
// open second db
let second_db_path: PathBuf = self.secondary_datadir.join("db").into();
let second_db = open_db_read_only(&second_db_path, self.second_db.log_level)?;

View File

@ -2,7 +2,7 @@ use super::tui::DbListTUI;
use crate::utils::{DbTool, ListFilter};
use clap::Parser;
use eyre::WrapErr;
use reth_db::{database::Database, table::Table, DatabaseEnvRO, RawValue, TableViewer, Tables};
use reth_db::{database::Database, table::Table, DatabaseEnv, RawValue, TableViewer, Tables};
use reth_primitives::hex;
use std::cell::RefCell;
use tracing::error;
@ -50,7 +50,7 @@ pub struct Command {
impl Command {
/// Execute `db list` command
pub fn execute(self, tool: &DbTool<'_, DatabaseEnvRO>) -> eyre::Result<()> {
pub fn execute(self, tool: &DbTool<'_, DatabaseEnv>) -> eyre::Result<()> {
self.table.view(&ListTableViewer { tool, args: &self })
}
@ -81,7 +81,7 @@ impl Command {
}
struct ListTableViewer<'a> {
tool: &'a DbTool<'a, DatabaseEnvRO>,
tool: &'a DbTool<'a, DatabaseEnv>,
args: &'a Command,
}

View File

@ -1,4 +1,4 @@
use reth_db::DatabaseEnvRO;
use reth_db::DatabaseEnv;
use reth_primitives::{
snapshot::{Compression, Filters},
ChainSpec, SnapshotSegment,
@ -16,7 +16,7 @@ pub(crate) enum BenchKind {
pub(crate) fn bench<F1, F2, R>(
bench_kind: BenchKind,
db: (DatabaseEnvRO, Arc<ChainSpec>),
db: (DatabaseEnv, Arc<ChainSpec>),
segment: SnapshotSegment,
filters: Filters,
compression: Compression,
@ -25,7 +25,7 @@ pub(crate) fn bench<F1, F2, R>(
) -> eyre::Result<()>
where
F1: FnMut() -> eyre::Result<R>,
F2: Fn(DatabaseProviderRO<'_, DatabaseEnvRO>) -> eyre::Result<R>,
F2: Fn(DatabaseProviderRO<'_, DatabaseEnv>) -> eyre::Result<R>,
R: Debug + PartialEq,
{
let (db, chain) = db;

View File

@ -1,6 +1,6 @@
use clap::Parser;
use itertools::Itertools;
use reth_db::{open_db_read_only, DatabaseEnvRO};
use reth_db::{open_db_read_only, DatabaseEnv};
use reth_interfaces::db::LogLevel;
use reth_primitives::{
snapshot::{Compression, InclusionFilter, PerfectHashingFunction},
@ -71,22 +71,21 @@ impl Command {
if !self.only_bench {
for ((mode, compression), phf) in all_combinations.clone() {
match mode {
SnapshotSegment::Headers => self
.generate_headers_snapshot::<DatabaseEnvRO>(
&provider,
*compression,
InclusionFilter::Cuckoo,
*phf,
)?,
SnapshotSegment::Headers => self.generate_headers_snapshot::<DatabaseEnv>(
&provider,
*compression,
InclusionFilter::Cuckoo,
*phf,
)?,
SnapshotSegment::Transactions => self
.generate_transactions_snapshot::<DatabaseEnvRO>(
.generate_transactions_snapshot::<DatabaseEnv>(
&provider,
*compression,
InclusionFilter::Cuckoo,
*phf,
)?,
SnapshotSegment::Receipts => self
.generate_receipts_snapshot::<DatabaseEnvRO>(
.generate_receipts_snapshot::<DatabaseEnv>(
&provider,
*compression,
InclusionFilter::Cuckoo,

View File

@ -395,7 +395,7 @@ mod tests {
use assert_matches::assert_matches;
use futures::poll;
use reth_db::{
mdbx::{Env, WriteMap},
mdbx::DatabaseEnv,
test_utils::{create_test_rw_db, TempDatabase},
};
use reth_interfaces::{p2p::either::EitherDownloader, test_utils::TestFullBlockClient};
@ -449,7 +449,7 @@ mod tests {
}
/// Builds the pipeline.
fn build(self, chain_spec: Arc<ChainSpec>) -> Pipeline<Arc<TempDatabase<Env<WriteMap>>>> {
fn build(self, chain_spec: Arc<ChainSpec>) -> Pipeline<Arc<TempDatabase<DatabaseEnv>>> {
reth_tracing::init_test_tracing();
let db = create_test_rw_db();

View File

@ -8,8 +8,7 @@ use crate::{
};
use reth_interfaces::db::LogLevel;
use reth_libmdbx::{
DatabaseFlags, Environment, EnvironmentFlags, EnvironmentKind, Geometry, Mode, PageSize,
SyncMode, RO, RW,
DatabaseFlags, Environment, EnvironmentFlags, Geometry, Mode, PageSize, SyncMode, RO, RW,
};
use std::{ops::Deref, path::Path};
use tx::Tx;
@ -25,7 +24,7 @@ const DEFAULT_MAX_READERS: u64 = 32_000;
/// Environment used when opening a MDBX environment. RO/RW.
#[derive(Debug)]
pub enum EnvKind {
pub enum DatabaseEnvKind {
/// Read-only MDBX environment.
RO,
/// Read-write MDBX environment.
@ -34,19 +33,19 @@ pub enum EnvKind {
/// Wrapper for the libmdbx environment.
#[derive(Debug)]
pub struct Env<E: EnvironmentKind> {
pub struct DatabaseEnv {
/// Libmdbx-sys environment.
pub inner: Environment<E>,
pub inner: Environment,
/// Whether to record metrics or not.
with_metrics: bool,
}
impl<'a, E: EnvironmentKind> DatabaseGAT<'a> for Env<E> {
type TX = tx::Tx<'a, RO, E>;
type TXMut = tx::Tx<'a, RW, E>;
impl<'a> DatabaseGAT<'a> for DatabaseEnv {
type TX = tx::Tx<'a, RO>;
type TXMut = tx::Tx<'a, RW>;
}
impl<E: EnvironmentKind> Database for Env<E> {
impl Database for DatabaseEnv {
fn tx(&self) -> Result<<Self as DatabaseGAT<'_>>::TX, DatabaseError> {
Ok(Tx::new_with_metrics(
self.inner.begin_ro_txn().map_err(|e| DatabaseError::InitTx(e.into()))?,
@ -62,21 +61,26 @@ impl<E: EnvironmentKind> Database for Env<E> {
}
}
impl<E: EnvironmentKind> Env<E> {
impl DatabaseEnv {
/// Opens the database at the specified path with the given `EnvKind`.
///
/// It does not create the tables, for that call [`Env::create_tables`].
/// It does not create the tables, for that call [`DatabaseEnv::create_tables`].
pub fn open(
path: &Path,
kind: EnvKind,
kind: DatabaseEnvKind,
log_level: Option<LogLevel>,
) -> Result<Env<E>, DatabaseError> {
) -> Result<DatabaseEnv, DatabaseError> {
let mut inner_env = Environment::builder();
let mode = match kind {
EnvKind::RO => Mode::ReadOnly,
EnvKind::RW => Mode::ReadWrite { sync_mode: SyncMode::Durable },
DatabaseEnvKind::RO => Mode::ReadOnly,
DatabaseEnvKind::RW => {
// enable writemap mode in RW mode
inner_env.write_map();
Mode::ReadWrite { sync_mode: SyncMode::Durable }
}
};
let mut inner_env = Environment::builder();
inner_env.set_max_dbs(Tables::ALL.len());
inner_env.set_geometry(Geometry {
// Maximum database size of 4 terabytes
@ -124,7 +128,7 @@ impl<E: EnvironmentKind> Env<E> {
}
}
let env = Env {
let env = DatabaseEnv {
inner: inner_env.open(path).map_err(|e| DatabaseError::Open(e.into()))?,
with_metrics: false,
};
@ -158,8 +162,8 @@ impl<E: EnvironmentKind> Env<E> {
}
}
impl<E: EnvironmentKind> Deref for Env<E> {
type Target = Environment<E>;
impl Deref for DatabaseEnv {
type Target = Environment;
fn deref(&self) -> &Self::Target {
&self.inner
@ -180,13 +184,12 @@ mod tests {
AccountChangeSet,
};
use reth_interfaces::db::{DatabaseWriteError, DatabaseWriteOperation};
use reth_libmdbx::{NoWriteMap, WriteMap};
use reth_primitives::{Account, Address, Header, IntegerList, StorageEntry, B256, U256};
use std::{path::Path, str::FromStr, sync::Arc};
use tempfile::TempDir;
/// Create database for testing
fn create_test_db<E: EnvironmentKind>(kind: EnvKind) -> Arc<Env<E>> {
fn create_test_db(kind: DatabaseEnvKind) -> Arc<DatabaseEnv> {
Arc::new(create_test_db_with_path(
kind,
&tempfile::TempDir::new().expect(ERROR_TEMPDIR).into_path(),
@ -194,8 +197,8 @@ mod tests {
}
/// Create database for testing with specified path
fn create_test_db_with_path<E: EnvironmentKind>(kind: EnvKind, path: &Path) -> Env<E> {
let env = Env::<E>::open(path, kind, None).expect(ERROR_DB_CREATION);
fn create_test_db_with_path(kind: DatabaseEnvKind, path: &Path) -> DatabaseEnv {
let env = DatabaseEnv::open(path, kind, None).expect(ERROR_DB_CREATION);
env.create_tables().expect(ERROR_TABLE_CREATION);
env
}
@ -212,12 +215,12 @@ mod tests {
#[test]
fn db_creation() {
create_test_db::<NoWriteMap>(EnvKind::RW);
create_test_db(DatabaseEnvKind::RW);
}
#[test]
fn db_manual_put_get() {
let env = create_test_db::<NoWriteMap>(EnvKind::RW);
let env = create_test_db(DatabaseEnvKind::RW);
let value = Header::default();
let key = 1u64;
@ -236,7 +239,7 @@ mod tests {
#[test]
fn db_cursor_walk() {
let env = create_test_db::<NoWriteMap>(EnvKind::RW);
let env = create_test_db(DatabaseEnvKind::RW);
let value = Header::default();
let key = 1u64;
@ -261,7 +264,7 @@ mod tests {
#[test]
fn db_cursor_walk_range() {
let db: Arc<Env<WriteMap>> = create_test_db(EnvKind::RW);
let db: Arc<DatabaseEnv> = create_test_db(DatabaseEnvKind::RW);
// PUT (0, 0), (1, 0), (2, 0), (3, 0)
let tx = db.tx_mut().expect(ERROR_INIT_TX);
@ -325,7 +328,7 @@ mod tests {
#[test]
fn db_cursor_walk_range_on_dup_table() {
let db: Arc<Env<WriteMap>> = create_test_db(EnvKind::RW);
let db: Arc<DatabaseEnv> = create_test_db(DatabaseEnvKind::RW);
let address0 = Address::ZERO;
let address1 = Address::with_last_byte(1);
@ -367,7 +370,7 @@ mod tests {
#[allow(clippy::reversed_empty_ranges)]
#[test]
fn db_cursor_walk_range_invalid() {
let db: Arc<Env<WriteMap>> = create_test_db(EnvKind::RW);
let db: Arc<DatabaseEnv> = create_test_db(DatabaseEnvKind::RW);
// PUT (0, 0), (1, 0), (2, 0), (3, 0)
let tx = db.tx_mut().expect(ERROR_INIT_TX);
@ -395,7 +398,7 @@ mod tests {
#[test]
fn db_walker() {
let db: Arc<Env<WriteMap>> = create_test_db(EnvKind::RW);
let db: Arc<DatabaseEnv> = create_test_db(DatabaseEnvKind::RW);
// PUT (0, 0), (1, 0), (3, 0)
let tx = db.tx_mut().expect(ERROR_INIT_TX);
@ -425,7 +428,7 @@ mod tests {
#[test]
fn db_reverse_walker() {
let db: Arc<Env<WriteMap>> = create_test_db(EnvKind::RW);
let db: Arc<DatabaseEnv> = create_test_db(DatabaseEnvKind::RW);
// PUT (0, 0), (1, 0), (3, 0)
let tx = db.tx_mut().expect(ERROR_INIT_TX);
@ -455,7 +458,7 @@ mod tests {
#[test]
fn db_walk_back() {
let db: Arc<Env<WriteMap>> = create_test_db(EnvKind::RW);
let db: Arc<DatabaseEnv> = create_test_db(DatabaseEnvKind::RW);
// PUT (0, 0), (1, 0), (3, 0)
let tx = db.tx_mut().expect(ERROR_INIT_TX);
@ -494,7 +497,7 @@ mod tests {
#[test]
fn db_cursor_seek_exact_or_previous_key() {
let db: Arc<Env<WriteMap>> = create_test_db(EnvKind::RW);
let db: Arc<DatabaseEnv> = create_test_db(DatabaseEnvKind::RW);
// PUT
let tx = db.tx_mut().expect(ERROR_INIT_TX);
@ -520,7 +523,7 @@ mod tests {
#[test]
fn db_cursor_insert() {
let db: Arc<Env<WriteMap>> = create_test_db(EnvKind::RW);
let db: Arc<DatabaseEnv> = create_test_db(DatabaseEnvKind::RW);
// PUT
let tx = db.tx_mut().expect(ERROR_INIT_TX);
@ -563,7 +566,7 @@ mod tests {
#[test]
fn db_cursor_insert_dup() {
let db: Arc<Env<WriteMap>> = create_test_db(EnvKind::RW);
let db: Arc<DatabaseEnv> = create_test_db(DatabaseEnvKind::RW);
let tx = db.tx_mut().expect(ERROR_INIT_TX);
let mut dup_cursor = tx.cursor_dup_write::<PlainStorageState>().unwrap();
@ -581,7 +584,7 @@ mod tests {
#[test]
fn db_cursor_delete_current_non_existent() {
let db: Arc<Env<WriteMap>> = create_test_db(EnvKind::RW);
let db: Arc<DatabaseEnv> = create_test_db(DatabaseEnvKind::RW);
let tx = db.tx_mut().expect(ERROR_INIT_TX);
let key1 = Address::with_last_byte(1);
@ -609,7 +612,7 @@ mod tests {
#[test]
fn db_cursor_insert_wherever_cursor_is() {
let db: Arc<Env<WriteMap>> = create_test_db(EnvKind::RW);
let db: Arc<DatabaseEnv> = create_test_db(DatabaseEnvKind::RW);
let tx = db.tx_mut().expect(ERROR_INIT_TX);
// PUT
@ -642,7 +645,7 @@ mod tests {
#[test]
fn db_cursor_append() {
let db: Arc<Env<WriteMap>> = create_test_db(EnvKind::RW);
let db: Arc<DatabaseEnv> = create_test_db(DatabaseEnvKind::RW);
// PUT
let tx = db.tx_mut().expect(ERROR_INIT_TX);
@ -669,7 +672,7 @@ mod tests {
#[test]
fn db_cursor_append_failure() {
let db: Arc<Env<WriteMap>> = create_test_db(EnvKind::RW);
let db: Arc<DatabaseEnv> = create_test_db(DatabaseEnvKind::RW);
// PUT
let tx = db.tx_mut().expect(ERROR_INIT_TX);
@ -706,7 +709,7 @@ mod tests {
#[test]
fn db_cursor_upsert() {
let db: Arc<Env<WriteMap>> = create_test_db(EnvKind::RW);
let db: Arc<DatabaseEnv> = create_test_db(DatabaseEnvKind::RW);
let tx = db.tx_mut().expect(ERROR_INIT_TX);
let mut cursor = tx.cursor_write::<PlainAccountState>().unwrap();
@ -741,7 +744,7 @@ mod tests {
#[test]
fn db_cursor_dupsort_append() {
let db: Arc<Env<WriteMap>> = create_test_db(EnvKind::RW);
let db: Arc<DatabaseEnv> = create_test_db(DatabaseEnvKind::RW);
let transition_id = 2;
@ -810,28 +813,28 @@ mod tests {
.expect(ERROR_ETH_ADDRESS);
{
let env = create_test_db_with_path::<WriteMap>(EnvKind::RW, &path);
let env = create_test_db_with_path(DatabaseEnvKind::RW, &path);
// PUT
let result = env.update(|tx| {
tx.put::<PlainAccountState>(key, value).expect(ERROR_PUT);
200
});
assert!(result.expect(ERROR_RETURN_VALUE) == 200);
assert_eq!(result.expect(ERROR_RETURN_VALUE), 200);
}
let env = Env::<WriteMap>::open(&path, EnvKind::RO, None).expect(ERROR_DB_CREATION);
let env = DatabaseEnv::open(&path, DatabaseEnvKind::RO, None).expect(ERROR_DB_CREATION);
// GET
let result =
env.view(|tx| tx.get::<PlainAccountState>(key).expect(ERROR_GET)).expect(ERROR_GET);
assert!(result == Some(value))
assert_eq!(result, Some(value))
}
#[test]
fn db_dup_sort() {
let env = create_test_db::<NoWriteMap>(EnvKind::RW);
let env = create_test_db(DatabaseEnvKind::RW);
let key = Address::from_str("0xa2c122be93b0074270ebee7f6b7292c7deb45047")
.expect(ERROR_ETH_ADDRESS);
@ -875,7 +878,7 @@ mod tests {
#[test]
fn db_iterate_over_all_dup_values() {
let env = create_test_db::<NoWriteMap>(EnvKind::RW);
let env = create_test_db(DatabaseEnvKind::RW);
let key1 = Address::from_str("0x1111111111111111111111111111111111111111")
.expect(ERROR_ETH_ADDRESS);
let key2 = Address::from_str("0x2222222222222222222222222222222222222222")
@ -921,7 +924,7 @@ mod tests {
#[test]
fn dup_value_with_same_subkey() {
let env = create_test_db::<NoWriteMap>(EnvKind::RW);
let env = create_test_db(DatabaseEnvKind::RW);
let key1 = Address::new([0x11; 20]);
let key2 = Address::new([0x22; 20]);
@ -964,7 +967,7 @@ mod tests {
#[test]
fn db_sharded_key() {
let db: Arc<Env<WriteMap>> = create_test_db(EnvKind::RW);
let db: Arc<DatabaseEnv> = create_test_db(DatabaseEnvKind::RW);
let real_key = Address::from_str("0xa2c122be93b0074270ebee7f6b7292c7deb45047").unwrap();
for i in 1..5 {

View File

@ -12,14 +12,14 @@ use crate::{
};
use parking_lot::RwLock;
use reth_interfaces::db::{DatabaseWriteError, DatabaseWriteOperation};
use reth_libmdbx::{ffi::DBI, EnvironmentKind, Transaction, TransactionKind, WriteFlags, RW};
use reth_libmdbx::{ffi::DBI, Transaction, TransactionKind, WriteFlags, RW};
use std::{marker::PhantomData, str::FromStr, sync::Arc, time::Instant};
/// Wrapper for the libmdbx transaction.
#[derive(Debug)]
pub struct Tx<'a, K: TransactionKind, E: EnvironmentKind> {
pub struct Tx<'a, K: TransactionKind> {
/// Libmdbx-sys transaction.
pub inner: Transaction<'a, K, E>,
pub inner: Transaction<'a, K>,
/// Database table handle cache.
pub(crate) db_handles: Arc<RwLock<[Option<DBI>; NUM_TABLES]>>,
/// Handler for metrics with its own [Drop] implementation for cases when the transaction isn't
@ -29,9 +29,9 @@ pub struct Tx<'a, K: TransactionKind, E: EnvironmentKind> {
metrics_handler: Option<MetricsHandler<K>>,
}
impl<'env, K: TransactionKind, E: EnvironmentKind> Tx<'env, K, E> {
impl<'env, K: TransactionKind> Tx<'env, K> {
/// Creates new `Tx` object with a `RO` or `RW` transaction.
pub fn new<'a>(inner: Transaction<'a, K, E>) -> Self
pub fn new<'a>(inner: Transaction<'a, K>) -> Self
where
'a: 'env,
{
@ -39,7 +39,7 @@ impl<'env, K: TransactionKind, E: EnvironmentKind> Tx<'env, K, E> {
}
/// Creates new `Tx` object with a `RO` or `RW` transaction and optionally enables metrics.
pub fn new_with_metrics<'a>(inner: Transaction<'a, K, E>, with_metrics: bool) -> Self
pub fn new_with_metrics<'a>(inner: Transaction<'a, K>, with_metrics: bool) -> Self
where
'a: 'env,
{
@ -128,7 +128,7 @@ impl<'env, K: TransactionKind, E: EnvironmentKind> Tx<'env, K, E> {
&self,
operation: Operation,
value_size: Option<usize>,
f: impl FnOnce(&Transaction<'_, K, E>) -> R,
f: impl FnOnce(&Transaction<'_, K>) -> R,
) -> R {
if self.metrics_handler.is_some() {
OperationMetrics::record(T::NAME, operation, value_size, || f(&self.inner))
@ -173,19 +173,19 @@ impl<K: TransactionKind> Drop for MetricsHandler<K> {
}
}
impl<'a, K: TransactionKind, E: EnvironmentKind> DbTxGAT<'a> for Tx<'_, K, E> {
impl<'a, K: TransactionKind> DbTxGAT<'a> for Tx<'_, K> {
type Cursor<T: Table> = Cursor<'a, K, T>;
type DupCursor<T: DupSort> = Cursor<'a, K, T>;
}
impl<'a, K: TransactionKind, E: EnvironmentKind> DbTxMutGAT<'a> for Tx<'_, K, E> {
impl<'a, K: TransactionKind> DbTxMutGAT<'a> for Tx<'_, K> {
type CursorMut<T: Table> = Cursor<'a, RW, T>;
type DupCursorMut<T: DupSort> = Cursor<'a, RW, T>;
}
impl<E: EnvironmentKind> TableImporter for Tx<'_, RW, E> {}
impl TableImporter for Tx<'_, RW> {}
impl<K: TransactionKind, E: EnvironmentKind> DbTx for Tx<'_, K, E> {
impl<K: TransactionKind> DbTx for Tx<'_, K> {
fn get<T: Table>(&self, key: T::Key) -> Result<Option<<T as Table>::Value>, DatabaseError> {
self.execute_with_operation_metric::<T, _>(Operation::Get, None, |tx| {
tx.get(self.get_dbi::<T>()?, key.encode().as_ref())
@ -229,7 +229,7 @@ impl<K: TransactionKind, E: EnvironmentKind> DbTx for Tx<'_, K, E> {
}
}
impl<E: EnvironmentKind> DbTxMut for Tx<'_, RW, E> {
impl DbTxMut for Tx<'_, RW> {
fn put<T: Table>(&self, key: T::Key, value: T::Value) -> Result<(), DatabaseError> {
let key = key.encode();
let value = value.compress();

View File

@ -87,15 +87,7 @@ pub use tables::*;
pub use utils::is_database_empty;
#[cfg(feature = "mdbx")]
use mdbx::{Env, EnvKind, NoWriteMap, WriteMap};
#[cfg(feature = "mdbx")]
/// Alias type for the database environment in use. Read/Write mode.
pub type DatabaseEnv = Env<WriteMap>;
#[cfg(feature = "mdbx")]
/// Alias type for the database engine in use. Read only mode.
pub type DatabaseEnvRO = Env<NoWriteMap>;
pub use mdbx::{DatabaseEnv, DatabaseEnvKind};
use eyre::WrapErr;
use reth_interfaces::db::LogLevel;
@ -120,7 +112,7 @@ pub fn init_db<P: AsRef<Path>>(path: P, log_level: Option<LogLevel>) -> eyre::Re
}
#[cfg(feature = "mdbx")]
{
let db = DatabaseEnv::open(rpath, EnvKind::RW, log_level)?;
let db = DatabaseEnv::open(rpath, DatabaseEnvKind::RW, log_level)?;
db.create_tables()?;
Ok(db)
}
@ -131,10 +123,10 @@ pub fn init_db<P: AsRef<Path>>(path: P, log_level: Option<LogLevel>) -> eyre::Re
}
/// Opens up an existing database. Read only mode. It doesn't create it or create tables if missing.
pub fn open_db_read_only(path: &Path, log_level: Option<LogLevel>) -> eyre::Result<DatabaseEnvRO> {
pub fn open_db_read_only(path: &Path, log_level: Option<LogLevel>) -> eyre::Result<DatabaseEnv> {
#[cfg(feature = "mdbx")]
{
Env::<NoWriteMap>::open(path, EnvKind::RO, log_level)
DatabaseEnv::open(path, DatabaseEnvKind::RO, log_level)
.with_context(|| format!("Could not open database at path: {}", path.display()))
}
#[cfg(not(feature = "mdbx"))]
@ -143,12 +135,12 @@ pub fn open_db_read_only(path: &Path, log_level: Option<LogLevel>) -> eyre::Resu
}
}
/// Opens up an existing database. Read/Write mode. It doesn't create it or create tables if
/// missing.
/// Opens up an existing database. Read/Write mode with WriteMap enabled. It doesn't create it or
/// create tables if missing.
pub fn open_db(path: &Path, log_level: Option<LogLevel>) -> eyre::Result<DatabaseEnv> {
#[cfg(feature = "mdbx")]
{
Env::<WriteMap>::open(path, EnvKind::RW, log_level)
DatabaseEnv::open(path, DatabaseEnvKind::RW, log_level)
.with_context(|| format!("Could not open database at path: {}", path.display()))
}
#[cfg(not(feature = "mdbx"))]
@ -234,7 +226,7 @@ pub mod test_utils {
}
/// Create read only database for testing
pub fn create_test_ro_db() -> Arc<TempDatabase<DatabaseEnvRO>> {
pub fn create_test_ro_db() -> Arc<TempDatabase<DatabaseEnv>> {
let path = tempfile::TempDir::new().expect(ERROR_TEMPDIR).into_path();
{
init_db(path.as_path(), None).expect(ERROR_DB_CREATION);

View File

@ -1,4 +1,4 @@
use reth_libmdbx::{Environment, NoWriteMap, WriteFlags};
use reth_libmdbx::{Environment, WriteFlags};
use tempfile::{tempdir, TempDir};
pub fn get_key(n: u32) -> String {
@ -9,7 +9,7 @@ pub fn get_data(n: u32) -> String {
format!("data{n}")
}
pub fn setup_bench_db(num_rows: u32) -> (TempDir, Environment<NoWriteMap>) {
pub fn setup_bench_db(num_rows: u32) -> (TempDir, Environment) {
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();

View File

@ -3,7 +3,7 @@ use crate::{
flags::*,
mdbx_try_optional,
transaction::{TransactionKind, TransactionPtr, RW},
EnvironmentKind, TableObject, Transaction,
TableObject, Transaction,
};
use ffi::{
MDBX_cursor_op, MDBX_FIRST, MDBX_FIRST_DUP, MDBX_GET_BOTH, MDBX_GET_BOTH_RANGE,
@ -28,10 +28,7 @@ impl<'txn, K> Cursor<'txn, K>
where
K: TransactionKind,
{
pub(crate) fn new<E: EnvironmentKind>(
txn: &'txn Transaction<'_, K, E>,
dbi: ffi::MDBX_dbi,
) -> Result<Self> {
pub(crate) fn new(txn: &'txn Transaction<'_, K>, dbi: ffi::MDBX_dbi) -> Result<Self> {
let mut cursor: *mut ffi::MDBX_cursor = ptr::null_mut();
let txn = txn.txn_ptr();
unsafe {

View File

@ -1,5 +1,4 @@
use crate::{
environment::EnvironmentKind,
error::{mdbx_result, Result},
transaction::TransactionKind,
Transaction,
@ -21,8 +20,8 @@ impl<'txn> Database<'txn> {
///
/// Prefer using `Environment::open_db`, `Environment::create_db`, `TransactionExt::open_db`,
/// or `RwTransaction::create_db`.
pub(crate) fn new<'env, K: TransactionKind, E: EnvironmentKind>(
txn: &'txn Transaction<'env, K, E>,
pub(crate) fn new<'env, K: TransactionKind>(
txn: &'txn Transaction<'env, K>,
name: Option<&str>,
flags: MDBX_db_flags_t,
) -> Result<Self> {

View File

@ -11,7 +11,6 @@ use std::{
ffi::CString,
fmt,
fmt::Debug,
marker::PhantomData,
mem,
ops::{Bound, RangeBounds},
path::Path,
@ -21,32 +20,41 @@ use std::{
time::Duration,
};
mod private {
use super::*;
pub trait Sealed {}
impl Sealed for NoWriteMap {}
impl Sealed for WriteMap {}
/// Determines how data is mapped into memory
///
/// It only takes affect when the environment is opened.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum EnvironmentKind {
/// Open the environment in default mode, without WRITEMAP.
#[default]
Default,
/// Open the environment as mdbx-WRITEMAP.
/// Use a writeable memory map unless the environment is opened as MDBX_RDONLY
/// ([Mode::ReadOnly]).
///
/// All data will be mapped into memory in the read-write mode [Mode::ReadWrite]. This offers a
/// significant performance benefit, since the data will be modified directly in mapped
/// memory and then flushed to disk by single system call, without any memory management
/// nor copying.
///
/// This mode is incompatible with nested transactions.
WriteMap,
}
pub trait EnvironmentKind: private::Sealed + Debug + 'static {
const EXTRA_FLAGS: ffi::MDBX_env_flags_t;
}
impl EnvironmentKind {
/// Returns true if the environment was opened as WRITEMAP.
#[inline]
pub const fn is_write_map(&self) -> bool {
matches!(self, EnvironmentKind::WriteMap)
}
#[derive(Debug)]
#[non_exhaustive]
pub struct NoWriteMap;
#[derive(Debug)]
#[non_exhaustive]
pub struct WriteMap;
impl EnvironmentKind for NoWriteMap {
const EXTRA_FLAGS: ffi::MDBX_env_flags_t = ffi::MDBX_ENV_DEFAULTS;
}
impl EnvironmentKind for WriteMap {
const EXTRA_FLAGS: ffi::MDBX_env_flags_t = ffi::MDBX_WRITEMAP;
/// Additional flags required when opening the environment.
pub(crate) fn extra_flags(&self) -> ffi::MDBX_env_flags_t {
match self {
EnvironmentKind::Default => ffi::MDBX_ENV_DEFAULTS,
EnvironmentKind::WriteMap => ffi::MDBX_WRITEMAP,
}
}
}
#[derive(Copy, Clone, Debug)]
@ -66,20 +74,13 @@ pub(crate) enum TxnManagerMessage {
}
/// An environment supports multiple databases, all residing in the same shared-memory map.
pub struct Environment<E>
where
E: EnvironmentKind,
{
pub struct Environment {
inner: EnvironmentInner,
_marker: PhantomData<E>,
}
impl<E> Environment<E>
where
E: EnvironmentKind,
{
impl Environment {
/// Creates a new builder for specifying options for opening an MDBX environment.
pub fn builder() -> EnvironmentBuilder<E> {
pub fn builder() -> EnvironmentBuilder {
EnvironmentBuilder {
flags: EnvironmentFlags::default(),
max_readers: None,
@ -92,10 +93,20 @@ where
spill_min_denominator: None,
geometry: None,
log_level: None,
_marker: PhantomData,
kind: Default::default(),
}
}
/// Returns true if the environment was opened as WRITEMAP.
pub fn is_write_map(&self) -> bool {
self.inner.env_kind.is_write_map()
}
/// Returns the kind of the environment.
pub fn env_kind(&self) -> EnvironmentKind {
self.inner.env_kind
}
/// Returns the manager that handles transaction messages.
///
/// Requires [Mode::ReadWrite] and returns None otherwise.
@ -115,13 +126,13 @@ where
/// Create a read-only transaction for use with the environment.
#[inline]
pub fn begin_ro_txn(&self) -> Result<Transaction<'_, RO, E>> {
pub fn begin_ro_txn(&self) -> Result<Transaction<'_, RO>> {
Transaction::new(self)
}
/// Create a read-write transaction for use with the environment. This method will block while
/// there are any other read-write transactions open on the environment.
pub fn begin_rw_txn(&self) -> Result<Transaction<'_, RW, E>> {
pub fn begin_rw_txn(&self) -> Result<Transaction<'_, RW>> {
let sender = self.txn_manager().ok_or(Error::Access)?;
let txn = loop {
let (tx, rx) = sync_channel(0);
@ -183,9 +194,8 @@ where
///
/// ```
/// # use reth_libmdbx::Environment;
/// # use reth_libmdbx::NoWriteMap;
/// let dir = tempfile::tempdir().unwrap();
/// let env = Environment::<NoWriteMap>::builder().open(dir.path()).unwrap();
/// let env = Environment::builder().open(dir.path()).unwrap();
/// let info = env.info().unwrap();
/// let stat = env.stat().unwrap();
/// let freelist = env.freelist().unwrap();
@ -228,6 +238,7 @@ where
/// The env is opened via [mdbx_env_create](ffi::mdbx_env_create) and closed when this type drops.
struct EnvironmentInner {
env: *mut ffi::MDBX_env,
env_kind: EnvironmentKind,
txn_manager: Option<SyncSender<TxnManagerMessage>>,
}
@ -347,15 +358,12 @@ impl Info {
}
}
unsafe impl<E> Send for Environment<E> where E: EnvironmentKind {}
unsafe impl<E> Sync for Environment<E> where E: EnvironmentKind {}
unsafe impl Send for Environment {}
unsafe impl Sync for Environment {}
impl<E> fmt::Debug for Environment<E>
where
E: EnvironmentKind,
{
impl fmt::Debug for Environment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Environment").finish_non_exhaustive()
f.debug_struct("Environment").field("kind", &self.inner.env_kind).finish_non_exhaustive()
}
}
@ -385,10 +393,7 @@ impl<R> Default for Geometry<R> {
/// Options for opening or creating an environment.
#[derive(Debug, Clone)]
pub struct EnvironmentBuilder<E>
where
E: EnvironmentKind,
{
pub struct EnvironmentBuilder {
flags: EnvironmentFlags,
max_readers: Option<u64>,
max_dbs: Option<u64>,
@ -400,17 +405,14 @@ where
spill_min_denominator: Option<u64>,
geometry: Option<Geometry<(Option<usize>, Option<usize>)>>,
log_level: Option<ffi::MDBX_log_level_t>,
_marker: PhantomData<E>,
kind: EnvironmentKind,
}
impl<E> EnvironmentBuilder<E>
where
E: EnvironmentKind,
{
impl EnvironmentBuilder {
/// Open an environment.
///
/// Database files will be opened with 644 permissions.
pub fn open(&self, path: &Path) -> Result<Environment<E>> {
pub fn open(&self, path: &Path) -> Result<Environment> {
self.open_with_permissions(path, 0o644)
}
@ -421,7 +423,7 @@ where
&self,
path: &Path,
mode: ffi::mdbx_mode_t,
) -> Result<Environment<E>> {
) -> Result<Environment> {
let mut env: *mut ffi::MDBX_env = ptr::null_mut();
unsafe {
if let Some(log_level) = self.log_level {
@ -505,7 +507,7 @@ where
mdbx_result(ffi::mdbx_env_open(
env,
path.as_ptr(),
self.flags.make_flags() | E::EXTRA_FLAGS,
self.flags.make_flags() | self.kind.extra_flags(),
mode,
))?;
@ -517,7 +519,7 @@ where
}
}
let mut env = EnvironmentInner { env, txn_manager: None };
let mut env = EnvironmentInner { env, txn_manager: None, env_kind: self.kind };
if let Mode::ReadWrite { .. } = self.flags.mode {
let (tx, rx) = std::sync::mpsc::sync_channel(0);
@ -562,7 +564,20 @@ where
env.txn_manager = Some(tx);
}
Ok(Environment { inner: env, _marker: Default::default() })
Ok(Environment { inner: env })
}
/// Configures how this environment will be opened.
pub fn set_kind(&mut self, kind: EnvironmentKind) -> &mut Self {
self.kind = kind;
self
}
/// Opens the environment with mdbx WRITEMAP
///
/// See also [EnvironmentKind]
pub fn write_map(&mut self) -> &mut Self {
self.set_kind(EnvironmentKind::WriteMap)
}
/// Sets the provided options in the environment.

View File

@ -55,6 +55,7 @@ pub enum Error {
Access,
TooLarge,
DecodeErrorLenDiff,
NestedTransactionsUnsupportedWithWriteMap,
Other(i32),
}

View File

@ -28,11 +28,11 @@ pub enum SyncMode {
/// are recycled the MVCC snapshots corresponding to previous "steady" transactions (see
/// below).
///
/// With [crate::WriteMap] the [SyncMode::SafeNoSync] instructs MDBX to use asynchronous
/// mmap-flushes to disk. Asynchronous mmap-flushes means that actually all writes will
/// scheduled and performed by operation system on it own manner, i.e. unordered.
/// MDBX itself just notify operating system that it would be nice to write data to disk, but
/// no more.
/// With [crate::EnvironmentKind::WriteMap] the [SyncMode::SafeNoSync] instructs MDBX to use
/// asynchronous mmap-flushes to disk. Asynchronous mmap-flushes means that actually all
/// writes will scheduled and performed by operation system on it own manner, i.e.
/// unordered. MDBX itself just notify operating system that it would be nice to write data
/// to disk, but no more.
///
/// Depending on the platform and hardware, with [SyncMode::SafeNoSync] you may get a multiple
/// increase of write performance, even 10 times or more.
@ -70,17 +70,18 @@ pub enum SyncMode {
/// you may get a multiple increase of write performance, even 100 times or more.
///
/// If the filesystem preserves write order (which is rare and never provided unless explicitly
/// noted) and the [WriteMap](crate::WriteMap) and [EnvironmentFlags::liforeclaim] flags are
/// not used, then a system crash can't corrupt the database, but you can lose the last
/// transactions, if at least one buffer is not yet flushed to disk. The risk is governed
/// by how often the system flushes dirty buffers to disk and how often
/// [Environment::sync()](crate::Environment::sync) is called. So, transactions exhibit ACI
/// (atomicity, consistency, isolation) properties and only lose D (durability).
/// I.e. database integrity is maintained, but a system crash may undo the final transactions.
/// noted) and the [WriteMap](crate::EnvironmentKind::WriteMap) and
/// [EnvironmentFlags::liforeclaim] flags are not used, then a system crash can't corrupt
/// the database, but you can lose the last transactions, if at least one buffer is not yet
/// flushed to disk. The risk is governed by how often the system flushes dirty buffers to
/// disk and how often [Environment::sync()](crate::Environment::sync) is called. So,
/// transactions exhibit ACI (atomicity, consistency, isolation) properties and only lose D
/// (durability). I.e. database integrity is maintained, but a system crash may undo the
/// final transactions.
///
/// Otherwise, if the filesystem not preserves write order (which is typically) or
/// [WriteMap](crate::WriteMap) or [EnvironmentFlags::liforeclaim] flags are used, you should
/// expect the corrupted database after a system crash.
/// [WriteMap](crate::EnvironmentKind::WriteMap) or [EnvironmentFlags::liforeclaim] flags are
/// used, you should expect the corrupted database after a system crash.
///
/// So, most important thing about [SyncMode::UtterlyNoSync]:
/// - A system crash immediately after commit the write transaction high likely lead to

View File

@ -15,8 +15,7 @@ pub use crate::{
cursor::{Cursor, Iter, IterDup},
database::Database,
environment::{
Environment, EnvironmentBuilder, EnvironmentKind, Geometry, Info, NoWriteMap, PageSize,
Stat, WriteMap,
Environment, EnvironmentBuilder, EnvironmentKind, Geometry, Info, PageSize, Stat,
},
error::{Error, Result},
flags::*,
@ -40,8 +39,6 @@ mod test_utils {
use byteorder::{ByteOrder, LittleEndian};
use tempfile::tempdir;
type Environment = crate::Environment<NoWriteMap>;
/// Regression test for https://github.com/danburkert/lmdb-rs/issues/21.
/// This test reliably segfaults when run against lmbdb compiled with opt level -O3 and newer
/// GCC compilers.

View File

@ -1,6 +1,6 @@
use crate::{
database::Database,
environment::{Environment, EnvironmentKind, NoWriteMap, TxnManagerMessage, TxnPtr},
environment::{Environment, TxnManagerMessage, TxnPtr},
error::{mdbx_result, Result},
flags::{DatabaseFlags, WriteFlags},
Cursor, Error, Stat, TableObject,
@ -12,7 +12,6 @@ use parking_lot::Mutex;
use std::{
fmt,
fmt::Debug,
marker::PhantomData,
mem::size_of,
ptr, slice,
sync::{atomic::AtomicBool, mpsc::sync_channel, Arc},
@ -60,20 +59,18 @@ impl TransactionKind for RW {
/// An MDBX transaction.
///
/// All database operations require a transaction.
pub struct Transaction<'env, K, E>
pub struct Transaction<'env, K>
where
K: TransactionKind,
E: EnvironmentKind,
{
inner: Arc<TransactionInner<'env, K, E>>,
inner: Arc<TransactionInner<'env, K>>,
}
impl<'env, K, E> Transaction<'env, K, E>
impl<'env, K> Transaction<'env, K>
where
K: TransactionKind,
E: EnvironmentKind,
{
pub(crate) fn new(env: &'env Environment<E>) -> Result<Self> {
pub(crate) fn new(env: &'env Environment) -> Result<Self> {
let mut txn: *mut ffi::MDBX_txn = ptr::null_mut();
unsafe {
mdbx_result(ffi::mdbx_txn_begin_ex(
@ -87,13 +84,13 @@ where
}
}
pub(crate) fn new_from_ptr(env: &'env Environment<E>, txn: *mut ffi::MDBX_txn) -> Self {
pub(crate) fn new_from_ptr(env: &'env Environment, txn: *mut ffi::MDBX_txn) -> Self {
let inner = TransactionInner {
txn: TransactionPtr::new(txn),
primed_dbis: Mutex::new(IndexSet::new()),
committed: AtomicBool::new(false),
env,
_marker: PhantomData,
_marker: Default::default(),
};
Self { inner: Arc::new(inner) }
}
@ -118,7 +115,7 @@ where
}
/// Returns a raw pointer to the MDBX environment.
pub fn env(&self) -> &Environment<E> {
pub fn env(&self) -> &Environment {
self.inner.env
}
@ -253,10 +250,9 @@ where
}
/// Internals of a transaction.
struct TransactionInner<'env, K, E>
struct TransactionInner<'env, K>
where
K: TransactionKind,
E: EnvironmentKind,
{
/// The transaction pointer itself.
txn: TransactionPtr,
@ -264,14 +260,13 @@ where
primed_dbis: Mutex<IndexSet<ffi::MDBX_dbi>>,
/// Whether the transaction has committed.
committed: AtomicBool,
env: &'env Environment<E>,
_marker: PhantomData<fn(K)>,
env: &'env Environment,
_marker: std::marker::PhantomData<fn(K)>,
}
impl<'env, K, E> TransactionInner<'env, K, E>
impl<'env, K> TransactionInner<'env, K>
where
K: TransactionKind,
E: EnvironmentKind,
{
/// Marks the transaction as committed.
fn set_committed(&self) {
@ -288,10 +283,9 @@ where
}
}
impl<'env, K, E> Drop for TransactionInner<'env, K, E>
impl<'env, K> Drop for TransactionInner<'env, K>
where
K: TransactionKind,
E: EnvironmentKind,
{
fn drop(&mut self) {
self.txn_execute(|txn| {
@ -314,10 +308,7 @@ where
}
}
impl<'env, E> Transaction<'env, RW, E>
where
E: EnvironmentKind,
{
impl<'env> Transaction<'env, RW> {
fn open_db_with_flags(&self, name: Option<&str>, flags: DatabaseFlags) -> Result<Database<'_>> {
Database::new(self, name, flags.bits())
}
@ -451,10 +442,7 @@ where
}
}
impl<'env, E> Transaction<'env, RO, E>
where
E: EnvironmentKind,
{
impl<'env> Transaction<'env, RO> {
/// Closes the database handle.
///
/// # Safety
@ -467,9 +455,12 @@ where
}
}
impl<'env> Transaction<'env, RW, NoWriteMap> {
impl<'env> Transaction<'env, RW> {
/// Begins a new nested transaction inside of this transaction.
pub fn begin_nested_txn(&mut self) -> Result<Transaction<'_, RW, NoWriteMap>> {
pub fn begin_nested_txn(&mut self) -> Result<Transaction<'_, RW>> {
if self.inner.env.is_write_map() {
return Err(Error::NestedTransactionsUnsupportedWithWriteMap)
}
self.txn_execute(|txn| {
let (tx, rx) = sync_channel(0);
self.env()
@ -487,10 +478,9 @@ impl<'env> Transaction<'env, RW, NoWriteMap> {
}
}
impl<'env, K, E> fmt::Debug for Transaction<'env, K, E>
impl<'env, K> fmt::Debug for Transaction<'env, K>
where
K: TransactionKind,
E: EnvironmentKind,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RoTransaction").finish_non_exhaustive()
@ -526,15 +516,12 @@ unsafe impl Sync for TransactionPtr {}
#[cfg(test)]
mod tests {
use super::*;
use crate::WriteMap;
fn assert_send_sync<T: Send + Sync>() {}
#[allow(dead_code)]
fn test_txn_send_sync() {
assert_send_sync::<Transaction<'_, RO, NoWriteMap>>();
assert_send_sync::<Transaction<'_, RW, NoWriteMap>>();
assert_send_sync::<Transaction<'_, RO, WriteMap>>();
assert_send_sync::<Transaction<'_, RW, WriteMap>>();
assert_send_sync::<Transaction<'_, RO>>();
assert_send_sync::<Transaction<'_, RW>>();
}
}

View File

@ -2,8 +2,6 @@ use reth_libmdbx::*;
use std::borrow::Cow;
use tempfile::tempdir;
type Environment = reth_libmdbx::Environment<NoWriteMap>;
#[test]
fn test_get() {
let dir = tempdir().unwrap();

View File

@ -2,8 +2,6 @@ use byteorder::{ByteOrder, LittleEndian};
use reth_libmdbx::*;
use tempfile::tempdir;
type Environment = reth_libmdbx::Environment<NoWriteMap>;
#[test]
fn test_open() {
let dir = tempdir().unwrap();

View File

@ -7,8 +7,6 @@ use std::{
};
use tempfile::tempdir;
type Environment = reth_libmdbx::Environment<NoWriteMap>;
#[test]
fn test_put_get_del() {
let dir = tempdir().unwrap();