diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index 61b057cde7..ecf86c65f8 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -26,7 +26,7 @@ use { ancestor_iterator::AncestorIterator, bank_forks_utils, blockstore::{create_new_ledger, Blockstore, BlockstoreError, PurgeType}, - blockstore_db::{self, Database}, + blockstore_db::{self, columns as cf, Column, ColumnName, Database}, blockstore_options::{ AccessType, BlockstoreOptions, BlockstoreRecoveryMode, LedgerColumnOptions, ShredStorageType, @@ -900,6 +900,74 @@ fn open_blockstore( } } +fn raw_key_to_slot(key: &[u8], column_name: &str) -> Option { + match column_name { + cf::SlotMeta::NAME => Some(cf::SlotMeta::slot(cf::SlotMeta::index(key))), + cf::Orphans::NAME => Some(cf::Orphans::slot(cf::Orphans::index(key))), + cf::DeadSlots::NAME => Some(cf::SlotMeta::slot(cf::SlotMeta::index(key))), + cf::DuplicateSlots::NAME => Some(cf::SlotMeta::slot(cf::SlotMeta::index(key))), + cf::ErasureMeta::NAME => Some(cf::ErasureMeta::slot(cf::ErasureMeta::index(key))), + cf::BankHash::NAME => Some(cf::BankHash::slot(cf::BankHash::index(key))), + cf::Root::NAME => Some(cf::Root::slot(cf::Root::index(key))), + cf::Index::NAME => Some(cf::Index::slot(cf::Index::index(key))), + cf::ShredData::NAME => Some(cf::ShredData::slot(cf::ShredData::index(key))), + cf::ShredCode::NAME => Some(cf::ShredCode::slot(cf::ShredCode::index(key))), + cf::TransactionStatus::NAME => Some(cf::TransactionStatus::slot( + cf::TransactionStatus::index(key), + )), + cf::AddressSignatures::NAME => Some(cf::AddressSignatures::slot( + cf::AddressSignatures::index(key), + )), + cf::TransactionMemos::NAME => None, // does not implement slot() + cf::TransactionStatusIndex::NAME => None, // does not implement slot() + cf::Rewards::NAME => Some(cf::Rewards::slot(cf::Rewards::index(key))), + cf::Blocktime::NAME => Some(cf::Blocktime::slot(cf::Blocktime::index(key))), + cf::PerfSamples::NAME => Some(cf::PerfSamples::slot(cf::PerfSamples::index(key))), + cf::BlockHeight::NAME => Some(cf::BlockHeight::slot(cf::BlockHeight::index(key))), + cf::ProgramCosts::NAME => None, // does not implement slot() + cf::OptimisticSlots::NAME => { + Some(cf::OptimisticSlots::slot(cf::OptimisticSlots::index(key))) + } + &_ => None, + } +} + +fn print_blockstore_file_metadata( + blockstore: &Blockstore, + file_name: &Option<&str>, +) -> Result<(), String> { + let live_files = blockstore + .live_files_metadata() + .map_err(|err| format!("{:?}", err))?; + + // All files under live_files_metadata are prefixed with "/". + let sst_file_name = file_name.as_ref().map(|name| format!("/{}", name)); + for file in live_files { + if sst_file_name.is_none() || file.name.eq(sst_file_name.as_ref().unwrap()) { + println!( + "[{}] cf_name: {}, level: {}, start_slot: {:?}, end_slot: {:?}, size: {}, num_entries: {}", + file.name, + file.column_family_name, + file.level, + raw_key_to_slot(&file.start_key.unwrap(), &file.column_family_name), + raw_key_to_slot(&file.end_key.unwrap(), &file.column_family_name), + file.size, + file.num_entries, + ); + if sst_file_name.is_some() { + return Ok(()); + } + } + } + if sst_file_name.is_some() { + return Err(format!( + "Failed to find or load the metadata of the specified file {:?}", + file_name + )); + } + Ok(()) +} + // This function is duplicated in validator/src/main.rs... fn hardforks_of(matches: &ArgMatches<'_>, name: &str) -> Option> { if matches.is_present(name) { @@ -2107,6 +2175,19 @@ fn main() { .help("Slots that their blocks are computed for cost, default to all slots in ledger"), ) ) + .subcommand( + SubCommand::with_name("print-file-metadata") + .about("Print the metadata of the specified ledger-store file. \ + If no file name is specified, it will print the metadata of all ledger files.") + .arg( + Arg::with_name("file_name") + .long("file-name") + .takes_value(true) + .value_name("SST_FILE_NAME") + .help("The ledger file name (e.g. 011080.sst.) \ + If no file name is specified, it will print the metadata of all ledger files.") + ) + ) .get_matches(); info!("{} {}", crate_name!(), solana_version::version!()); @@ -4131,6 +4212,19 @@ fn main() { } } } + ("print-file-metadata", Some(arg_matches)) => { + let blockstore = open_blockstore( + &ledger_path, + AccessType::Secondary, + wal_recovery_mode, + &shred_storage_type, + false, + ); + let sst_file_name = arg_matches.value_of("file_name"); + if let Err(err) = print_blockstore_file_metadata(&blockstore, &sst_file_name) { + eprintln!("{}", err); + } + } ("", _) => { eprintln!("{}", matches.usage()); exit(1); diff --git a/ledger/src/blockstore.rs b/ledger/src/blockstore.rs index 0cc73b1411..3c44d60b51 100644 --- a/ledger/src/blockstore.rs +++ b/ledger/src/blockstore.rs @@ -30,7 +30,7 @@ use { iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}, ThreadPool, }, - rocksdb::DBRawIterator, + rocksdb::{DBRawIterator, LiveFile}, solana_entry::entry::{create_ticks, Entry}, solana_measure::measure::Measure, solana_metrics::{ @@ -500,6 +500,10 @@ impl Blockstore { root_forks.chain(orphans_iter.flat_map(move |orphan| NextSlotsIterator::new(orphan, self))) } + pub fn live_files_metadata(&self) -> Result> { + self.db.live_files_metadata() + } + pub fn slot_data_iterator( &self, slot: Slot, diff --git a/ledger/src/blockstore_db.rs b/ledger/src/blockstore_db.rs index 104d31e92e..32d6d7752a 100644 --- a/ledger/src/blockstore_db.rs +++ b/ledger/src/blockstore_db.rs @@ -21,7 +21,7 @@ use { compaction_filter_factory::{CompactionFilterContext, CompactionFilterFactory}, properties as RocksProperties, ColumnFamily, ColumnFamilyDescriptor, CompactionDecision, DBCompactionStyle, DBIterator, DBRawIterator, FifoCompactOptions, - IteratorMode as RocksIteratorMode, Options, WriteBatch as RWriteBatch, DB, + IteratorMode as RocksIteratorMode, LiveFile, Options, WriteBatch as RWriteBatch, DB, }, serde::{de::DeserializeOwned, Serialize}, solana_runtime::hardened_unpack::UnpackError, @@ -527,6 +527,13 @@ impl Rocks { Err(e) => Err(BlockstoreError::RocksDb(e)), } } + + fn live_files_metadata(&self) -> Result> { + match self.db.live_files() { + Ok(live_files) => Ok(live_files), + Err(e) => Err(BlockstoreError::RocksDb(e)), + } + } } pub trait Column { @@ -1155,6 +1162,10 @@ impl Database { pub fn set_oldest_slot(&self, oldest_slot: Slot) { self.backend.oldest_slot.set(oldest_slot); } + + pub fn live_files_metadata(&self) -> Result> { + self.backend.live_files_metadata() + } } impl LedgerColumn