diff --git a/crates/storage/db/src/implementation/mdbx/tx.rs b/crates/storage/db/src/implementation/mdbx/tx.rs index 3661c64d32..16d38ead67 100644 --- a/crates/storage/db/src/implementation/mdbx/tx.rs +++ b/crates/storage/db/src/implementation/mdbx/tx.rs @@ -8,7 +8,6 @@ use crate::{ transaction::{DbTx, DbTxMut}, DatabaseError, }; -use parking_lot::Mutex; use reth_interfaces::db::{DatabaseWriteError, DatabaseWriteOperation}; use reth_libmdbx::{ffi::DBI, CommitLatency, Transaction, TransactionKind, WriteFlags, RW}; use reth_tracing::tracing::{debug, trace, warn}; @@ -17,7 +16,7 @@ use std::{ marker::PhantomData, sync::{ atomic::{AtomicBool, Ordering}, - Arc, + Arc, OnceLock, }, time::{Duration, Instant}, }; @@ -38,7 +37,7 @@ pub struct Tx { metrics_handler: Option>, /// Database table handle cache. - db_handles: Mutex<[Option; Tables::COUNT]>, + db_handles: [OnceLock; Tables::COUNT], } impl Tx { @@ -66,7 +65,14 @@ impl Tx { #[inline] fn new_inner(inner: Transaction, metrics_handler: Option>) -> Self { - Self { inner, db_handles: Mutex::new([None; Tables::COUNT]), metrics_handler } + // NOTE: These constants are needed to initialize `OnceLock` at compile-time, as array + // initialization is not allowed with non-Copy types, and `const { }` blocks are not stable + // yet. + #[allow(clippy::declare_interior_mutable_const)] + const ONCELOCK_DBI_NEW: OnceLock = OnceLock::new(); + #[allow(clippy::declare_interior_mutable_const)] + const DB_HANDLES: [OnceLock; Tables::COUNT] = [ONCELOCK_DBI_NEW; Tables::COUNT]; + Self { inner, db_handles: DB_HANDLES, metrics_handler } } /// Gets this transaction ID. @@ -76,13 +82,22 @@ impl Tx { /// Gets a table database handle if it exists, otherwise creates it. pub fn get_dbi(&self) -> Result { - match self.db_handles.lock()[T::TABLE as usize] { - Some(handle) => Ok(handle), - ref mut handle @ None => { - let db = - self.inner.open_db(Some(T::NAME)).map_err(|e| DatabaseError::Open(e.into()))?; - Ok(*handle.insert(db.dbi())) + // TODO: Use `OnceLock::get_or_try_init` once it's stable. + let slot = &self.db_handles[T::TABLE as usize]; + match slot.get() { + Some(handle) => Ok(*handle), + None => self.open_and_store_db::(slot), + } + } + + #[cold] + fn open_and_store_db(&self, slot: &OnceLock) -> Result { + match self.inner.open_db(Some(T::NAME)) { + Ok(db) => { + slot.set(db.dbi()).unwrap(); + Ok(db.dbi()) } + Err(e) => Err(DatabaseError::Open(e.into())), } }