Skip to content

Commit 71c1247

Browse files
wetkeyboardjoshieDoshekhirin
authored
perf(cli): optimize StorageChangeSets import in merkle stage dump (#18022)
Co-authored-by: joshieDo <93316087+joshieDo@users.noreply.github.com> Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
1 parent dff382b commit 71c1247

File tree

3 files changed

+119
-5
lines changed

3 files changed

+119
-5
lines changed

crates/cli/commands/src/stage/dump/merkle.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use std::sync::Arc;
22

33
use super::setup;
4-
use alloy_primitives::BlockNumber;
4+
use alloy_primitives::{Address, BlockNumber};
55
use eyre::Result;
66
use reth_config::config::EtlConfig;
77
use reth_consensus::{ConsensusError, FullConsensus};
88
use reth_db::DatabaseEnv;
9-
use reth_db_api::{database::Database, table::TableImporter, tables};
9+
use reth_db_api::{database::Database, models::BlockNumberAddress, table::TableImporter, tables};
1010
use reth_db_common::DbTool;
1111
use reth_evm::ConfigureEvm;
1212
use reth_exex::ExExManagerHandle;
@@ -135,9 +135,13 @@ fn unwind_and_copy<N: ProviderNodeTypes>(
135135

136136
let unwind_inner_tx = provider.into_tx();
137137

138-
// TODO optimize we can actually just get the entries we need
139-
output_db
140-
.update(|tx| tx.import_dupsort::<tables::StorageChangeSets, _>(&unwind_inner_tx))??;
138+
output_db.update(|tx| {
139+
tx.import_table_with_range::<tables::StorageChangeSets, _>(
140+
&unwind_inner_tx,
141+
Some(BlockNumberAddress((from, Address::ZERO))),
142+
BlockNumberAddress((to, Address::repeat_byte(0xff))),
143+
)
144+
})??;
141145

142146
output_db.update(|tx| tx.import_table::<tables::HashedAccounts, _>(&unwind_inner_tx))??;
143147
output_db.update(|tx| tx.import_dupsort::<tables::HashedStorages, _>(&unwind_inner_tx))??;

crates/storage/db-api/src/table.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ pub trait TableImporter: DbTxMut {
139139
}
140140

141141
/// Imports table data from another transaction within a range.
142+
///
143+
/// This method works correctly with both regular and `DupSort` tables. For `DupSort` tables,
144+
/// all duplicate entries within the range are preserved during import.
142145
fn import_table_with_range<T: Table, R: DbTx>(
143146
&self,
144147
source_tx: &R,

crates/storage/db/src/implementation/mdbx/cursor.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,3 +345,110 @@ impl<T: DupSort> DbDupCursorRW<T> for Cursor<RW, T> {
345345
)
346346
}
347347
}
348+
349+
#[cfg(test)]
350+
mod tests {
351+
use crate::{
352+
mdbx::{DatabaseArguments, DatabaseEnv, DatabaseEnvKind},
353+
tables::StorageChangeSets,
354+
Database,
355+
};
356+
use alloy_primitives::{address, Address, B256, U256};
357+
use reth_db_api::{
358+
cursor::{DbCursorRO, DbDupCursorRW},
359+
models::{BlockNumberAddress, ClientVersion},
360+
table::TableImporter,
361+
transaction::{DbTx, DbTxMut},
362+
};
363+
use reth_primitives_traits::StorageEntry;
364+
use std::sync::Arc;
365+
use tempfile::TempDir;
366+
367+
fn create_test_db() -> Arc<DatabaseEnv> {
368+
let path = TempDir::new().unwrap();
369+
let mut db = DatabaseEnv::open(
370+
path.path(),
371+
DatabaseEnvKind::RW,
372+
DatabaseArguments::new(ClientVersion::default()),
373+
)
374+
.unwrap();
375+
db.create_tables().unwrap();
376+
Arc::new(db)
377+
}
378+
379+
#[test]
380+
fn test_import_table_with_range_works_on_dupsort() {
381+
let addr1 = address!("0000000000000000000000000000000000000001");
382+
let addr2 = address!("0000000000000000000000000000000000000002");
383+
let addr3 = address!("0000000000000000000000000000000000000003");
384+
let source_db = create_test_db();
385+
let target_db = create_test_db();
386+
let test_data = vec![
387+
(
388+
BlockNumberAddress((100, addr1)),
389+
StorageEntry { key: B256::with_last_byte(1), value: U256::from(100) },
390+
),
391+
(
392+
BlockNumberAddress((100, addr1)),
393+
StorageEntry { key: B256::with_last_byte(2), value: U256::from(200) },
394+
),
395+
(
396+
BlockNumberAddress((100, addr1)),
397+
StorageEntry { key: B256::with_last_byte(3), value: U256::from(300) },
398+
),
399+
(
400+
BlockNumberAddress((101, addr1)),
401+
StorageEntry { key: B256::with_last_byte(1), value: U256::from(400) },
402+
),
403+
(
404+
BlockNumberAddress((101, addr2)),
405+
StorageEntry { key: B256::with_last_byte(1), value: U256::from(500) },
406+
),
407+
(
408+
BlockNumberAddress((101, addr2)),
409+
StorageEntry { key: B256::with_last_byte(2), value: U256::from(600) },
410+
),
411+
(
412+
BlockNumberAddress((102, addr3)),
413+
StorageEntry { key: B256::with_last_byte(1), value: U256::from(700) },
414+
),
415+
];
416+
417+
// setup data
418+
let tx = source_db.tx_mut().unwrap();
419+
{
420+
let mut cursor = tx.cursor_dup_write::<StorageChangeSets>().unwrap();
421+
for (key, value) in &test_data {
422+
cursor.append_dup(*key, *value).unwrap();
423+
}
424+
}
425+
tx.commit().unwrap();
426+
427+
// import data from source db to target
428+
let source_tx = source_db.tx().unwrap();
429+
let target_tx = target_db.tx_mut().unwrap();
430+
431+
target_tx
432+
.import_table_with_range::<StorageChangeSets, _>(
433+
&source_tx,
434+
Some(BlockNumberAddress((100, Address::ZERO))),
435+
BlockNumberAddress((102, Address::repeat_byte(0xff))),
436+
)
437+
.unwrap();
438+
target_tx.commit().unwrap();
439+
440+
// fetch all data from target db
441+
let verify_tx = target_db.tx().unwrap();
442+
let mut cursor = verify_tx.cursor_dup_read::<StorageChangeSets>().unwrap();
443+
let copied: Vec<_> = cursor.walk(None).unwrap().collect::<Result<Vec<_>, _>>().unwrap();
444+
445+
// verify each entry matches the test data
446+
assert_eq!(copied.len(), test_data.len(), "Should copy all entries including duplicates");
447+
for ((copied_key, copied_value), (expected_key, expected_value)) in
448+
copied.iter().zip(test_data.iter())
449+
{
450+
assert_eq!(copied_key, expected_key);
451+
assert_eq!(copied_value, expected_value);
452+
}
453+
}
454+
}

0 commit comments

Comments
 (0)