feat(storage, mdbx): transaction manager (#6126)

This commit is contained in:
Alexey Shekhirin
2024-01-23 12:24:56 +00:00
committed by GitHub
parent 9a5120a883
commit a6f8e449f7
36 changed files with 821 additions and 262 deletions

View File

@ -87,13 +87,13 @@ pub use utils::is_database_empty;
#[cfg(feature = "mdbx")]
pub use mdbx::{DatabaseEnv, DatabaseEnvKind};
use crate::mdbx::DatabaseArguments;
use eyre::WrapErr;
use reth_interfaces::db::LogLevel;
use std::path::Path;
/// Opens up an existing database or creates a new one at the specified path. Creates tables if
/// necessary. Read/Write mode.
pub fn init_db<P: AsRef<Path>>(path: P, log_level: Option<LogLevel>) -> eyre::Result<DatabaseEnv> {
pub fn init_db<P: AsRef<Path>>(path: P, args: DatabaseArguments) -> eyre::Result<DatabaseEnv> {
use crate::version::{check_db_version_file, create_db_version_file, DatabaseVersionError};
let rpath = path.as_ref();
@ -110,7 +110,7 @@ pub fn init_db<P: AsRef<Path>>(path: P, log_level: Option<LogLevel>) -> eyre::Re
}
#[cfg(feature = "mdbx")]
{
let db = DatabaseEnv::open(rpath, DatabaseEnvKind::RW, log_level)?;
let db = DatabaseEnv::open(rpath, DatabaseEnvKind::RW, args)?;
db.create_tables()?;
Ok(db)
}
@ -121,10 +121,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<DatabaseEnv> {
pub fn open_db_read_only(path: &Path, args: DatabaseArguments) -> eyre::Result<DatabaseEnv> {
#[cfg(feature = "mdbx")]
{
DatabaseEnv::open(path, DatabaseEnvKind::RO, log_level)
DatabaseEnv::open(path, DatabaseEnvKind::RO, args)
.with_context(|| format!("Could not open database at path: {}", path.display()))
}
#[cfg(not(feature = "mdbx"))]
@ -135,10 +135,10 @@ pub fn open_db_read_only(path: &Path, log_level: Option<LogLevel>) -> eyre::Resu
/// 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> {
pub fn open_db(path: &Path, args: DatabaseArguments) -> eyre::Result<DatabaseEnv> {
#[cfg(feature = "mdbx")]
{
DatabaseEnv::open(path, DatabaseEnvKind::RW, log_level)
DatabaseEnv::open(path, DatabaseEnvKind::RW, args)
.with_context(|| format!("Could not open database at path: {}", path.display()))
}
#[cfg(not(feature = "mdbx"))]
@ -155,6 +155,7 @@ pub mod test_utils {
database::Database,
database_metrics::{DatabaseMetadata, DatabaseMetadataValue, DatabaseMetrics},
};
use reth_libmdbx::MaxReadTransactionDuration;
use reth_primitives::fs;
use std::{path::PathBuf, sync::Arc};
@ -235,7 +236,12 @@ pub mod test_utils {
let path = tempdir_path();
let emsg = format!("{}: {:?}", ERROR_DB_CREATION, path);
let db = init_db(&path, None).expect(&emsg);
let db = init_db(
&path,
DatabaseArguments::default()
.max_read_transaction_duration(Some(MaxReadTransactionDuration::Unbounded)),
)
.expect(&emsg);
Arc::new(TempDatabase { db: Some(db), path })
}
@ -243,17 +249,25 @@ pub mod test_utils {
/// Create read/write database for testing
pub fn create_test_rw_db_with_path<P: AsRef<Path>>(path: P) -> Arc<TempDatabase<DatabaseEnv>> {
let path = path.as_ref().to_path_buf();
let db = init_db(path.as_path(), None).expect(ERROR_DB_CREATION);
let db = init_db(
path.as_path(),
DatabaseArguments::default()
.max_read_transaction_duration(Some(MaxReadTransactionDuration::Unbounded)),
)
.expect(ERROR_DB_CREATION);
Arc::new(TempDatabase { db: Some(db), path })
}
/// Create read only database for testing
pub fn create_test_ro_db() -> Arc<TempDatabase<DatabaseEnv>> {
let args = DatabaseArguments::default()
.max_read_transaction_duration(Some(MaxReadTransactionDuration::Unbounded));
let path = tempdir_path();
{
init_db(path.as_path(), None).expect(ERROR_DB_CREATION);
init_db(path.as_path(), args).expect(ERROR_DB_CREATION);
}
let db = open_db_read_only(path.as_path(), None).expect(ERROR_DB_OPEN);
let db = open_db_read_only(path.as_path(), args).expect(ERROR_DB_OPEN);
Arc::new(TempDatabase { db: Some(db), path })
}
}
@ -262,9 +276,11 @@ pub mod test_utils {
mod tests {
use crate::{
init_db,
mdbx::DatabaseArguments,
version::{db_version_file_path, DatabaseVersionError},
};
use assert_matches::assert_matches;
use reth_libmdbx::MaxReadTransactionDuration;
use reth_primitives::fs;
use tempfile::tempdir;
@ -272,22 +288,25 @@ mod tests {
fn db_version() {
let path = tempdir().unwrap();
let args = DatabaseArguments::default()
.max_read_transaction_duration(Some(MaxReadTransactionDuration::Unbounded));
// Database is empty
{
let db = init_db(&path, None);
let db = init_db(&path, args);
assert_matches!(db, Ok(_));
}
// Database is not empty, current version is the same as in the file
{
let db = init_db(&path, None);
let db = init_db(&path, args);
assert_matches!(db, Ok(_));
}
// Database is not empty, version file is malformed
{
fs::write(path.path().join(db_version_file_path(&path)), "invalid-version").unwrap();
let db = init_db(&path, None);
let db = init_db(&path, args);
assert!(db.is_err());
assert_matches!(
db.unwrap_err().downcast_ref::<DatabaseVersionError>(),
@ -298,7 +317,7 @@ mod tests {
// Database is not empty, version file contains not matching version
{
fs::write(path.path().join(db_version_file_path(&path)), "0").unwrap();
let db = init_db(&path, None);
let db = init_db(&path, args);
assert!(db.is_err());
assert_matches!(
db.unwrap_err().downcast_ref::<DatabaseVersionError>(),