-
Notifications
You must be signed in to change notification settings - Fork 1.9k
feat(db): add MDBX put-append for fast ordered puts #18603
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
#![allow(missing_docs)] | ||
|
||
use alloy_primitives::B256; | ||
use criterion::{criterion_group, criterion_main, Criterion}; | ||
use reth_db::{test_utils::create_test_rw_db_with_path, CanonicalHeaders, Database}; | ||
use reth_db_api::transaction::DbTxMut; | ||
|
||
mod utils; | ||
use utils::BENCH_DB_PATH; | ||
|
||
const NUM_BLOCKS: u64 = 1_000_000; | ||
|
||
criterion_group! { | ||
name = benches; | ||
config = Criterion::default(); | ||
targets = put | ||
} | ||
criterion_main!(benches); | ||
|
||
// Small benchmark showing that `append` is much faster than `put` when keys are put in order | ||
fn put(c: &mut Criterion) { | ||
let mut group = c.benchmark_group("Put"); | ||
|
||
let setup = || { | ||
let _ = std::fs::remove_dir_all(BENCH_DB_PATH); | ||
create_test_rw_db_with_path(BENCH_DB_PATH).tx_mut().expect("tx") | ||
}; | ||
|
||
group.bench_function("put", |b| { | ||
b.iter_with_setup(setup, |tx| { | ||
for i in 0..NUM_BLOCKS { | ||
tx.put::<CanonicalHeaders>(i, B256::ZERO).unwrap(); | ||
} | ||
}) | ||
}); | ||
|
||
group.bench_function("append", |b| { | ||
b.iter_with_setup(setup, |tx| { | ||
for i in 0..NUM_BLOCKS { | ||
tx.append::<CanonicalHeaders>(i, B256::ZERO).unwrap(); | ||
} | ||
}) | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -340,28 +340,64 @@ impl<K: TransactionKind> DbTx for Tx<K> { | |||||
} | ||||||
} | ||||||
|
||||||
#[derive(Clone, Copy)] | ||||||
enum PutKind { | ||||||
/// Default kind that inserts a new key-value or overwrites an existed key. | ||||||
Upsert, | ||||||
/// Append the key-value to the end of the table -- fast path when the new | ||||||
/// key is the highest so far, like the latest block number. | ||||||
Append, | ||||||
} | ||||||
|
||||||
impl PutKind { | ||||||
const fn into_operation_and_flags(self) -> (Operation, DatabaseWriteOperation, WriteFlags) { | ||||||
match self { | ||||||
Self::Upsert => { | ||||||
(Operation::PutUpsert, DatabaseWriteOperation::PutUpsert, WriteFlags::UPSERT) | ||||||
} | ||||||
Self::Append => { | ||||||
(Operation::PutAppend, DatabaseWriteOperation::PutAppend, WriteFlags::APPEND) | ||||||
} | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
impl Tx<RW> { | ||||||
/// The inner implementation mapping to `mdbx_put` that supports different | ||||||
/// put kinds like upserting and appending. | ||||||
fn put<T: Table>( | ||||||
&self, | ||||||
kind: PutKind, | ||||||
key: T::Key, | ||||||
value: T::Value, | ||||||
) -> Result<(), DatabaseError> { | ||||||
let key = key.encode(); | ||||||
let value = value.compress(); | ||||||
let (operation, write_operation, flags) = kind.into_operation_and_flags(); | ||||||
self.execute_with_operation_metric::<T, _>(operation, Some(value.as_ref().len()), |tx| { | ||||||
tx.put(self.get_dbi::<T>()?, key.as_ref(), value, flags).map_err(|e| { | ||||||
DatabaseWriteError { | ||||||
info: e.into(), | ||||||
operation: write_operation, | ||||||
table_name: T::NAME, | ||||||
key: key.into(), | ||||||
} | ||||||
.into() | ||||||
}) | ||||||
}) | ||||||
} | ||||||
} | ||||||
|
||||||
impl DbTxMut for Tx<RW> { | ||||||
type CursorMut<T: Table> = Cursor<RW, T>; | ||||||
type DupCursorMut<T: DupSort> = Cursor<RW, T>; | ||||||
|
||||||
fn put<T: Table>(&self, key: T::Key, value: T::Value) -> Result<(), DatabaseError> { | ||||||
let key = key.encode(); | ||||||
let value = value.compress(); | ||||||
self.execute_with_operation_metric::<T, _>( | ||||||
Operation::Put, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how is putupsert different from just put? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's the same, just renamed for clarity. By default with flag reth/crates/storage/libmdbx-rs/mdbx-sys/libmdbx/mdbx.h Lines 1624 to 1625 in cff5512
|
||||||
Some(value.as_ref().len()), | ||||||
|tx| { | ||||||
tx.put(self.get_dbi::<T>()?, key.as_ref(), value, WriteFlags::UPSERT).map_err(|e| { | ||||||
DatabaseWriteError { | ||||||
info: e.into(), | ||||||
operation: DatabaseWriteOperation::Put, | ||||||
table_name: T::NAME, | ||||||
key: key.into(), | ||||||
} | ||||||
.into() | ||||||
}) | ||||||
}, | ||||||
) | ||||||
self.put::<T>(PutKind::Upsert, key, value) | ||||||
} | ||||||
|
||||||
fn append<T: Table>(&self, key: T::Key, value: T::Value) -> Result<(), DatabaseError> { | ||||||
self.put::<T>(PutKind::Append, key, value) | ||||||
} | ||||||
|
||||||
fn delete<T: Table>( | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -106,8 +106,10 @@ pub enum DatabaseWriteOperation { | |
CursorInsert, | ||
/// Append duplicate cursor. | ||
CursorAppendDup, | ||
/// Put. | ||
Put, | ||
/// Put upsert. | ||
PutUpsert, | ||
/// Put append. | ||
PutAppend, | ||
Comment on lines
+109
to
+112
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could we add another enum for PutKind then this can replace the bool and we can do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice! Too many |
||
} | ||
|
||
/// Database log level. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we add some docs here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done 🙏.