-
Notifications
You must be signed in to change notification settings - Fork 201
perf(l1): two-CF receipts migration #6598
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
base: main
Are you sure you want to change the base?
Changes from 16 commits
93e7662
69e0acc
049d3e6
61d09f3
e020e3f
835b6f2
ee8e0ff
0990157
e5a0586
58b4c83
9cc728b
cd35dd4
33d253d
d195064
733a7c9
f11d0a6
6287873
04eae69
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,11 +30,16 @@ pub const ACCOUNT_CODES: &str = "account_codes"; | |
| /// - [`u8; 8`] = `code_length.to_be_bytes()` | ||
| pub const ACCOUNT_CODE_METADATA: &str = "account_code_metadata"; | ||
|
|
||
| /// Receipts column family: [`Vec<u8>`] => [`Vec<u8>`] | ||
| /// - [`Vec<u8>`] = `(block_hash, index).encode_to_vec()` | ||
| /// - [`Vec<u8>`] = `receipt.encode_to_vec()` | ||
| /// Receipts column family (legacy, pre-v2): [`Vec<u8>`] => [`Vec<u8>`] | ||
| /// Kept only for migration reads; dropped automatically on next startup. | ||
| pub const RECEIPTS: &str = "receipts"; | ||
|
|
||
| /// Receipts v2 column family: [`Vec<u8>`] => [`Vec<u8>`] | ||
| /// - Key: `block_hash (32B) || index (8B big-endian u64)` — fixed-width raw key | ||
| /// enabling cursor-based prefix iteration by block hash. | ||
| /// - Value: `receipt.encode_to_vec()` | ||
| pub const RECEIPTS_V2: &str = "receipts_v2"; | ||
|
|
||
| /// Transaction locations column family: [`Vec<u8>`] => [`Vec<u8>`] | ||
| /// - [`Vec<u8>`] = Composite key | ||
| /// ```rust,no_run | ||
|
|
@@ -102,7 +107,7 @@ pub const MISC_VALUES: &str = "misc_values"; | |
| /// - [`Vec<u8>`] = `serde_json::to_vec(&witness)` | ||
| pub const EXECUTION_WITNESSES: &str = "execution_witnesses"; | ||
|
|
||
| pub const TABLES: [&str; 19] = [ | ||
| pub const TABLES: [&str; 20] = [ | ||
| CHAIN_DATA, | ||
| ACCOUNT_CODES, | ||
| ACCOUNT_CODE_METADATA, | ||
|
|
@@ -113,6 +118,7 @@ pub const TABLES: [&str; 19] = [ | |
| PENDING_BLOCKS, | ||
| TRANSACTION_LOCATIONS, | ||
| RECEIPTS, | ||
| RECEIPTS_V2, | ||
|
Contributor
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. The old
But for cf_name in &existing_cfs {
if cf_name != "default" && !TABLES.contains(&cf_name.as_str()) {
warn!("Dropping obsolete column family: {}", cf_name);
let _ = db.drop_cf(cf_name) ...
}
}So with both The test at Fix: remove Worth a quick test of this scenario before merge — e.g., a unit test that runs migrate + reopens the backend + asserts |
||
| SNAP_STATE, | ||
| INVALID_CHAINS, | ||
| ACCOUNT_TRIE_NODES, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| /// Standalone binary to benchmark the v1→v2 RECEIPTS migration. | ||
| /// | ||
| /// Usage: bench_migration <db_path> | ||
| /// | ||
| /// Opens the RocksDB database, runs the two-CF migration (receipts → receipts_v2), | ||
| /// and reports wall-clock time and peak RSS. | ||
| /// | ||
| /// Prerequisites: | ||
| /// 1. Run `seed_migration_test <db_path>` to seed 150M old-format entries | ||
| /// 2. Ensure metadata.json has {"schema_version": 1} (or just don't create one) | ||
| /// 3. Run this binary | ||
| use std::time::Instant; | ||
|
|
||
| fn get_rss_mb() -> Option<f64> { | ||
| #[cfg(target_os = "macos")] | ||
| { | ||
| use std::mem; | ||
| let mut info: libc::rusage = unsafe { mem::zeroed() }; | ||
| let ret = unsafe { libc::getrusage(libc::RUSAGE_SELF, &mut info) }; | ||
| if ret == 0 { | ||
| // macOS reports maxrss in bytes | ||
| Some(info.ru_maxrss as f64 / (1024.0 * 1024.0)) | ||
| } else { | ||
| None | ||
| } | ||
| } | ||
| #[cfg(target_os = "linux")] | ||
| { | ||
| use std::mem; | ||
| let mut info: libc::rusage = unsafe { mem::zeroed() }; | ||
| let ret = unsafe { libc::getrusage(libc::RUSAGE_SELF, &mut info) }; | ||
| if ret == 0 { | ||
| // Linux reports maxrss in kilobytes | ||
| Some(info.ru_maxrss as f64 / 1024.0) | ||
| } else { | ||
| None | ||
| } | ||
| } | ||
| #[cfg(not(any(target_os = "macos", target_os = "linux")))] | ||
| { | ||
| None | ||
| } | ||
| } | ||
|
|
||
| fn main() { | ||
| // Initialize tracing so migration progress logs are visible | ||
| tracing_subscriber::fmt() | ||
| .with_target(false) | ||
| .with_env_filter( | ||
| tracing_subscriber::EnvFilter::try_from_default_env() | ||
| .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")), | ||
| ) | ||
| .init(); | ||
|
|
||
| let args: Vec<String> = std::env::args().collect(); | ||
| if args.len() != 2 { | ||
| eprintln!("Usage: {} <db_path>", args[0]); | ||
| std::process::exit(1); | ||
| } | ||
| let db_path = &args[1]; | ||
|
|
||
| println!("Opening database at: {db_path}"); | ||
| let rss_before = get_rss_mb(); | ||
|
|
||
| let backend = ethrex_storage::backend::rocksdb::RocksDBBackend::open(db_path) | ||
| .expect("Failed to open RocksDB"); | ||
|
|
||
| let rss_after_open = get_rss_mb(); | ||
| println!( | ||
| "Database opened. RSS after open: {:.1} MB", | ||
| rss_after_open.unwrap_or(0.0) | ||
| ); | ||
|
|
||
| // Run the migration | ||
| println!("Starting migration v1→v2 (two-CF: receipts → receipts_v2)..."); | ||
| let start = Instant::now(); | ||
|
|
||
| ethrex_storage::migrations::run_pending_migrations( | ||
| &backend, | ||
| std::path::Path::new(db_path), | ||
| 1, // pretend we're at v1 | ||
| ) | ||
| .expect("Migration failed"); | ||
|
|
||
| let elapsed = start.elapsed(); | ||
| let rss_after = get_rss_mb(); | ||
|
|
||
| println!("\n=== Migration Benchmark Results ==="); | ||
| println!("Wall-clock time: {:.1}s", elapsed.as_secs_f64()); | ||
| if let Some(before) = rss_before { | ||
| println!("RSS before open: {:.1} MB", before); | ||
| } | ||
| if let Some(after_open) = rss_after_open { | ||
| println!("RSS after open: {:.1} MB", after_open); | ||
| } | ||
| if let Some(after) = rss_after { | ||
| println!("Peak RSS (maxrss): {:.1} MB", after); | ||
| } | ||
| println!("==================================="); | ||
| } |
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.
receipts.last()is correct only as long asfetch_count == index + 1and the count check above enforces that exact length. Usingreceipts.get(index as usize)is an explicit, index-stable access that doesn't silently return the wrong receipt if the count logic is ever adjusted.Prompt To Fix With AI