prioritize slot repairs for unknown last index and close to completion (#21070)
This commit is contained in:
parent
ebea3297f9
commit
2ed7e3af89
|
@ -34,6 +34,7 @@ pub mod outstanding_requests;
|
||||||
pub mod packet_hasher;
|
pub mod packet_hasher;
|
||||||
pub mod progress_map;
|
pub mod progress_map;
|
||||||
pub mod qos_service;
|
pub mod qos_service;
|
||||||
|
pub mod repair_generic_traversal;
|
||||||
pub mod repair_response;
|
pub mod repair_response;
|
||||||
pub mod repair_service;
|
pub mod repair_service;
|
||||||
pub mod repair_weight;
|
pub mod repair_weight;
|
||||||
|
|
|
@ -0,0 +1,312 @@
|
||||||
|
use crate::{
|
||||||
|
heaviest_subtree_fork_choice::HeaviestSubtreeForkChoice, repair_service::RepairService,
|
||||||
|
serve_repair::ShredRepairType, tree_diff::TreeDiff,
|
||||||
|
};
|
||||||
|
use solana_ledger::{blockstore::Blockstore, blockstore_meta::SlotMeta};
|
||||||
|
use solana_sdk::{clock::Slot, hash::Hash};
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
|
struct GenericTraversal<'a> {
|
||||||
|
tree: &'a HeaviestSubtreeForkChoice,
|
||||||
|
pending: Vec<Slot>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> GenericTraversal<'a> {
|
||||||
|
pub fn new(tree: &'a HeaviestSubtreeForkChoice) -> Self {
|
||||||
|
Self {
|
||||||
|
tree,
|
||||||
|
pending: vec![tree.root().0],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for GenericTraversal<'a> {
|
||||||
|
type Item = Slot;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let next = self.pending.pop();
|
||||||
|
if let Some(slot) = next {
|
||||||
|
let children: Vec<_> = self
|
||||||
|
.tree
|
||||||
|
.children(&(slot, Hash::default()))
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|(child_slot, _)| *child_slot)
|
||||||
|
.collect();
|
||||||
|
self.pending.extend(children);
|
||||||
|
}
|
||||||
|
next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_unknown_last_index(
|
||||||
|
tree: &HeaviestSubtreeForkChoice,
|
||||||
|
blockstore: &Blockstore,
|
||||||
|
slot_meta_cache: &mut HashMap<Slot, Option<SlotMeta>>,
|
||||||
|
processed_slots: &mut HashSet<Slot>,
|
||||||
|
limit: usize,
|
||||||
|
) -> Vec<ShredRepairType> {
|
||||||
|
let iter = GenericTraversal::new(tree);
|
||||||
|
let mut unknown_last = Vec::new();
|
||||||
|
for slot in iter {
|
||||||
|
if processed_slots.contains(&slot) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let slot_meta = slot_meta_cache
|
||||||
|
.entry(slot)
|
||||||
|
.or_insert_with(|| blockstore.meta(slot).unwrap());
|
||||||
|
if let Some(slot_meta) = slot_meta {
|
||||||
|
if slot_meta.known_last_index().is_none() {
|
||||||
|
let shred_index = blockstore.get_index(slot).unwrap();
|
||||||
|
let num_processed_shreds = if let Some(shred_index) = shred_index {
|
||||||
|
shred_index.data().num_shreds() as u64
|
||||||
|
} else {
|
||||||
|
slot_meta.consumed
|
||||||
|
};
|
||||||
|
unknown_last.push((slot, slot_meta.received, num_processed_shreds));
|
||||||
|
processed_slots.insert(slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// prioritize slots with more received shreds
|
||||||
|
unknown_last.sort_by(|(_, _, count1), (_, _, count2)| count2.cmp(count1));
|
||||||
|
unknown_last
|
||||||
|
.iter()
|
||||||
|
.take(limit)
|
||||||
|
.map(|(slot, received, _)| ShredRepairType::HighestShred(*slot, *received))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_unrepaired_path(
|
||||||
|
start_slot: Slot,
|
||||||
|
blockstore: &Blockstore,
|
||||||
|
slot_meta_cache: &mut HashMap<Slot, Option<SlotMeta>>,
|
||||||
|
visited: &mut HashSet<Slot>,
|
||||||
|
) -> Vec<Slot> {
|
||||||
|
let mut path = Vec::new();
|
||||||
|
let mut slot = start_slot;
|
||||||
|
while !visited.contains(&slot) {
|
||||||
|
visited.insert(slot);
|
||||||
|
let slot_meta = slot_meta_cache
|
||||||
|
.entry(slot)
|
||||||
|
.or_insert_with(|| blockstore.meta(slot).unwrap());
|
||||||
|
if let Some(slot_meta) = slot_meta {
|
||||||
|
if slot_meta.is_full() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
path.push(slot);
|
||||||
|
slot = slot_meta.parent_slot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
path.reverse();
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_closest_completion(
|
||||||
|
tree: &HeaviestSubtreeForkChoice,
|
||||||
|
blockstore: &Blockstore,
|
||||||
|
slot_meta_cache: &mut HashMap<Slot, Option<SlotMeta>>,
|
||||||
|
processed_slots: &mut HashSet<Slot>,
|
||||||
|
limit: usize,
|
||||||
|
) -> Vec<ShredRepairType> {
|
||||||
|
let mut v: Vec<(Slot, u64)> = Vec::default();
|
||||||
|
let iter = GenericTraversal::new(tree);
|
||||||
|
for slot in iter {
|
||||||
|
if processed_slots.contains(&slot) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let slot_meta = slot_meta_cache
|
||||||
|
.entry(slot)
|
||||||
|
.or_insert_with(|| blockstore.meta(slot).unwrap());
|
||||||
|
if let Some(slot_meta) = slot_meta {
|
||||||
|
if slot_meta.is_full() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Some(last_index) = slot_meta.known_last_index() {
|
||||||
|
let shred_index = blockstore.get_index(slot).unwrap();
|
||||||
|
let dist = if let Some(shred_index) = shred_index {
|
||||||
|
let shred_count = shred_index.data().num_shreds() as u64;
|
||||||
|
last_index - shred_count
|
||||||
|
} else {
|
||||||
|
last_index - slot_meta.consumed
|
||||||
|
};
|
||||||
|
v.push((slot, dist));
|
||||||
|
processed_slots.insert(slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v.sort_by(|(_, d1), (_, d2)| d1.cmp(d2));
|
||||||
|
|
||||||
|
let mut visited = HashSet::new();
|
||||||
|
let mut repairs = Vec::new();
|
||||||
|
for (slot, _) in v {
|
||||||
|
if repairs.len() >= limit {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// attempt to repair heaviest slots starting with their parents
|
||||||
|
let path = get_unrepaired_path(slot, blockstore, slot_meta_cache, &mut visited);
|
||||||
|
for slot in path {
|
||||||
|
if repairs.len() >= limit {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let slot_meta = slot_meta_cache.get(&slot).unwrap().as_ref().unwrap();
|
||||||
|
let new_repairs = RepairService::generate_repairs_for_slot(
|
||||||
|
blockstore,
|
||||||
|
slot,
|
||||||
|
slot_meta,
|
||||||
|
limit - repairs.len(),
|
||||||
|
);
|
||||||
|
repairs.extend(new_repairs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repairs
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod test {
|
||||||
|
use super::*;
|
||||||
|
use solana_ledger::{
|
||||||
|
blockstore::{Blockstore, MAX_TURBINE_PROPAGATION_IN_MS},
|
||||||
|
get_tmp_ledger_path,
|
||||||
|
};
|
||||||
|
use solana_sdk::hash::Hash;
|
||||||
|
use std::{thread::sleep, time::Duration};
|
||||||
|
use trees::{tr, Tree, TreeWalk};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_unknown_last_index() {
|
||||||
|
let (blockstore, heaviest_subtree_fork_choice) = setup_forks();
|
||||||
|
let last_shred = blockstore.meta(0).unwrap().unwrap().received;
|
||||||
|
let mut slot_meta_cache = HashMap::default();
|
||||||
|
let mut processed_slots = HashSet::default();
|
||||||
|
let repairs = get_unknown_last_index(
|
||||||
|
&heaviest_subtree_fork_choice,
|
||||||
|
&blockstore,
|
||||||
|
&mut slot_meta_cache,
|
||||||
|
&mut processed_slots,
|
||||||
|
10,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
repairs,
|
||||||
|
[0, 1, 3, 5, 2, 4]
|
||||||
|
.iter()
|
||||||
|
.map(|slot| ShredRepairType::HighestShred(*slot, last_shred))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_closest_completion() {
|
||||||
|
let (blockstore, heaviest_subtree_fork_choice) = setup_forks();
|
||||||
|
let mut slot_meta_cache = HashMap::default();
|
||||||
|
let mut processed_slots = HashSet::default();
|
||||||
|
let repairs = get_closest_completion(
|
||||||
|
&heaviest_subtree_fork_choice,
|
||||||
|
&blockstore,
|
||||||
|
&mut slot_meta_cache,
|
||||||
|
&mut processed_slots,
|
||||||
|
10,
|
||||||
|
);
|
||||||
|
assert_eq!(repairs, []);
|
||||||
|
|
||||||
|
let forks = tr(0) / (tr(1) / (tr(2) / (tr(4))) / (tr(3) / (tr(5))));
|
||||||
|
let ledger_path = get_tmp_ledger_path!();
|
||||||
|
let blockstore = Blockstore::open(&ledger_path).unwrap();
|
||||||
|
add_tree_with_missing_shreds(
|
||||||
|
&blockstore,
|
||||||
|
forks.clone(),
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
100,
|
||||||
|
Hash::default(),
|
||||||
|
);
|
||||||
|
let heaviest_subtree_fork_choice = HeaviestSubtreeForkChoice::new_from_tree(forks);
|
||||||
|
sleep(Duration::from_millis(MAX_TURBINE_PROPAGATION_IN_MS));
|
||||||
|
let mut slot_meta_cache = HashMap::default();
|
||||||
|
let mut processed_slots = HashSet::default();
|
||||||
|
let repairs = get_closest_completion(
|
||||||
|
&heaviest_subtree_fork_choice,
|
||||||
|
&blockstore,
|
||||||
|
&mut slot_meta_cache,
|
||||||
|
&mut processed_slots,
|
||||||
|
2,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
repairs,
|
||||||
|
[ShredRepairType::Shred(0, 3), ShredRepairType::Shred(1, 3)]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_tree_with_missing_shreds(
|
||||||
|
blockstore: &Blockstore,
|
||||||
|
forks: Tree<Slot>,
|
||||||
|
is_orphan: bool,
|
||||||
|
is_slot_complete: bool,
|
||||||
|
num_ticks: u64,
|
||||||
|
starting_hash: Hash,
|
||||||
|
) {
|
||||||
|
let mut walk = TreeWalk::from(forks);
|
||||||
|
let mut blockhashes = HashMap::new();
|
||||||
|
while let Some(visit) = walk.get() {
|
||||||
|
let slot = *visit.node().data();
|
||||||
|
if blockstore.meta(slot).unwrap().is_some()
|
||||||
|
&& blockstore.orphan(slot).unwrap().is_none()
|
||||||
|
{
|
||||||
|
// If slot exists in blockstore and is not an orphan, then skip it
|
||||||
|
walk.forward();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let parent = walk.get_parent().map(|n| *n.data());
|
||||||
|
if parent.is_some() || !is_orphan {
|
||||||
|
let parent_hash = parent
|
||||||
|
// parent won't exist for first node in a tree where
|
||||||
|
// `is_orphan == true`
|
||||||
|
.and_then(|parent| blockhashes.get(&parent))
|
||||||
|
.unwrap_or(&starting_hash);
|
||||||
|
let entries = solana_entry::entry::create_ticks(
|
||||||
|
num_ticks * (std::cmp::max(1, slot - parent.unwrap_or(slot))),
|
||||||
|
0,
|
||||||
|
*parent_hash,
|
||||||
|
);
|
||||||
|
blockhashes.insert(slot, entries.last().unwrap().hash);
|
||||||
|
|
||||||
|
let mut shreds = solana_ledger::blockstore::entries_to_test_shreds(
|
||||||
|
entries.clone(),
|
||||||
|
slot,
|
||||||
|
parent.unwrap_or(slot),
|
||||||
|
is_slot_complete,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
// remove next to last shred
|
||||||
|
let shred = shreds.pop().unwrap();
|
||||||
|
shreds.pop().unwrap();
|
||||||
|
shreds.push(shred);
|
||||||
|
|
||||||
|
blockstore.insert_shreds(shreds, None, false).unwrap();
|
||||||
|
}
|
||||||
|
walk.forward();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_forks() -> (Blockstore, HeaviestSubtreeForkChoice) {
|
||||||
|
/*
|
||||||
|
Build fork structure:
|
||||||
|
slot 0
|
||||||
|
|
|
||||||
|
slot 1
|
||||||
|
/ \
|
||||||
|
slot 2 |
|
||||||
|
| slot 3
|
||||||
|
slot 4 |
|
||||||
|
slot 5
|
||||||
|
*/
|
||||||
|
|
||||||
|
let forks = tr(0) / (tr(1) / (tr(2) / (tr(4))) / (tr(3) / (tr(5))));
|
||||||
|
let ledger_path = get_tmp_ledger_path!();
|
||||||
|
let blockstore = Blockstore::open(&ledger_path).unwrap();
|
||||||
|
blockstore.add_tree(forks.clone(), false, false, 2, Hash::default());
|
||||||
|
|
||||||
|
(blockstore, HeaviestSubtreeForkChoice::new_from_tree(forks))
|
||||||
|
}
|
||||||
|
}
|
|
@ -95,6 +95,8 @@ pub struct RepairTiming {
|
||||||
pub add_votes_elapsed: u64,
|
pub add_votes_elapsed: u64,
|
||||||
pub get_best_orphans_elapsed: u64,
|
pub get_best_orphans_elapsed: u64,
|
||||||
pub get_best_shreds_elapsed: u64,
|
pub get_best_shreds_elapsed: u64,
|
||||||
|
pub get_unknown_last_index_elapsed: u64,
|
||||||
|
pub get_closest_completion_elapsed: u64,
|
||||||
pub send_repairs_elapsed: u64,
|
pub send_repairs_elapsed: u64,
|
||||||
pub build_repairs_batch_elapsed: u64,
|
pub build_repairs_batch_elapsed: u64,
|
||||||
pub batch_send_repairs_elapsed: u64,
|
pub batch_send_repairs_elapsed: u64,
|
||||||
|
@ -118,11 +120,50 @@ impl RepairTiming {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct BestRepairsStats {
|
||||||
|
pub call_count: u64,
|
||||||
|
pub num_orphan_slots: u64,
|
||||||
|
pub num_orphan_repairs: u64,
|
||||||
|
pub num_best_shreds_slots: u64,
|
||||||
|
pub num_best_shreds_repairs: u64,
|
||||||
|
pub num_unknown_last_index_slots: u64,
|
||||||
|
pub num_unknown_last_index_repairs: u64,
|
||||||
|
pub num_closest_completion_slots: u64,
|
||||||
|
pub num_closest_completion_repairs: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BestRepairsStats {
|
||||||
|
pub fn update(
|
||||||
|
&mut self,
|
||||||
|
num_orphan_slots: u64,
|
||||||
|
num_orphan_repairs: u64,
|
||||||
|
num_best_shreds_slots: u64,
|
||||||
|
num_best_shreds_repairs: u64,
|
||||||
|
num_unknown_last_index_slots: u64,
|
||||||
|
num_unknown_last_index_repairs: u64,
|
||||||
|
num_closest_completion_slots: u64,
|
||||||
|
num_closest_completion_repairs: u64,
|
||||||
|
) {
|
||||||
|
self.call_count += 1;
|
||||||
|
self.num_orphan_slots += num_orphan_slots;
|
||||||
|
self.num_orphan_repairs += num_orphan_repairs;
|
||||||
|
self.num_best_shreds_slots += num_best_shreds_slots;
|
||||||
|
self.num_best_shreds_repairs += num_best_shreds_repairs;
|
||||||
|
self.num_unknown_last_index_slots += num_unknown_last_index_slots;
|
||||||
|
self.num_unknown_last_index_repairs += num_unknown_last_index_repairs;
|
||||||
|
self.num_closest_completion_slots += num_closest_completion_slots;
|
||||||
|
self.num_closest_completion_repairs += num_closest_completion_repairs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const MAX_REPAIR_LENGTH: usize = 512;
|
pub const MAX_REPAIR_LENGTH: usize = 512;
|
||||||
pub const MAX_REPAIR_PER_DUPLICATE: usize = 20;
|
pub const MAX_REPAIR_PER_DUPLICATE: usize = 20;
|
||||||
pub const MAX_DUPLICATE_WAIT_MS: usize = 10_000;
|
pub const MAX_DUPLICATE_WAIT_MS: usize = 10_000;
|
||||||
pub const REPAIR_MS: u64 = 100;
|
pub const REPAIR_MS: u64 = 100;
|
||||||
pub const MAX_ORPHANS: usize = 5;
|
pub const MAX_ORPHANS: usize = 5;
|
||||||
|
pub const MAX_UNKNOWN_LAST_INDEX_REPAIRS: usize = 10;
|
||||||
|
pub const MAX_CLOSEST_COMPLETION_REPAIRS: usize = 100;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RepairInfo {
|
pub struct RepairInfo {
|
||||||
|
@ -210,6 +251,7 @@ impl RepairService {
|
||||||
let id = repair_info.cluster_info.id();
|
let id = repair_info.cluster_info.id();
|
||||||
let mut repair_stats = RepairStats::default();
|
let mut repair_stats = RepairStats::default();
|
||||||
let mut repair_timing = RepairTiming::default();
|
let mut repair_timing = RepairTiming::default();
|
||||||
|
let mut best_repairs_stats = BestRepairsStats::default();
|
||||||
let mut last_stats = Instant::now();
|
let mut last_stats = Instant::now();
|
||||||
let duplicate_slot_repair_statuses: HashMap<Slot, DuplicateSlotRepairStatus> =
|
let duplicate_slot_repair_statuses: HashMap<Slot, DuplicateSlotRepairStatus> =
|
||||||
HashMap::new();
|
HashMap::new();
|
||||||
|
@ -257,15 +299,20 @@ impl RepairService {
|
||||||
);
|
);
|
||||||
add_votes_elapsed.stop();
|
add_votes_elapsed.stop();
|
||||||
|
|
||||||
repair_weight.get_best_weighted_repairs(
|
let repairs = repair_weight.get_best_weighted_repairs(
|
||||||
blockstore,
|
blockstore,
|
||||||
root_bank.epoch_stakes_map(),
|
root_bank.epoch_stakes_map(),
|
||||||
root_bank.epoch_schedule(),
|
root_bank.epoch_schedule(),
|
||||||
MAX_ORPHANS,
|
MAX_ORPHANS,
|
||||||
MAX_REPAIR_LENGTH,
|
MAX_REPAIR_LENGTH,
|
||||||
|
MAX_UNKNOWN_LAST_INDEX_REPAIRS,
|
||||||
|
MAX_CLOSEST_COMPLETION_REPAIRS,
|
||||||
&duplicate_slot_repair_statuses,
|
&duplicate_slot_repair_statuses,
|
||||||
Some(&mut repair_timing),
|
Some(&mut repair_timing),
|
||||||
)
|
Some(&mut best_repairs_stats),
|
||||||
|
);
|
||||||
|
|
||||||
|
repairs
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut build_repairs_batch_elapsed = Measure::start("build_repairs_batch_elapsed");
|
let mut build_repairs_batch_elapsed = Measure::start("build_repairs_batch_elapsed");
|
||||||
|
@ -362,6 +409,16 @@ impl RepairService {
|
||||||
repair_timing.get_best_shreds_elapsed,
|
repair_timing.get_best_shreds_elapsed,
|
||||||
i64
|
i64
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"get-unknown-last-index-elapsed",
|
||||||
|
repair_timing.get_unknown_last_index_elapsed,
|
||||||
|
i64
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"get-closest-completion-elapsed",
|
||||||
|
repair_timing.get_closest_completion_elapsed,
|
||||||
|
i64
|
||||||
|
),
|
||||||
(
|
(
|
||||||
"send-repairs-elapsed",
|
"send-repairs-elapsed",
|
||||||
repair_timing.send_repairs_elapsed,
|
repair_timing.send_repairs_elapsed,
|
||||||
|
@ -378,8 +435,45 @@ impl RepairService {
|
||||||
i64
|
i64
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
datapoint_info!(
|
||||||
|
"serve_repair-best-repairs",
|
||||||
|
("call-count", best_repairs_stats.call_count, i64),
|
||||||
|
("orphan-slots", best_repairs_stats.num_orphan_slots, i64),
|
||||||
|
("orphan-repairs", best_repairs_stats.num_orphan_repairs, i64),
|
||||||
|
(
|
||||||
|
"best-shreds-slots",
|
||||||
|
best_repairs_stats.num_best_shreds_slots,
|
||||||
|
i64
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"best-shreds-repairs",
|
||||||
|
best_repairs_stats.num_best_shreds_repairs,
|
||||||
|
i64
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"unknown-last-index-slots",
|
||||||
|
best_repairs_stats.num_unknown_last_index_slots,
|
||||||
|
i64
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"unknown-last-index-repairs",
|
||||||
|
best_repairs_stats.num_unknown_last_index_repairs,
|
||||||
|
i64
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"closest-completion-slots",
|
||||||
|
best_repairs_stats.num_closest_completion_slots,
|
||||||
|
i64
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"closest-completion-repairs",
|
||||||
|
best_repairs_stats.num_closest_completion_repairs,
|
||||||
|
i64
|
||||||
|
),
|
||||||
|
);
|
||||||
repair_stats = RepairStats::default();
|
repair_stats = RepairStats::default();
|
||||||
repair_timing = RepairTiming::default();
|
repair_timing = RepairTiming::default();
|
||||||
|
best_repairs_stats = BestRepairsStats::default();
|
||||||
last_stats = Instant::now();
|
last_stats = Instant::now();
|
||||||
}
|
}
|
||||||
sleep(Duration::from_millis(REPAIR_MS));
|
sleep(Duration::from_millis(REPAIR_MS));
|
||||||
|
@ -474,7 +568,7 @@ impl RepairService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[cfg_attr(not(test), allow(dead_code))]
|
||||||
fn generate_duplicate_repairs_for_slot(
|
fn generate_duplicate_repairs_for_slot(
|
||||||
blockstore: &Blockstore,
|
blockstore: &Blockstore,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
|
@ -499,7 +593,7 @@ impl RepairService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[cfg_attr(not(test), allow(dead_code))]
|
||||||
fn generate_and_send_duplicate_repairs(
|
fn generate_and_send_duplicate_repairs(
|
||||||
duplicate_slot_repair_statuses: &mut HashMap<Slot, DuplicateSlotRepairStatus>,
|
duplicate_slot_repair_statuses: &mut HashMap<Slot, DuplicateSlotRepairStatus>,
|
||||||
cluster_slots: &ClusterSlots,
|
cluster_slots: &ClusterSlots,
|
||||||
|
@ -550,7 +644,7 @@ impl RepairService {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[cfg_attr(not(test), allow(dead_code))]
|
||||||
fn serialize_and_send_request(
|
fn serialize_and_send_request(
|
||||||
repair_type: &ShredRepairType,
|
repair_type: &ShredRepairType,
|
||||||
repair_socket: &UdpSocket,
|
repair_socket: &UdpSocket,
|
||||||
|
@ -566,7 +660,7 @@ impl RepairService {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[cfg_attr(not(test), allow(dead_code))]
|
||||||
fn update_duplicate_slot_repair_addr(
|
fn update_duplicate_slot_repair_addr(
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
status: &mut DuplicateSlotRepairStatus,
|
status: &mut DuplicateSlotRepairStatus,
|
||||||
|
@ -659,8 +753,11 @@ mod test {
|
||||||
&EpochSchedule::default(),
|
&EpochSchedule::default(),
|
||||||
MAX_ORPHANS,
|
MAX_ORPHANS,
|
||||||
MAX_REPAIR_LENGTH,
|
MAX_REPAIR_LENGTH,
|
||||||
|
MAX_UNKNOWN_LAST_INDEX_REPAIRS,
|
||||||
|
MAX_CLOSEST_COMPLETION_REPAIRS,
|
||||||
&HashSet::default(),
|
&HashSet::default(),
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
),
|
),
|
||||||
vec![
|
vec![
|
||||||
ShredRepairType::Orphan(2),
|
ShredRepairType::Orphan(2),
|
||||||
|
@ -693,8 +790,11 @@ mod test {
|
||||||
&EpochSchedule::default(),
|
&EpochSchedule::default(),
|
||||||
MAX_ORPHANS,
|
MAX_ORPHANS,
|
||||||
MAX_REPAIR_LENGTH,
|
MAX_REPAIR_LENGTH,
|
||||||
|
MAX_UNKNOWN_LAST_INDEX_REPAIRS,
|
||||||
|
MAX_CLOSEST_COMPLETION_REPAIRS,
|
||||||
&HashSet::default(),
|
&HashSet::default(),
|
||||||
None
|
None,
|
||||||
|
None,
|
||||||
),
|
),
|
||||||
vec![ShredRepairType::HighestShred(0, 0)]
|
vec![ShredRepairType::HighestShred(0, 0)]
|
||||||
);
|
);
|
||||||
|
@ -748,8 +848,11 @@ mod test {
|
||||||
&EpochSchedule::default(),
|
&EpochSchedule::default(),
|
||||||
MAX_ORPHANS,
|
MAX_ORPHANS,
|
||||||
MAX_REPAIR_LENGTH,
|
MAX_REPAIR_LENGTH,
|
||||||
|
MAX_UNKNOWN_LAST_INDEX_REPAIRS,
|
||||||
|
MAX_CLOSEST_COMPLETION_REPAIRS,
|
||||||
&HashSet::default(),
|
&HashSet::default(),
|
||||||
None
|
None,
|
||||||
|
None,
|
||||||
),
|
),
|
||||||
expected
|
expected
|
||||||
);
|
);
|
||||||
|
@ -761,8 +864,11 @@ mod test {
|
||||||
&EpochSchedule::default(),
|
&EpochSchedule::default(),
|
||||||
MAX_ORPHANS,
|
MAX_ORPHANS,
|
||||||
expected.len() - 2,
|
expected.len() - 2,
|
||||||
|
MAX_UNKNOWN_LAST_INDEX_REPAIRS,
|
||||||
|
MAX_CLOSEST_COMPLETION_REPAIRS,
|
||||||
&HashSet::default(),
|
&HashSet::default(),
|
||||||
None
|
None,
|
||||||
|
None,
|
||||||
)[..],
|
)[..],
|
||||||
expected[0..expected.len() - 2]
|
expected[0..expected.len() - 2]
|
||||||
);
|
);
|
||||||
|
@ -799,8 +905,11 @@ mod test {
|
||||||
&EpochSchedule::default(),
|
&EpochSchedule::default(),
|
||||||
MAX_ORPHANS,
|
MAX_ORPHANS,
|
||||||
MAX_REPAIR_LENGTH,
|
MAX_REPAIR_LENGTH,
|
||||||
|
MAX_UNKNOWN_LAST_INDEX_REPAIRS,
|
||||||
|
MAX_CLOSEST_COMPLETION_REPAIRS,
|
||||||
&HashSet::default(),
|
&HashSet::default(),
|
||||||
None
|
None,
|
||||||
|
None,
|
||||||
),
|
),
|
||||||
expected
|
expected
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
heaviest_subtree_fork_choice::HeaviestSubtreeForkChoice, repair_service::RepairTiming,
|
heaviest_subtree_fork_choice::HeaviestSubtreeForkChoice,
|
||||||
repair_weighted_traversal, serve_repair::ShredRepairType, tree_diff::TreeDiff,
|
repair_generic_traversal::{get_closest_completion, get_unknown_last_index},
|
||||||
|
repair_service::{BestRepairsStats, RepairTiming},
|
||||||
|
repair_weighted_traversal,
|
||||||
|
serve_repair::ShredRepairType,
|
||||||
|
tree_diff::TreeDiff,
|
||||||
|
};
|
||||||
|
use solana_ledger::{
|
||||||
|
ancestor_iterator::AncestorIterator, blockstore::Blockstore, blockstore_meta::SlotMeta,
|
||||||
};
|
};
|
||||||
use solana_ledger::{ancestor_iterator::AncestorIterator, blockstore::Blockstore};
|
|
||||||
use solana_measure::measure::Measure;
|
use solana_measure::measure::Measure;
|
||||||
use solana_runtime::{contains::Contains, epoch_stakes::EpochStakes};
|
use solana_runtime::{contains::Contains, epoch_stakes::EpochStakes};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
|
@ -138,6 +144,7 @@ impl RepairWeight {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn get_best_weighted_repairs<'a>(
|
pub fn get_best_weighted_repairs<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
blockstore: &Blockstore,
|
blockstore: &Blockstore,
|
||||||
|
@ -145,29 +152,91 @@ impl RepairWeight {
|
||||||
epoch_schedule: &EpochSchedule,
|
epoch_schedule: &EpochSchedule,
|
||||||
max_new_orphans: usize,
|
max_new_orphans: usize,
|
||||||
max_new_shreds: usize,
|
max_new_shreds: usize,
|
||||||
|
max_unknown_last_index_repairs: usize,
|
||||||
|
max_closest_completion_repairs: usize,
|
||||||
ignore_slots: &impl Contains<'a, Slot>,
|
ignore_slots: &impl Contains<'a, Slot>,
|
||||||
repair_timing: Option<&mut RepairTiming>,
|
repair_timing: Option<&mut RepairTiming>,
|
||||||
|
stats: Option<&mut BestRepairsStats>,
|
||||||
) -> Vec<ShredRepairType> {
|
) -> Vec<ShredRepairType> {
|
||||||
let mut repairs = vec![];
|
let mut repairs = vec![];
|
||||||
|
let mut processed_slots: HashSet<Slot> = vec![self.root].into_iter().collect();
|
||||||
|
let mut slot_meta_cache = HashMap::default();
|
||||||
|
|
||||||
let mut get_best_orphans_elapsed = Measure::start("get_best_orphans");
|
let mut get_best_orphans_elapsed = Measure::start("get_best_orphans");
|
||||||
// Update the orphans in order from heaviest to least heavy
|
// Update the orphans in order from heaviest to least heavy
|
||||||
self.get_best_orphans(
|
self.get_best_orphans(
|
||||||
blockstore,
|
blockstore,
|
||||||
|
&mut processed_slots,
|
||||||
&mut repairs,
|
&mut repairs,
|
||||||
epoch_stakes,
|
epoch_stakes,
|
||||||
epoch_schedule,
|
epoch_schedule,
|
||||||
max_new_orphans,
|
max_new_orphans,
|
||||||
);
|
);
|
||||||
|
let num_orphan_slots = processed_slots.len() - 1;
|
||||||
|
let num_orphan_repairs = repairs.len();
|
||||||
get_best_orphans_elapsed.stop();
|
get_best_orphans_elapsed.stop();
|
||||||
|
|
||||||
let mut get_best_shreds_elapsed = Measure::start("get_best_shreds");
|
let mut get_best_shreds_elapsed = Measure::start("get_best_shreds");
|
||||||
|
let mut best_shreds_repairs = Vec::default();
|
||||||
// Find the best incomplete slots in rooted subtree
|
// Find the best incomplete slots in rooted subtree
|
||||||
self.get_best_shreds(blockstore, &mut repairs, max_new_shreds, ignore_slots);
|
self.get_best_shreds(
|
||||||
|
blockstore,
|
||||||
|
&mut slot_meta_cache,
|
||||||
|
&mut best_shreds_repairs,
|
||||||
|
max_new_shreds,
|
||||||
|
ignore_slots,
|
||||||
|
);
|
||||||
|
let num_best_shreds_repairs = best_shreds_repairs.len();
|
||||||
|
let repair_slots_set: HashSet<Slot> =
|
||||||
|
best_shreds_repairs.iter().map(|r| r.slot()).collect();
|
||||||
|
let num_best_shreds_slots = repair_slots_set.len();
|
||||||
|
processed_slots.extend(repair_slots_set);
|
||||||
|
repairs.extend(best_shreds_repairs);
|
||||||
get_best_shreds_elapsed.stop();
|
get_best_shreds_elapsed.stop();
|
||||||
|
|
||||||
|
let mut get_unknown_last_index_elapsed = Measure::start("get_unknown_last_index");
|
||||||
|
let pre_num_slots = processed_slots.len();
|
||||||
|
let unknown_last_index_repairs = self.get_best_unknown_last_index(
|
||||||
|
blockstore,
|
||||||
|
&mut slot_meta_cache,
|
||||||
|
&mut processed_slots,
|
||||||
|
max_unknown_last_index_repairs,
|
||||||
|
);
|
||||||
|
let num_unknown_last_index_repairs = unknown_last_index_repairs.len();
|
||||||
|
let num_unknown_last_index_slots = processed_slots.len() - pre_num_slots;
|
||||||
|
repairs.extend(unknown_last_index_repairs);
|
||||||
|
get_unknown_last_index_elapsed.stop();
|
||||||
|
|
||||||
|
let mut get_closest_completion_elapsed = Measure::start("get_closest_completion");
|
||||||
|
let pre_num_slots = processed_slots.len();
|
||||||
|
let closest_completion_repairs = self.get_best_closest_completion(
|
||||||
|
blockstore,
|
||||||
|
&mut slot_meta_cache,
|
||||||
|
&mut processed_slots,
|
||||||
|
max_closest_completion_repairs,
|
||||||
|
);
|
||||||
|
let num_closest_completion_repairs = closest_completion_repairs.len();
|
||||||
|
let num_closest_completion_slots = processed_slots.len() - pre_num_slots;
|
||||||
|
repairs.extend(closest_completion_repairs);
|
||||||
|
get_closest_completion_elapsed.stop();
|
||||||
|
|
||||||
|
if let Some(stats) = stats {
|
||||||
|
stats.update(
|
||||||
|
num_orphan_slots as u64,
|
||||||
|
num_orphan_repairs as u64,
|
||||||
|
num_best_shreds_slots as u64,
|
||||||
|
num_best_shreds_repairs as u64,
|
||||||
|
num_unknown_last_index_slots as u64,
|
||||||
|
num_unknown_last_index_repairs as u64,
|
||||||
|
num_closest_completion_slots as u64,
|
||||||
|
num_closest_completion_repairs as u64,
|
||||||
|
);
|
||||||
|
}
|
||||||
if let Some(repair_timing) = repair_timing {
|
if let Some(repair_timing) = repair_timing {
|
||||||
repair_timing.get_best_orphans_elapsed += get_best_orphans_elapsed.as_us();
|
repair_timing.get_best_orphans_elapsed += get_best_orphans_elapsed.as_us();
|
||||||
repair_timing.get_best_shreds_elapsed += get_best_shreds_elapsed.as_us();
|
repair_timing.get_best_shreds_elapsed += get_best_shreds_elapsed.as_us();
|
||||||
|
repair_timing.get_unknown_last_index_elapsed += get_unknown_last_index_elapsed.as_us();
|
||||||
|
repair_timing.get_closest_completion_elapsed += get_closest_completion_elapsed.as_us();
|
||||||
}
|
}
|
||||||
repairs
|
repairs
|
||||||
}
|
}
|
||||||
|
@ -248,6 +317,7 @@ impl RepairWeight {
|
||||||
fn get_best_shreds<'a>(
|
fn get_best_shreds<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
blockstore: &Blockstore,
|
blockstore: &Blockstore,
|
||||||
|
slot_meta_cache: &mut HashMap<Slot, Option<SlotMeta>>,
|
||||||
repairs: &mut Vec<ShredRepairType>,
|
repairs: &mut Vec<ShredRepairType>,
|
||||||
max_new_shreds: usize,
|
max_new_shreds: usize,
|
||||||
ignore_slots: &impl Contains<'a, Slot>,
|
ignore_slots: &impl Contains<'a, Slot>,
|
||||||
|
@ -256,6 +326,7 @@ impl RepairWeight {
|
||||||
repair_weighted_traversal::get_best_repair_shreds(
|
repair_weighted_traversal::get_best_repair_shreds(
|
||||||
root_tree,
|
root_tree,
|
||||||
blockstore,
|
blockstore,
|
||||||
|
slot_meta_cache,
|
||||||
repairs,
|
repairs,
|
||||||
max_new_shreds,
|
max_new_shreds,
|
||||||
ignore_slots,
|
ignore_slots,
|
||||||
|
@ -265,6 +336,7 @@ impl RepairWeight {
|
||||||
fn get_best_orphans(
|
fn get_best_orphans(
|
||||||
&mut self,
|
&mut self,
|
||||||
blockstore: &Blockstore,
|
blockstore: &Blockstore,
|
||||||
|
processed_slots: &mut HashSet<Slot>,
|
||||||
repairs: &mut Vec<ShredRepairType>,
|
repairs: &mut Vec<ShredRepairType>,
|
||||||
epoch_stakes: &HashMap<Epoch, EpochStakes>,
|
epoch_stakes: &HashMap<Epoch, EpochStakes>,
|
||||||
epoch_schedule: &EpochSchedule,
|
epoch_schedule: &EpochSchedule,
|
||||||
|
@ -292,7 +364,7 @@ impl RepairWeight {
|
||||||
if best_orphans.len() >= max_new_orphans {
|
if best_orphans.len() >= max_new_orphans {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if heaviest_tree_root == self.root {
|
if processed_slots.contains(&heaviest_tree_root) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Ignore trees that were merged in a previous iteration
|
// Ignore trees that were merged in a previous iteration
|
||||||
|
@ -307,6 +379,7 @@ impl RepairWeight {
|
||||||
if new_orphan_root != self.root && !best_orphans.contains(&new_orphan_root) {
|
if new_orphan_root != self.root && !best_orphans.contains(&new_orphan_root) {
|
||||||
best_orphans.insert(new_orphan_root);
|
best_orphans.insert(new_orphan_root);
|
||||||
repairs.push(ShredRepairType::Orphan(new_orphan_root));
|
repairs.push(ShredRepairType::Orphan(new_orphan_root));
|
||||||
|
processed_slots.insert(new_orphan_root);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -319,6 +392,7 @@ impl RepairWeight {
|
||||||
if !best_orphans.contains(&new_orphan) {
|
if !best_orphans.contains(&new_orphan) {
|
||||||
repairs.push(ShredRepairType::Orphan(new_orphan));
|
repairs.push(ShredRepairType::Orphan(new_orphan));
|
||||||
best_orphans.insert(new_orphan);
|
best_orphans.insert(new_orphan);
|
||||||
|
processed_slots.insert(new_orphan);
|
||||||
}
|
}
|
||||||
|
|
||||||
if best_orphans.len() == max_new_orphans {
|
if best_orphans.len() == max_new_orphans {
|
||||||
|
@ -328,6 +402,54 @@ impl RepairWeight {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_best_unknown_last_index(
|
||||||
|
&mut self,
|
||||||
|
blockstore: &Blockstore,
|
||||||
|
slot_meta_cache: &mut HashMap<Slot, Option<SlotMeta>>,
|
||||||
|
processed_slots: &mut HashSet<Slot>,
|
||||||
|
max_new_repairs: usize,
|
||||||
|
) -> Vec<ShredRepairType> {
|
||||||
|
let mut repairs = Vec::default();
|
||||||
|
for (_slot, tree) in self.trees.iter() {
|
||||||
|
if repairs.len() >= max_new_repairs {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let new_repairs = get_unknown_last_index(
|
||||||
|
tree,
|
||||||
|
blockstore,
|
||||||
|
slot_meta_cache,
|
||||||
|
processed_slots,
|
||||||
|
max_new_repairs - repairs.len(),
|
||||||
|
);
|
||||||
|
repairs.extend(new_repairs);
|
||||||
|
}
|
||||||
|
repairs
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_best_closest_completion(
|
||||||
|
&mut self,
|
||||||
|
blockstore: &Blockstore,
|
||||||
|
slot_meta_cache: &mut HashMap<Slot, Option<SlotMeta>>,
|
||||||
|
processed_slots: &mut HashSet<Slot>,
|
||||||
|
max_new_repairs: usize,
|
||||||
|
) -> Vec<ShredRepairType> {
|
||||||
|
let mut repairs = Vec::default();
|
||||||
|
for (_slot, tree) in self.trees.iter() {
|
||||||
|
if repairs.len() >= max_new_repairs {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let new_repairs = get_closest_completion(
|
||||||
|
tree,
|
||||||
|
blockstore,
|
||||||
|
slot_meta_cache,
|
||||||
|
processed_slots,
|
||||||
|
max_new_repairs - repairs.len(),
|
||||||
|
);
|
||||||
|
repairs.extend(new_repairs);
|
||||||
|
}
|
||||||
|
repairs
|
||||||
|
}
|
||||||
|
|
||||||
// Attempts to chain the orphan subtree rooted at `orphan_tree_root`
|
// Attempts to chain the orphan subtree rooted at `orphan_tree_root`
|
||||||
// to any earlier subtree with new any ancestry information in `blockstore`.
|
// to any earlier subtree with new any ancestry information in `blockstore`.
|
||||||
// Returns the earliest known ancestor of `heaviest_tree_root`.
|
// Returns the earliest known ancestor of `heaviest_tree_root`.
|
||||||
|
@ -852,8 +974,10 @@ mod test {
|
||||||
// Ask for only 1 orphan. Because the orphans have the same weight,
|
// Ask for only 1 orphan. Because the orphans have the same weight,
|
||||||
// should prioritize smaller orphan first
|
// should prioritize smaller orphan first
|
||||||
let mut repairs = vec![];
|
let mut repairs = vec![];
|
||||||
|
let mut processed_slots: HashSet<Slot> = vec![repair_weight.root].into_iter().collect();
|
||||||
repair_weight.get_best_orphans(
|
repair_weight.get_best_orphans(
|
||||||
&blockstore,
|
&blockstore,
|
||||||
|
&mut processed_slots,
|
||||||
&mut repairs,
|
&mut repairs,
|
||||||
bank.epoch_stakes_map(),
|
bank.epoch_stakes_map(),
|
||||||
bank.epoch_schedule(),
|
bank.epoch_schedule(),
|
||||||
|
@ -879,6 +1003,7 @@ mod test {
|
||||||
// New vote on same orphan branch, without any new slot chaining
|
// New vote on same orphan branch, without any new slot chaining
|
||||||
// information blockstore should not resolve the orphan
|
// information blockstore should not resolve the orphan
|
||||||
repairs = vec![];
|
repairs = vec![];
|
||||||
|
processed_slots = vec![repair_weight.root].into_iter().collect();
|
||||||
let votes = vec![(10, vec![vote_pubkeys[0]])];
|
let votes = vec![(10, vec![vote_pubkeys[0]])];
|
||||||
repair_weight.add_votes(
|
repair_weight.add_votes(
|
||||||
&blockstore,
|
&blockstore,
|
||||||
|
@ -888,6 +1013,7 @@ mod test {
|
||||||
);
|
);
|
||||||
repair_weight.get_best_orphans(
|
repair_weight.get_best_orphans(
|
||||||
&blockstore,
|
&blockstore,
|
||||||
|
&mut processed_slots,
|
||||||
&mut repairs,
|
&mut repairs,
|
||||||
bank.epoch_stakes_map(),
|
bank.epoch_stakes_map(),
|
||||||
bank.epoch_schedule(),
|
bank.epoch_schedule(),
|
||||||
|
@ -898,8 +1024,10 @@ mod test {
|
||||||
|
|
||||||
// Ask for 2 orphans, should return all the orphans
|
// Ask for 2 orphans, should return all the orphans
|
||||||
repairs = vec![];
|
repairs = vec![];
|
||||||
|
processed_slots = vec![repair_weight.root].into_iter().collect();
|
||||||
repair_weight.get_best_orphans(
|
repair_weight.get_best_orphans(
|
||||||
&blockstore,
|
&blockstore,
|
||||||
|
&mut processed_slots,
|
||||||
&mut repairs,
|
&mut repairs,
|
||||||
bank.epoch_stakes_map(),
|
bank.epoch_stakes_map(),
|
||||||
bank.epoch_schedule(),
|
bank.epoch_schedule(),
|
||||||
|
@ -911,6 +1039,7 @@ mod test {
|
||||||
|
|
||||||
// If one orphan gets heavier, should pick that one
|
// If one orphan gets heavier, should pick that one
|
||||||
repairs = vec![];
|
repairs = vec![];
|
||||||
|
processed_slots = vec![repair_weight.root].into_iter().collect();
|
||||||
let votes = vec![(20, vec![vote_pubkeys[0]])];
|
let votes = vec![(20, vec![vote_pubkeys[0]])];
|
||||||
repair_weight.add_votes(
|
repair_weight.add_votes(
|
||||||
&blockstore,
|
&blockstore,
|
||||||
|
@ -920,6 +1049,7 @@ mod test {
|
||||||
);
|
);
|
||||||
repair_weight.get_best_orphans(
|
repair_weight.get_best_orphans(
|
||||||
&blockstore,
|
&blockstore,
|
||||||
|
&mut processed_slots,
|
||||||
&mut repairs,
|
&mut repairs,
|
||||||
bank.epoch_stakes_map(),
|
bank.epoch_stakes_map(),
|
||||||
bank.epoch_schedule(),
|
bank.epoch_schedule(),
|
||||||
|
@ -931,10 +1061,12 @@ mod test {
|
||||||
// Resolve the orphans, should now return no
|
// Resolve the orphans, should now return no
|
||||||
// orphans
|
// orphans
|
||||||
repairs = vec![];
|
repairs = vec![];
|
||||||
|
processed_slots = vec![repair_weight.root].into_iter().collect();
|
||||||
blockstore.add_tree(tr(6) / (tr(8)), true, true, 2, Hash::default());
|
blockstore.add_tree(tr(6) / (tr(8)), true, true, 2, Hash::default());
|
||||||
blockstore.add_tree(tr(11) / (tr(20)), true, true, 2, Hash::default());
|
blockstore.add_tree(tr(11) / (tr(20)), true, true, 2, Hash::default());
|
||||||
repair_weight.get_best_orphans(
|
repair_weight.get_best_orphans(
|
||||||
&blockstore,
|
&blockstore,
|
||||||
|
&mut processed_slots,
|
||||||
&mut repairs,
|
&mut repairs,
|
||||||
bank.epoch_stakes_map(),
|
bank.epoch_stakes_map(),
|
||||||
bank.epoch_schedule(),
|
bank.epoch_schedule(),
|
||||||
|
@ -967,9 +1099,11 @@ mod test {
|
||||||
// orphan in the `trees` map, we should search for
|
// orphan in the `trees` map, we should search for
|
||||||
// exactly one more of the remaining two
|
// exactly one more of the remaining two
|
||||||
let mut repairs = vec![];
|
let mut repairs = vec![];
|
||||||
|
let mut processed_slots: HashSet<Slot> = vec![repair_weight.root].into_iter().collect();
|
||||||
blockstore.add_tree(tr(100) / (tr(101)), true, true, 2, Hash::default());
|
blockstore.add_tree(tr(100) / (tr(101)), true, true, 2, Hash::default());
|
||||||
repair_weight.get_best_orphans(
|
repair_weight.get_best_orphans(
|
||||||
&blockstore,
|
&blockstore,
|
||||||
|
&mut processed_slots,
|
||||||
&mut repairs,
|
&mut repairs,
|
||||||
bank.epoch_stakes_map(),
|
bank.epoch_stakes_map(),
|
||||||
bank.epoch_schedule(),
|
bank.epoch_schedule(),
|
||||||
|
@ -981,8 +1115,10 @@ mod test {
|
||||||
|
|
||||||
// If we ask for 3 orphans, we should get all of them
|
// If we ask for 3 orphans, we should get all of them
|
||||||
let mut repairs = vec![];
|
let mut repairs = vec![];
|
||||||
|
processed_slots = vec![repair_weight.root].into_iter().collect();
|
||||||
repair_weight.get_best_orphans(
|
repair_weight.get_best_orphans(
|
||||||
&blockstore,
|
&blockstore,
|
||||||
|
&mut processed_slots,
|
||||||
&mut repairs,
|
&mut repairs,
|
||||||
bank.epoch_stakes_map(),
|
bank.epoch_stakes_map(),
|
||||||
bank.epoch_schedule(),
|
bank.epoch_schedule(),
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
||||||
heaviest_subtree_fork_choice::HeaviestSubtreeForkChoice, repair_service::RepairService,
|
heaviest_subtree_fork_choice::HeaviestSubtreeForkChoice, repair_service::RepairService,
|
||||||
serve_repair::ShredRepairType, tree_diff::TreeDiff,
|
serve_repair::ShredRepairType, tree_diff::TreeDiff,
|
||||||
};
|
};
|
||||||
use solana_ledger::blockstore::Blockstore;
|
use solana_ledger::{blockstore::Blockstore, blockstore_meta::SlotMeta};
|
||||||
use solana_runtime::contains::Contains;
|
use solana_runtime::contains::Contains;
|
||||||
use solana_sdk::{clock::Slot, hash::Hash};
|
use solana_sdk::{clock::Slot, hash::Hash};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
@ -29,7 +29,7 @@ struct RepairWeightTraversal<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RepairWeightTraversal<'a> {
|
impl<'a> RepairWeightTraversal<'a> {
|
||||||
pub fn new(tree: &'a HeaviestSubtreeForkChoice) -> Self {
|
fn new(tree: &'a HeaviestSubtreeForkChoice) -> Self {
|
||||||
Self {
|
Self {
|
||||||
tree,
|
tree,
|
||||||
pending: vec![Visit::Unvisited(tree.root().0)],
|
pending: vec![Visit::Unvisited(tree.root().0)],
|
||||||
|
@ -73,6 +73,7 @@ impl<'a> Iterator for RepairWeightTraversal<'a> {
|
||||||
pub fn get_best_repair_shreds<'a>(
|
pub fn get_best_repair_shreds<'a>(
|
||||||
tree: &HeaviestSubtreeForkChoice,
|
tree: &HeaviestSubtreeForkChoice,
|
||||||
blockstore: &Blockstore,
|
blockstore: &Blockstore,
|
||||||
|
slot_meta_cache: &mut HashMap<Slot, Option<SlotMeta>>,
|
||||||
repairs: &mut Vec<ShredRepairType>,
|
repairs: &mut Vec<ShredRepairType>,
|
||||||
max_new_shreds: usize,
|
max_new_shreds: usize,
|
||||||
ignore_slots: &impl Contains<'a, Slot>,
|
ignore_slots: &impl Contains<'a, Slot>,
|
||||||
|
@ -81,7 +82,6 @@ pub fn get_best_repair_shreds<'a>(
|
||||||
let max_repairs = initial_len + max_new_shreds;
|
let max_repairs = initial_len + max_new_shreds;
|
||||||
let weighted_iter = RepairWeightTraversal::new(tree);
|
let weighted_iter = RepairWeightTraversal::new(tree);
|
||||||
let mut visited_set = HashSet::new();
|
let mut visited_set = HashSet::new();
|
||||||
let mut slot_meta_cache = HashMap::new();
|
|
||||||
for next in weighted_iter {
|
for next in weighted_iter {
|
||||||
if repairs.len() > max_repairs {
|
if repairs.len() > max_repairs {
|
||||||
break;
|
break;
|
||||||
|
@ -215,10 +215,12 @@ pub mod test {
|
||||||
// `blockstore` and `heaviest_subtree_fork_choice` match exactly, so should
|
// `blockstore` and `heaviest_subtree_fork_choice` match exactly, so should
|
||||||
// return repairs for all slots (none are completed) in order of traversal
|
// return repairs for all slots (none are completed) in order of traversal
|
||||||
let mut repairs = vec![];
|
let mut repairs = vec![];
|
||||||
|
let mut slot_meta_cache = HashMap::default();
|
||||||
let last_shred = blockstore.meta(0).unwrap().unwrap().received;
|
let last_shred = blockstore.meta(0).unwrap().unwrap().received;
|
||||||
get_best_repair_shreds(
|
get_best_repair_shreds(
|
||||||
&heaviest_subtree_fork_choice,
|
&heaviest_subtree_fork_choice,
|
||||||
&blockstore,
|
&blockstore,
|
||||||
|
&mut slot_meta_cache,
|
||||||
&mut repairs,
|
&mut repairs,
|
||||||
6,
|
6,
|
||||||
&HashSet::default(),
|
&HashSet::default(),
|
||||||
|
@ -234,6 +236,7 @@ pub mod test {
|
||||||
// Add some leaves to blockstore, attached to the current best leaf, should prioritize
|
// Add some leaves to blockstore, attached to the current best leaf, should prioritize
|
||||||
// repairing those new leaves before trying other branches
|
// repairing those new leaves before trying other branches
|
||||||
repairs = vec![];
|
repairs = vec![];
|
||||||
|
slot_meta_cache = HashMap::default();
|
||||||
let best_overall_slot = heaviest_subtree_fork_choice.best_overall_slot().0;
|
let best_overall_slot = heaviest_subtree_fork_choice.best_overall_slot().0;
|
||||||
assert_eq!(best_overall_slot, 4);
|
assert_eq!(best_overall_slot, 4);
|
||||||
blockstore.add_tree(
|
blockstore.add_tree(
|
||||||
|
@ -246,6 +249,7 @@ pub mod test {
|
||||||
get_best_repair_shreds(
|
get_best_repair_shreds(
|
||||||
&heaviest_subtree_fork_choice,
|
&heaviest_subtree_fork_choice,
|
||||||
&blockstore,
|
&blockstore,
|
||||||
|
&mut slot_meta_cache,
|
||||||
&mut repairs,
|
&mut repairs,
|
||||||
6,
|
6,
|
||||||
&HashSet::default(),
|
&HashSet::default(),
|
||||||
|
@ -260,6 +264,7 @@ pub mod test {
|
||||||
|
|
||||||
// Completing slots should remove them from the repair list
|
// Completing slots should remove them from the repair list
|
||||||
repairs = vec![];
|
repairs = vec![];
|
||||||
|
slot_meta_cache = HashMap::default();
|
||||||
let completed_shreds: Vec<Shred> = [0, 2, 4, 6]
|
let completed_shreds: Vec<Shred> = [0, 2, 4, 6]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|slot| {
|
.map(|slot| {
|
||||||
|
@ -281,6 +286,7 @@ pub mod test {
|
||||||
get_best_repair_shreds(
|
get_best_repair_shreds(
|
||||||
&heaviest_subtree_fork_choice,
|
&heaviest_subtree_fork_choice,
|
||||||
&blockstore,
|
&blockstore,
|
||||||
|
&mut slot_meta_cache,
|
||||||
&mut repairs,
|
&mut repairs,
|
||||||
4,
|
4,
|
||||||
&HashSet::default(),
|
&HashSet::default(),
|
||||||
|
@ -296,10 +302,12 @@ pub mod test {
|
||||||
// Adding incomplete children with higher weighted parents, even if
|
// Adding incomplete children with higher weighted parents, even if
|
||||||
// the parents are complete should still be repaired
|
// the parents are complete should still be repaired
|
||||||
repairs = vec![];
|
repairs = vec![];
|
||||||
|
slot_meta_cache = HashMap::default();
|
||||||
blockstore.add_tree(tr(2) / (tr(8)), true, false, 2, Hash::default());
|
blockstore.add_tree(tr(2) / (tr(8)), true, false, 2, Hash::default());
|
||||||
get_best_repair_shreds(
|
get_best_repair_shreds(
|
||||||
&heaviest_subtree_fork_choice,
|
&heaviest_subtree_fork_choice,
|
||||||
&blockstore,
|
&blockstore,
|
||||||
|
&mut slot_meta_cache,
|
||||||
&mut repairs,
|
&mut repairs,
|
||||||
4,
|
4,
|
||||||
&HashSet::default(),
|
&HashSet::default(),
|
||||||
|
@ -320,9 +328,11 @@ pub mod test {
|
||||||
// 4 again when the Unvisited(2) event happens
|
// 4 again when the Unvisited(2) event happens
|
||||||
blockstore.add_tree(tr(2) / (tr(6) / tr(7)), true, false, 2, Hash::default());
|
blockstore.add_tree(tr(2) / (tr(6) / tr(7)), true, false, 2, Hash::default());
|
||||||
let mut repairs = vec![];
|
let mut repairs = vec![];
|
||||||
|
let mut slot_meta_cache = HashMap::default();
|
||||||
get_best_repair_shreds(
|
get_best_repair_shreds(
|
||||||
&heaviest_subtree_fork_choice,
|
&heaviest_subtree_fork_choice,
|
||||||
&blockstore,
|
&blockstore,
|
||||||
|
&mut slot_meta_cache,
|
||||||
&mut repairs,
|
&mut repairs,
|
||||||
std::usize::MAX,
|
std::usize::MAX,
|
||||||
&HashSet::default(),
|
&HashSet::default(),
|
||||||
|
@ -344,11 +354,13 @@ pub mod test {
|
||||||
// Adding slots to ignore should remove them from the repair set, but
|
// Adding slots to ignore should remove them from the repair set, but
|
||||||
// should not remove their children
|
// should not remove their children
|
||||||
let mut repairs = vec![];
|
let mut repairs = vec![];
|
||||||
|
let mut slot_meta_cache = HashMap::default();
|
||||||
let mut ignore_set: HashSet<Slot> = vec![1, 3].into_iter().collect();
|
let mut ignore_set: HashSet<Slot> = vec![1, 3].into_iter().collect();
|
||||||
let last_shred = blockstore.meta(0).unwrap().unwrap().received;
|
let last_shred = blockstore.meta(0).unwrap().unwrap().received;
|
||||||
get_best_repair_shreds(
|
get_best_repair_shreds(
|
||||||
&heaviest_subtree_fork_choice,
|
&heaviest_subtree_fork_choice,
|
||||||
&blockstore,
|
&blockstore,
|
||||||
|
&mut slot_meta_cache,
|
||||||
&mut repairs,
|
&mut repairs,
|
||||||
std::usize::MAX,
|
std::usize::MAX,
|
||||||
&ignore_set,
|
&ignore_set,
|
||||||
|
@ -364,11 +376,13 @@ pub mod test {
|
||||||
// Adding slot 2 to ignore should not remove its unexplored children from
|
// Adding slot 2 to ignore should not remove its unexplored children from
|
||||||
// the repair set
|
// the repair set
|
||||||
repairs = vec![];
|
repairs = vec![];
|
||||||
|
slot_meta_cache = HashMap::default();
|
||||||
blockstore.add_tree(tr(2) / (tr(6) / tr(7)), true, false, 2, Hash::default());
|
blockstore.add_tree(tr(2) / (tr(6) / tr(7)), true, false, 2, Hash::default());
|
||||||
ignore_set.insert(2);
|
ignore_set.insert(2);
|
||||||
get_best_repair_shreds(
|
get_best_repair_shreds(
|
||||||
&heaviest_subtree_fork_choice,
|
&heaviest_subtree_fork_choice,
|
||||||
&blockstore,
|
&blockstore,
|
||||||
|
&mut slot_meta_cache,
|
||||||
&mut repairs,
|
&mut repairs,
|
||||||
std::usize::MAX,
|
std::usize::MAX,
|
||||||
&ignore_set,
|
&ignore_set,
|
||||||
|
@ -385,9 +399,11 @@ pub mod test {
|
||||||
// child 7 from the repair set
|
// child 7 from the repair set
|
||||||
repairs = vec![];
|
repairs = vec![];
|
||||||
ignore_set.insert(6);
|
ignore_set.insert(6);
|
||||||
|
slot_meta_cache = HashMap::default();
|
||||||
get_best_repair_shreds(
|
get_best_repair_shreds(
|
||||||
&heaviest_subtree_fork_choice,
|
&heaviest_subtree_fork_choice,
|
||||||
&blockstore,
|
&blockstore,
|
||||||
|
&mut slot_meta_cache,
|
||||||
&mut repairs,
|
&mut repairs,
|
||||||
std::usize::MAX,
|
std::usize::MAX,
|
||||||
&ignore_set,
|
&ignore_set,
|
||||||
|
|
|
@ -192,6 +192,14 @@ impl SlotMeta {
|
||||||
self.consumed == self.last_index + 1
|
self.consumed == self.last_index + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn known_last_index(&self) -> Option<u64> {
|
||||||
|
if self.last_index == std::u64::MAX {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(self.last_index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_parent_set(&self) -> bool {
|
pub fn is_parent_set(&self) -> bool {
|
||||||
self.parent_slot != std::u64::MAX
|
self.parent_slot != std::u64::MAX
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue