Add ledger tool command print-file-metadata (#26790)

Add ledger-tool command print-file-metadata

#### Summary of Changes
This PR adds a ledger tool subcommand print-file-metadata.
```
USAGE:
    solana-ledger-tool print-file-metadata [FLAGS] [OPTIONS] [SST_FILE_NAME]

    Prints the metadata of the specified ledger-store file.
    If no file name is unspecified, then it will print the metadata of all ledger files
```
This commit is contained in:
Yueh-Hsuan Chiang 2022-09-06 21:46:35 -07:00 committed by GitHub
parent 204f272412
commit ed00365101
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 112 additions and 3 deletions

View File

@ -26,7 +26,7 @@ use {
ancestor_iterator::AncestorIterator, ancestor_iterator::AncestorIterator,
bank_forks_utils, bank_forks_utils,
blockstore::{create_new_ledger, Blockstore, BlockstoreError, PurgeType}, blockstore::{create_new_ledger, Blockstore, BlockstoreError, PurgeType},
blockstore_db::{self, Database}, blockstore_db::{self, columns as cf, Column, ColumnName, Database},
blockstore_options::{ blockstore_options::{
AccessType, BlockstoreOptions, BlockstoreRecoveryMode, LedgerColumnOptions, AccessType, BlockstoreOptions, BlockstoreRecoveryMode, LedgerColumnOptions,
ShredStorageType, ShredStorageType,
@ -900,6 +900,74 @@ fn open_blockstore(
} }
} }
fn raw_key_to_slot(key: &[u8], column_name: &str) -> Option<Slot> {
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... // This function is duplicated in validator/src/main.rs...
fn hardforks_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<Slot>> { fn hardforks_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<Slot>> {
if matches.is_present(name) { 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"), .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(); .get_matches();
info!("{} {}", crate_name!(), solana_version::version!()); 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()); eprintln!("{}", matches.usage());
exit(1); exit(1);

View File

@ -30,7 +30,7 @@ use {
iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}, iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator},
ThreadPool, ThreadPool,
}, },
rocksdb::DBRawIterator, rocksdb::{DBRawIterator, LiveFile},
solana_entry::entry::{create_ticks, Entry}, solana_entry::entry::{create_ticks, Entry},
solana_measure::measure::Measure, solana_measure::measure::Measure,
solana_metrics::{ solana_metrics::{
@ -500,6 +500,10 @@ impl Blockstore {
root_forks.chain(orphans_iter.flat_map(move |orphan| NextSlotsIterator::new(orphan, self))) root_forks.chain(orphans_iter.flat_map(move |orphan| NextSlotsIterator::new(orphan, self)))
} }
pub fn live_files_metadata(&self) -> Result<Vec<LiveFile>> {
self.db.live_files_metadata()
}
pub fn slot_data_iterator( pub fn slot_data_iterator(
&self, &self,
slot: Slot, slot: Slot,

View File

@ -21,7 +21,7 @@ use {
compaction_filter_factory::{CompactionFilterContext, CompactionFilterFactory}, compaction_filter_factory::{CompactionFilterContext, CompactionFilterFactory},
properties as RocksProperties, ColumnFamily, ColumnFamilyDescriptor, CompactionDecision, properties as RocksProperties, ColumnFamily, ColumnFamilyDescriptor, CompactionDecision,
DBCompactionStyle, DBIterator, DBRawIterator, FifoCompactOptions, 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}, serde::{de::DeserializeOwned, Serialize},
solana_runtime::hardened_unpack::UnpackError, solana_runtime::hardened_unpack::UnpackError,
@ -527,6 +527,13 @@ impl Rocks {
Err(e) => Err(BlockstoreError::RocksDb(e)), Err(e) => Err(BlockstoreError::RocksDb(e)),
} }
} }
fn live_files_metadata(&self) -> Result<Vec<LiveFile>> {
match self.db.live_files() {
Ok(live_files) => Ok(live_files),
Err(e) => Err(BlockstoreError::RocksDb(e)),
}
}
} }
pub trait Column { pub trait Column {
@ -1155,6 +1162,10 @@ impl Database {
pub fn set_oldest_slot(&self, oldest_slot: Slot) { pub fn set_oldest_slot(&self, oldest_slot: Slot) {
self.backend.oldest_slot.set(oldest_slot); self.backend.oldest_slot.set(oldest_slot);
} }
pub fn live_files_metadata(&self) -> Result<Vec<LiveFile>> {
self.backend.live_files_metadata()
}
} }
impl<C> LedgerColumn<C> impl<C> LedgerColumn<C>