Add output in JSON for `solana-ledger-tool bounds` subcommand (#28410)
Introduce a struct to store all of the relevant slot/root information, and then output all in one go at the end as either human-readable or json. There are some slight changes to the human-readable format for the case of an empty ledger
This commit is contained in:
parent
59fde130d6
commit
5eab3fb314
|
@ -1,6 +1,6 @@
|
|||
#![allow(clippy::integer_arithmetic)]
|
||||
use {
|
||||
crate::{bigtable::*, ledger_path::*},
|
||||
crate::{bigtable::*, ledger_path::*, output::*},
|
||||
chrono::{DateTime, Utc},
|
||||
clap::{
|
||||
crate_description, crate_name, value_t, value_t_or_exit, values_t_or_exit, App,
|
||||
|
@ -106,6 +106,7 @@ use {
|
|||
|
||||
mod bigtable;
|
||||
mod ledger_path;
|
||||
mod output;
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
enum LedgerOutputMethod {
|
||||
|
@ -1738,8 +1739,10 @@ fn main() {
|
|||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("bounds")
|
||||
.about("Print lowest and highest non-empty slots. \
|
||||
Note that there may be empty slots within the bounds")
|
||||
.about(
|
||||
"Print lowest and highest non-empty slots. \
|
||||
Note that there may be empty slots within the bounds",
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("all")
|
||||
.long("all")
|
||||
|
@ -1747,7 +1750,8 @@ fn main() {
|
|||
.required(false)
|
||||
.help("Additionally print all the non-empty slots within the bounds"),
|
||||
)
|
||||
).subcommand(
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("json")
|
||||
.about("Print the ledger in JSON format")
|
||||
.arg(&starting_slot_arg)
|
||||
|
@ -4170,56 +4174,63 @@ fn main() {
|
|||
&shred_storage_type,
|
||||
force_update_to_open,
|
||||
);
|
||||
|
||||
match blockstore.slot_meta_iterator(0) {
|
||||
Ok(metas) => {
|
||||
let output_format =
|
||||
OutputFormat::from_matches(arg_matches, "output_format", false);
|
||||
let all = arg_matches.is_present("all");
|
||||
|
||||
let slots: Vec<_> = metas.map(|(slot, _)| slot).collect();
|
||||
if slots.is_empty() {
|
||||
println!("Ledger is empty");
|
||||
|
||||
let slot_bounds = if slots.is_empty() {
|
||||
SlotBounds::default()
|
||||
} else {
|
||||
let first = slots.first().unwrap();
|
||||
let last = slots.last().unwrap_or(first);
|
||||
if first != last {
|
||||
println!(
|
||||
"Ledger has data for {} slots {:?} to {:?}",
|
||||
slots.len(),
|
||||
first,
|
||||
last
|
||||
);
|
||||
// Collect info about slot bounds
|
||||
let mut bounds = SlotBounds {
|
||||
slots: SlotInfo {
|
||||
total: slots.len(),
|
||||
first: Some(*slots.first().unwrap()),
|
||||
last: Some(*slots.last().unwrap()),
|
||||
..SlotInfo::default()
|
||||
},
|
||||
..SlotBounds::default()
|
||||
};
|
||||
if all {
|
||||
println!("Non-empty slots: {slots:?}");
|
||||
}
|
||||
} else {
|
||||
println!("Ledger has data for slot {first:?}");
|
||||
}
|
||||
bounds.all_slots = Some(&slots);
|
||||
}
|
||||
|
||||
// Consider also rooted slots, if present
|
||||
if let Ok(rooted) = blockstore.rooted_slot_iterator(0) {
|
||||
let mut first_rooted = 0;
|
||||
let mut last_rooted = 0;
|
||||
let mut first_rooted = None;
|
||||
let mut last_rooted = None;
|
||||
let mut total_rooted = 0;
|
||||
for (i, slot) in rooted.into_iter().enumerate() {
|
||||
if i == 0 {
|
||||
first_rooted = slot;
|
||||
first_rooted = Some(slot);
|
||||
}
|
||||
last_rooted = slot;
|
||||
last_rooted = Some(slot);
|
||||
total_rooted += 1;
|
||||
}
|
||||
let mut count_past_root = 0;
|
||||
for slot in slots.iter().rev() {
|
||||
if *slot > last_rooted {
|
||||
count_past_root += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
println!(
|
||||
" with {total_rooted} rooted slots from {first_rooted:?} to {last_rooted:?}"
|
||||
);
|
||||
println!(" and {count_past_root} slots past the last root");
|
||||
} else {
|
||||
println!(" with no rooted slots");
|
||||
let last_root_for_comparison = last_rooted.unwrap_or_default();
|
||||
let count_past_root = slots
|
||||
.iter()
|
||||
.rev()
|
||||
.take_while(|slot| *slot > &last_root_for_comparison)
|
||||
.count();
|
||||
|
||||
bounds.roots = SlotInfo {
|
||||
total: total_rooted,
|
||||
first: first_rooted,
|
||||
last: last_rooted,
|
||||
num_after_last_root: Some(count_past_root),
|
||||
};
|
||||
}
|
||||
bounds
|
||||
};
|
||||
|
||||
// Print collected data
|
||||
println!("{}", output_format.formatted_string(&slot_bounds));
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("Unable to read the Ledger: {err:?}");
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
use {
|
||||
serde::Serialize,
|
||||
solana_cli_output::{QuietDisplay, VerboseDisplay},
|
||||
std::fmt::{Display, Formatter, Result},
|
||||
};
|
||||
|
||||
#[derive(Serialize, Debug, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SlotInfo {
|
||||
pub total: usize,
|
||||
pub first: Option<u64>,
|
||||
pub last: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub num_after_last_root: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SlotBounds<'a> {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub all_slots: Option<&'a Vec<u64>>,
|
||||
pub slots: SlotInfo,
|
||||
pub roots: SlotInfo,
|
||||
}
|
||||
|
||||
impl VerboseDisplay for SlotBounds<'_> {}
|
||||
impl QuietDisplay for SlotBounds<'_> {}
|
||||
|
||||
impl Display for SlotBounds<'_> {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
if self.slots.total > 0 {
|
||||
let first = self.slots.first.unwrap();
|
||||
let last = self.slots.last.unwrap();
|
||||
|
||||
if first != last {
|
||||
writeln!(
|
||||
f,
|
||||
"Ledger has data for {:?} slots {:?} to {:?}",
|
||||
self.slots.total, first, last
|
||||
)?;
|
||||
|
||||
if let Some(all_slots) = self.all_slots {
|
||||
writeln!(f, "Non-empty slots: {:?}", all_slots)?;
|
||||
}
|
||||
} else {
|
||||
writeln!(f, "Ledger has data for slot {:?}", first)?;
|
||||
}
|
||||
|
||||
if self.roots.total > 0 {
|
||||
let first_rooted = self.roots.first.unwrap_or_default();
|
||||
let last_rooted = self.roots.last.unwrap_or_default();
|
||||
let num_after_last_root = self.roots.num_after_last_root.unwrap_or_default();
|
||||
writeln!(
|
||||
f,
|
||||
" with {:?} rooted slots from {:?} to {:?}",
|
||||
self.roots.total, first_rooted, last_rooted
|
||||
)?;
|
||||
|
||||
writeln!(
|
||||
f,
|
||||
" and {:?} slots past the last root",
|
||||
num_after_last_root
|
||||
)?;
|
||||
} else {
|
||||
writeln!(f, " with no rooted slots")?;
|
||||
}
|
||||
} else {
|
||||
writeln!(f, "Ledger is empty")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue