ledger-tool: Add flag to find non-vote optimistic slots (#30580)

In cluster restart scenarios, it is desirable to know the latest
optimistic slot(s), which the subcommand latest-optimistic-slots will
return. However, it is also useful to know whether slots contain only
votes or if they contain votes and user transactions.

This PR adds an extra column of output to show whether an optimistically
confirmed slot is vote only (contains zero non-vote transactions).
Additionally, a flag has been added to enable filtering output to
exclude vote only slots.
This commit is contained in:
steviez 2023-03-24 05:13:41 +08:00 committed by GitHub
parent b14fcf5a12
commit 8db35eb7e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 53 additions and 15 deletions

View File

@ -2354,6 +2354,12 @@ fn main() {
.required(false)
.help("Number of slots in the output"),
)
.arg(
Arg::with_name("exclude_vote_only_slots")
.long("exclude-vote-only-slots")
.required(false)
.help("Exclude slots that contain only votes from output"),
)
)
.subcommand(
SubCommand::with_name("repair-roots")
@ -4230,11 +4236,37 @@ fn main() {
force_update_to_open,
);
let num_slots = value_t_or_exit!(arg_matches, "num_slots", usize);
let slots = blockstore
.get_latest_optimistic_slots(num_slots)
.expect("Failed to get latest optimistic slots");
println!("{:>20} {:>44} {:>32}", "Slot", "Hash", "Timestamp");
for (slot, hash, timestamp) in slots.iter() {
let exclude_vote_only_slots = arg_matches.is_present("exclude_vote_only_slots");
let slots_iter = blockstore
.reversed_optimistic_slots_iterator()
.expect("Failed to get reversed optimistic slots iterator")
.map(|(slot, hash, timestamp)| {
let (entries, _, _) = blockstore
.get_slot_entries_with_shred_info(slot, 0, false)
.expect("Failed to get slot entries");
let contains_nonvote = entries
.iter()
.flat_map(|entry| entry.transactions.iter())
.flat_map(get_program_ids)
.any(|program_id| *program_id != solana_vote_program::id());
(slot, hash, timestamp, contains_nonvote)
});
let slots: Vec<_> = if exclude_vote_only_slots {
slots_iter
.filter(|(_, _, _, contains_nonvote)| *contains_nonvote)
.take(num_slots)
.collect()
} else {
slots_iter.take(num_slots).collect()
};
println!(
"{:>20} {:>44} {:>32} {:>13}",
"Slot", "Hash", "Timestamp", "Vote Only?"
);
for (slot, hash, timestamp, contains_nonvote) in slots.iter() {
let time_str = {
let secs: u64 = (timestamp / 1_000) as u64;
let nanos: u32 = ((timestamp % 1_000) * 1_000_000) as u32;
@ -4243,7 +4275,10 @@ fn main() {
datetime.to_rfc3339()
};
let hash_str = format!("{hash}");
println!("{:>20} {:>44} {:>32}", slot, &hash_str, &time_str);
println!(
"{:>20} {:>44} {:>32} {:>13}",
slot, &hash_str, &time_str, !contains_nonvote
);
}
}
("repair-roots", Some(arg_matches)) => {

View File

@ -552,6 +552,16 @@ impl Blockstore {
self.prepare_rooted_slot_iterator(slot, IteratorDirection::Reverse)
}
pub fn reversed_optimistic_slots_iterator(
&self,
) -> Result<impl Iterator<Item = (Slot, Hash, UnixTimestamp)> + '_> {
let iter = self.db.iter::<cf::OptimisticSlots>(IteratorMode::End)?;
Ok(iter.map(|(slot, bytes)| {
let meta: OptimisticSlotMetaVersioned = deserialize(&bytes).unwrap();
(slot, meta.hash(), meta.timestamp())
}))
}
/// Determines if we can iterate from `starting_slot` to >= `ending_slot` by full slots
/// `starting_slot` is excluded from the `is_full()` check
pub fn slot_range_connected(&self, starting_slot: Slot, ending_slot: Slot) -> bool {
@ -3103,15 +3113,8 @@ impl Blockstore {
&self,
num: usize,
) -> Result<Vec<(Slot, Hash, UnixTimestamp)>> {
Ok(self
.db
.iter::<cf::OptimisticSlots>(IteratorMode::End)?
.take(num)
.map(|(slot, data)| {
let meta: OptimisticSlotMetaVersioned = deserialize(&data).unwrap();
(slot, meta.hash(), meta.timestamp())
})
.collect())
let iter = self.reversed_optimistic_slots_iterator()?;
Ok(iter.take(num).collect())
}
pub fn set_duplicate_confirmed_slots_and_hashes(