Skip to content

Commit 28acb1c

Browse files
authored
removes branch to recover legacy shreds (#4488)
Legacy shreds are discarded on all clusters: https://github.com/anza-xyz/agave/blob/91d0d0cae/ledger/src/shred.rs#L1275-L1277 Removing the branch to recover legacy shreds would allow to further simplify and optimize shreds recovery code.
1 parent 6b21eae commit 28acb1c

File tree

2 files changed

+67
-100
lines changed

2 files changed

+67
-100
lines changed

ledger/src/blockstore.rs

+45-57
Original file line numberDiff line numberDiff line change
@@ -763,10 +763,10 @@ impl Blockstore {
763763
fn get_recovery_data_shreds<'a>(
764764
&'a self,
765765
index: &'a Index,
766-
slot: Slot,
767766
erasure_meta: &'a ErasureMeta,
768767
prev_inserted_shreds: &'a HashMap<ShredId, Shred>,
769768
) -> impl Iterator<Item = Shred> + 'a {
769+
let slot = index.slot;
770770
erasure_meta.data_shreds_indices().filter_map(move |i| {
771771
let key = ShredId::new(slot, u32::try_from(i).unwrap(), ShredType::Data);
772772
if let Some(shred) = prev_inserted_shreds.get(&key) {
@@ -792,10 +792,10 @@ impl Blockstore {
792792
fn get_recovery_coding_shreds<'a>(
793793
&'a self,
794794
index: &'a Index,
795-
slot: Slot,
796795
erasure_meta: &'a ErasureMeta,
797796
prev_inserted_shreds: &'a HashMap<ShredId, Shred>,
798797
) -> impl Iterator<Item = Shred> + 'a {
798+
let slot = index.slot;
799799
erasure_meta.coding_shreds_indices().filter_map(move |i| {
800800
let key = ShredId::new(slot, u32::try_from(i).unwrap(), ShredType::Code);
801801
if let Some(shred) = prev_inserted_shreds.get(&key) {
@@ -823,19 +823,12 @@ impl Blockstore {
823823
index: &Index,
824824
erasure_meta: &ErasureMeta,
825825
prev_inserted_shreds: &HashMap<ShredId, Shred>,
826-
leader_schedule_cache: &LeaderScheduleCache,
827826
reed_solomon_cache: &ReedSolomonCache,
828827
) -> std::result::Result<Vec<Shred>, shred::Error> {
829828
// Find shreds for this erasure set and try recovery
830-
let slot = index.slot;
831-
let available_shreds: Vec<_> = self
832-
.get_recovery_data_shreds(index, slot, erasure_meta, prev_inserted_shreds)
833-
.chain(self.get_recovery_coding_shreds(index, slot, erasure_meta, prev_inserted_shreds))
834-
.collect();
835-
let get_slot_leader = |slot: Slot| -> Option<Pubkey> {
836-
leader_schedule_cache.slot_leader_at(slot, /*bank:*/ None)
837-
};
838-
shred::recover(available_shreds, reed_solomon_cache, get_slot_leader)
829+
let data = self.get_recovery_data_shreds(index, erasure_meta, prev_inserted_shreds);
830+
let code = self.get_recovery_coding_shreds(index, erasure_meta, prev_inserted_shreds);
831+
shred::recover(data.chain(code), reed_solomon_cache)
839832
}
840833

841834
/// Collects and reports [`BlockstoreRocksDbColumnFamilyMetrics`] for the
@@ -942,7 +935,6 @@ impl Blockstore {
942935
erasure_metas: &'a BTreeMap<ErasureSetId, WorkingEntry<ErasureMeta>>,
943936
index_working_set: &'a HashMap<u64, IndexMetaWorkingSetEntry>,
944937
prev_inserted_shreds: &'a HashMap<ShredId, Shred>,
945-
leader_schedule_cache: &'a LeaderScheduleCache,
946938
reed_solomon_cache: &'a ReedSolomonCache,
947939
) -> impl Iterator<Item = Vec<Shred>> + 'a {
948940
// Recovery rules:
@@ -964,7 +956,6 @@ impl Blockstore {
964956
index,
965957
erasure_meta,
966958
prev_inserted_shreds,
967-
leader_schedule_cache,
968959
reed_solomon_cache,
969960
)
970961
})?
@@ -987,49 +978,46 @@ impl Blockstore {
987978
metrics: &mut BlockstoreInsertionMetrics,
988979
) {
989980
let mut start = Measure::start("Shred recovery");
990-
if let Some(leader_schedule_cache) = leader_schedule {
991-
let mut recovered_shreds = Vec::new();
992-
let recovered_data_shreds: Vec<_> = self
993-
.try_shred_recovery(
994-
&shred_insertion_tracker.erasure_metas,
995-
&shred_insertion_tracker.index_working_set,
996-
&shred_insertion_tracker.just_inserted_shreds,
997-
leader_schedule_cache,
998-
reed_solomon_cache,
999-
)
1000-
.map(|mut shreds| {
1001-
// All shreds should be retransmitted, but because there
1002-
// are no more missing data shreds in the erasure batch,
1003-
// coding shreds are not stored in blockstore.
1004-
recovered_shreds
1005-
.extend(shred::drain_coding_shreds(&mut shreds).map(Shred::into_payload));
1006-
recovered_shreds.extend(shreds.iter().map(Shred::payload).cloned());
1007-
shreds
1008-
})
1009-
.collect();
1010-
if !recovered_shreds.is_empty() {
1011-
let _ = retransmit_sender.send(recovered_shreds);
1012-
}
1013-
for shred in recovered_data_shreds.into_iter().flatten() {
1014-
metrics.num_recovered += 1;
1015-
*match self.check_insert_data_shred(
1016-
shred,
1017-
shred_insertion_tracker,
1018-
is_trusted,
1019-
leader_schedule,
1020-
ShredSource::Recovered,
1021-
) {
1022-
Err(InsertDataShredError::Exists) => &mut metrics.num_recovered_exists,
1023-
Err(InsertDataShredError::InvalidShred) => {
1024-
&mut metrics.num_recovered_failed_invalid
1025-
}
1026-
Err(InsertDataShredError::BlockstoreError(err)) => {
1027-
error!("blockstore error: {err}");
1028-
&mut metrics.num_recovered_blockstore_error
1029-
}
1030-
Ok(()) => &mut metrics.num_recovered_inserted,
1031-
} += 1;
1032-
}
981+
let mut recovered_shreds = Vec::new();
982+
let recovered_data_shreds: Vec<_> = self
983+
.try_shred_recovery(
984+
&shred_insertion_tracker.erasure_metas,
985+
&shred_insertion_tracker.index_working_set,
986+
&shred_insertion_tracker.just_inserted_shreds,
987+
reed_solomon_cache,
988+
)
989+
.map(|mut shreds| {
990+
// All shreds should be retransmitted, but because there are no
991+
// more missing data shreds in the erasure batch, coding shreds
992+
// are not stored in blockstore.
993+
recovered_shreds
994+
.extend(shred::drain_coding_shreds(&mut shreds).map(Shred::into_payload));
995+
recovered_shreds.extend(shreds.iter().map(Shred::payload).cloned());
996+
shreds
997+
})
998+
.collect();
999+
if !recovered_shreds.is_empty() {
1000+
let _ = retransmit_sender.send(recovered_shreds);
1001+
}
1002+
for shred in recovered_data_shreds.into_iter().flatten() {
1003+
metrics.num_recovered += 1;
1004+
*match self.check_insert_data_shred(
1005+
shred,
1006+
shred_insertion_tracker,
1007+
is_trusted,
1008+
leader_schedule,
1009+
ShredSource::Recovered,
1010+
) {
1011+
Err(InsertDataShredError::Exists) => &mut metrics.num_recovered_exists,
1012+
Err(InsertDataShredError::InvalidShred) => {
1013+
&mut metrics.num_recovered_failed_invalid
1014+
}
1015+
Err(InsertDataShredError::BlockstoreError(err)) => {
1016+
error!("blockstore error: {err}");
1017+
&mut metrics.num_recovered_blockstore_error
1018+
}
1019+
Ok(()) => &mut metrics.num_recovered_inserted,
1020+
} += 1;
10331021
}
10341022
start.stop();
10351023
metrics.shred_recovery_elapsed_us += start.as_us();

ledger/src/shred.rs

+22-43
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,10 @@ pub(crate) use self::shred_code::MAX_CODE_SHREDS_PER_SLOT;
5555
use {
5656
self::{shred_code::ShredCode, traits::Shred as _},
5757
crate::blockstore::{self, MAX_DATA_SHREDS_PER_SLOT},
58+
assert_matches::debug_assert_matches,
5859
bitflags::bitflags,
5960
num_enum::{IntoPrimitive, TryFromPrimitive},
6061
rayon::ThreadPool,
61-
reed_solomon_erasure::Error::TooFewShardsPresent,
6262
serde::{Deserialize, Serialize},
6363
solana_entry::entry::{create_ticks, Entry},
6464
solana_perf::packet::Packet,
@@ -1107,50 +1107,29 @@ impl TryFrom<u8> for ShredVariant {
11071107
}
11081108

11091109
pub(crate) fn recover(
1110-
shreds: Vec<Shred>,
1110+
shreds: impl IntoIterator<Item = Shred>,
11111111
reed_solomon_cache: &ReedSolomonCache,
1112-
get_slot_leader: impl Fn(Slot) -> Option<Pubkey>,
11131112
) -> Result<Vec<Shred>, Error> {
1114-
match shreds
1115-
.first()
1116-
.ok_or(TooFewShardsPresent)?
1117-
.common_header()
1118-
.shred_variant
1119-
{
1120-
ShredVariant::LegacyData | ShredVariant::LegacyCode => {
1121-
let mut shreds = Shredder::try_recovery(shreds, reed_solomon_cache)?;
1122-
shreds.retain(|shred| {
1123-
get_slot_leader(shred.slot())
1124-
.map(|pubkey| shred.verify(&pubkey))
1125-
.unwrap_or_default()
1126-
});
1127-
Ok(shreds)
1128-
}
1129-
ShredVariant::MerkleCode { .. } | ShredVariant::MerkleData { .. } => {
1130-
let shreds = shreds
1131-
.into_iter()
1132-
.map(merkle::Shred::try_from)
1133-
.collect::<Result<_, _>>()?;
1134-
// With Merkle shreds, leader signs the Merkle root of the erasure
1135-
// batch and all shreds within the same erasure batch have the same
1136-
// signature.
1137-
// For recovered shreds, the (unique) signature is copied from
1138-
// shreds which were received from turbine (or repair) and are
1139-
// already sig-verified.
1140-
// The same signature also verifies for recovered shreds because
1141-
// when reconstructing the Merkle tree for the erasure batch, we
1142-
// will obtain the same Merkle root.
1143-
merkle::recover(shreds, reed_solomon_cache)?
1144-
.map(|shred| {
1145-
let shred = Shred::from(shred?);
1146-
debug_assert!(get_slot_leader(shred.slot())
1147-
.map(|pubkey| shred.verify(&pubkey))
1148-
.unwrap_or_default());
1149-
Ok(shred)
1150-
})
1151-
.collect()
1152-
}
1153-
}
1113+
let shreds = shreds
1114+
.into_iter()
1115+
.map(|shred| {
1116+
debug_assert_matches!(
1117+
shred.common_header().shred_variant,
1118+
ShredVariant::MerkleCode { .. } | ShredVariant::MerkleData { .. }
1119+
);
1120+
merkle::Shred::try_from(shred)
1121+
})
1122+
.collect::<Result<_, _>>()?;
1123+
// With Merkle shreds, leader signs the Merkle root of the erasure batch
1124+
// and all shreds within the same erasure batch have the same signature.
1125+
// For recovered shreds, the (unique) signature is copied from shreds which
1126+
// were received from turbine (or repair) and are already sig-verified.
1127+
// The same signature also verifies for recovered shreds because when
1128+
// reconstructing the Merkle tree for the erasure batch, we will obtain the
1129+
// same Merkle root.
1130+
merkle::recover(shreds, reed_solomon_cache)?
1131+
.map(|shred| Ok(Shred::from(shred?)))
1132+
.collect()
11541133
}
11551134

11561135
#[allow(clippy::too_many_arguments)]

0 commit comments

Comments
 (0)