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,
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<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...
fn hardforks_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<Slot>> {
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);

View File

@ -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<Vec<LiveFile>> {
self.db.live_files_metadata()
}
pub fn slot_data_iterator(
&self,
slot: Slot,

View File

@ -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<Vec<LiveFile>> {
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<Vec<LiveFile>> {
self.backend.live_files_metadata()
}
}
impl<C> LedgerColumn<C>