Implement new Index Column (#4827)
* Implement new Index Column * Correct slicing of blobs * Mark coding blobs as coding when they're recovered * Prevent broadcast stages from mixing coding and data blobs in blocktree * Mark recovered blobs as present in the index * Fix indexing error in recovery * Fix broken tests, and some bug fixes * increase min stack size for coverage runs
This commit is contained in:
parent
b1a678b2db
commit
a383ea532f
File diff suppressed because it is too large
Load Diff
|
@ -39,6 +39,10 @@ pub mod columns {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// The root column
|
/// The root column
|
||||||
pub struct Root;
|
pub struct Root;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
/// The index column
|
||||||
|
pub struct Index;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Backend: Sized + Send + Sync {
|
pub trait Backend: Sized + Send + Sync {
|
||||||
|
|
|
@ -100,6 +100,25 @@ impl Column<Kvs> for cf::Data {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Column<Kvs> for cf::Index {
|
||||||
|
const NAME: &'static str = super::INDEX_CF;
|
||||||
|
type Index = u64;
|
||||||
|
|
||||||
|
fn key(slot: u64) -> Key {
|
||||||
|
let mut key = Key::default();
|
||||||
|
BigEndian::write_u64(&mut key.0[8..16], slot);
|
||||||
|
key
|
||||||
|
}
|
||||||
|
|
||||||
|
fn index(key: &Key) -> u64 {
|
||||||
|
BigEndian::read_u64(&key.0[8..16])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypedColumn<Kvs> for cf::Index {
|
||||||
|
type Type = crate::blocktree::meta::Index;
|
||||||
|
}
|
||||||
|
|
||||||
impl Column<Kvs> for cf::DeadSlots {
|
impl Column<Kvs> for cf::DeadSlots {
|
||||||
const NAME: &'static str = super::DEAD_SLOTS;
|
const NAME: &'static str = super::DEAD_SLOTS;
|
||||||
type Index = u64;
|
type Index = u64;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::erasure::{NUM_CODING, NUM_DATA};
|
use crate::erasure::{NUM_CODING, NUM_DATA};
|
||||||
use solana_metrics::datapoint;
|
use solana_metrics::datapoint;
|
||||||
use std::borrow::Borrow;
|
use std::{collections::BTreeSet, ops::RangeBounds};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, Eq, PartialEq)]
|
#[derive(Clone, Debug, Default, Deserialize, Serialize, Eq, PartialEq)]
|
||||||
// The Meta column family
|
// The Meta column family
|
||||||
|
@ -27,6 +27,116 @@ pub struct SlotMeta {
|
||||||
pub is_connected: bool,
|
pub is_connected: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
|
||||||
|
/// Index recording presence/absence of blobs
|
||||||
|
pub struct Index {
|
||||||
|
pub slot: u64,
|
||||||
|
data: DataIndex,
|
||||||
|
coding: CodingIndex,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
|
||||||
|
pub struct DataIndex {
|
||||||
|
/// Map representing presence/absence of data blobs
|
||||||
|
index: BTreeSet<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
|
||||||
|
/// Erasure coding information
|
||||||
|
pub struct CodingIndex {
|
||||||
|
/// Map from set index, to hashmap from blob index to presence bool
|
||||||
|
index: BTreeSet<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, Eq, PartialEq)]
|
||||||
|
/// Erasure coding information
|
||||||
|
pub struct ErasureMeta {
|
||||||
|
/// Which erasure set in the slot this is
|
||||||
|
pub set_index: u64,
|
||||||
|
/// Size of shards in this erasure set
|
||||||
|
pub size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum ErasureMetaStatus {
|
||||||
|
CanRecover,
|
||||||
|
DataFull,
|
||||||
|
StillNeed(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index {
|
||||||
|
pub(in crate::blocktree) fn new(slot: u64) -> Self {
|
||||||
|
Index {
|
||||||
|
slot,
|
||||||
|
data: DataIndex::default(),
|
||||||
|
coding: CodingIndex::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data(&self) -> &DataIndex {
|
||||||
|
&self.data
|
||||||
|
}
|
||||||
|
pub fn coding(&self) -> &CodingIndex {
|
||||||
|
&self.coding
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data_mut(&mut self) -> &mut DataIndex {
|
||||||
|
&mut self.data
|
||||||
|
}
|
||||||
|
pub fn coding_mut(&mut self) -> &mut CodingIndex {
|
||||||
|
&mut self.coding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO: Mark: Change this when coding
|
||||||
|
impl CodingIndex {
|
||||||
|
pub fn present_in_bounds(&self, bounds: impl RangeBounds<u64>) -> usize {
|
||||||
|
self.index.range(bounds).count()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_present(&self, index: u64) -> bool {
|
||||||
|
self.index.contains(&index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_present(&mut self, index: u64, presence: bool) {
|
||||||
|
if presence {
|
||||||
|
self.index.insert(index);
|
||||||
|
} else {
|
||||||
|
self.index.remove(&index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_many_present(&mut self, presence: impl IntoIterator<Item = (u64, bool)>) {
|
||||||
|
for (idx, present) in presence.into_iter() {
|
||||||
|
self.set_present(idx, present);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataIndex {
|
||||||
|
pub fn present_in_bounds(&self, bounds: impl RangeBounds<u64>) -> usize {
|
||||||
|
self.index.range(bounds).count()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_present(&self, index: u64) -> bool {
|
||||||
|
self.index.contains(&index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_present(&mut self, index: u64, presence: bool) {
|
||||||
|
if presence {
|
||||||
|
self.index.insert(index);
|
||||||
|
} else {
|
||||||
|
self.index.remove(&index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_many_present(&mut self, presence: impl IntoIterator<Item = (u64, bool)>) {
|
||||||
|
for (idx, present) in presence.into_iter() {
|
||||||
|
self.set_present(idx, present);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SlotMeta {
|
impl SlotMeta {
|
||||||
pub fn is_full(&self) -> bool {
|
pub fn is_full(&self) -> bool {
|
||||||
// last_index is std::u64::MAX when it has no information about how
|
// last_index is std::u64::MAX when it has no information about how
|
||||||
|
@ -72,62 +182,30 @@ impl SlotMeta {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, Eq, PartialEq)]
|
|
||||||
/// Erasure coding information
|
|
||||||
pub struct ErasureMeta {
|
|
||||||
/// Which erasure set in the slot this is
|
|
||||||
pub set_index: u64,
|
|
||||||
/// Size of shards in this erasure set
|
|
||||||
size: usize,
|
|
||||||
/// Bitfield representing presence/absence of data blobs
|
|
||||||
data: u64,
|
|
||||||
/// Bitfield representing presence/absence of coding blobs
|
|
||||||
coding: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub enum ErasureMetaStatus {
|
|
||||||
CanRecover,
|
|
||||||
DataFull,
|
|
||||||
StillNeed(usize),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ErasureMeta {
|
impl ErasureMeta {
|
||||||
pub fn new(set_index: u64) -> ErasureMeta {
|
pub fn new(set_index: u64) -> ErasureMeta {
|
||||||
ErasureMeta {
|
ErasureMeta { set_index, size: 0 }
|
||||||
set_index,
|
|
||||||
size: 0,
|
|
||||||
data: 0,
|
|
||||||
coding: 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn status(&self) -> ErasureMetaStatus {
|
pub fn status(&self, index: &Index) -> ErasureMetaStatus {
|
||||||
let (data_missing, coding_missing) =
|
use ErasureMetaStatus::*;
|
||||||
(NUM_DATA - self.num_data(), NUM_CODING - self.num_coding());
|
|
||||||
if data_missing > 0 && data_missing + coding_missing <= NUM_CODING {
|
let start_idx = self.start_index();
|
||||||
//assert!(self.size != 0);
|
let (data_end_idx, coding_end_idx) = self.end_indexes();
|
||||||
ErasureMetaStatus::CanRecover
|
|
||||||
|
let num_coding = index.coding().present_in_bounds(start_idx..coding_end_idx);
|
||||||
|
let num_data = index.data().present_in_bounds(start_idx..data_end_idx);
|
||||||
|
|
||||||
|
let (data_missing, coding_missing) = (NUM_DATA - num_data, NUM_CODING - num_coding);
|
||||||
|
|
||||||
|
let total_missing = data_missing + coding_missing;
|
||||||
|
|
||||||
|
if data_missing > 0 && total_missing <= NUM_CODING {
|
||||||
|
CanRecover
|
||||||
} else if data_missing == 0 {
|
} else if data_missing == 0 {
|
||||||
ErasureMetaStatus::DataFull
|
DataFull
|
||||||
} else {
|
} else {
|
||||||
ErasureMetaStatus::StillNeed(data_missing + coding_missing - NUM_CODING)
|
StillNeed(total_missing - NUM_CODING)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn num_coding(&self) -> usize {
|
|
||||||
self.coding.count_ones() as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn num_data(&self) -> usize {
|
|
||||||
self.data.count_ones() as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_coding_present(&self, index: u64) -> bool {
|
|
||||||
if let Some(position) = self.data_index_in_set(index) {
|
|
||||||
self.coding & (1 << position) != 0
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,72 +217,10 @@ impl ErasureMeta {
|
||||||
self.size
|
self.size
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_coding_present(&mut self, index: u64, present: bool) {
|
|
||||||
if let Some(position) = self.data_index_in_set(index) {
|
|
||||||
if present {
|
|
||||||
self.coding |= 1 << position;
|
|
||||||
} else {
|
|
||||||
self.coding &= !(1 << position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_data_present(&self, index: u64) -> bool {
|
|
||||||
if let Some(position) = self.data_index_in_set(index) {
|
|
||||||
self.data & (1 << position) != 0
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_data_present(&mut self, index: u64, present: bool) {
|
|
||||||
if let Some(position) = self.data_index_in_set(index) {
|
|
||||||
if present {
|
|
||||||
self.data |= 1 << position;
|
|
||||||
} else {
|
|
||||||
self.data &= !(1 << position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_data_multi<I, Idx>(&mut self, indexes: I, present: bool)
|
|
||||||
where
|
|
||||||
I: IntoIterator<Item = Idx>,
|
|
||||||
Idx: Borrow<u64>,
|
|
||||||
{
|
|
||||||
for index in indexes.into_iter() {
|
|
||||||
self.set_data_present(*index.borrow(), present);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_coding_multi<I, Idx>(&mut self, indexes: I, present: bool)
|
|
||||||
where
|
|
||||||
I: IntoIterator<Item = Idx>,
|
|
||||||
Idx: Borrow<u64>,
|
|
||||||
{
|
|
||||||
for index in indexes.into_iter() {
|
|
||||||
self.set_coding_present(*index.borrow(), present);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_index_for(index: u64) -> u64 {
|
pub fn set_index_for(index: u64) -> u64 {
|
||||||
index / NUM_DATA as u64
|
index / NUM_DATA as u64
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn data_index_in_set(&self, index: u64) -> Option<u64> {
|
|
||||||
let set_index = Self::set_index_for(index);
|
|
||||||
|
|
||||||
if set_index == self.set_index {
|
|
||||||
Some(index - self.start_index())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn coding_index_in_set(&self, index: u64) -> Option<u64> {
|
|
||||||
self.data_index_in_set(index).map(|i| i + NUM_DATA as u64)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start_index(&self) -> u64 {
|
pub fn start_index(&self) -> u64 {
|
||||||
self.set_index * NUM_DATA as u64
|
self.set_index * NUM_DATA as u64
|
||||||
}
|
}
|
||||||
|
@ -216,130 +232,59 @@ impl ErasureMeta {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[cfg(test)]
|
||||||
fn test_meta_indexes() {
|
mod test {
|
||||||
use rand::{thread_rng, Rng};
|
use super::*;
|
||||||
// to avoid casts everywhere
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
const NUM_DATA: u64 = crate::erasure::NUM_DATA as u64;
|
use std::iter::repeat;
|
||||||
|
|
||||||
let mut rng = thread_rng();
|
|
||||||
|
|
||||||
for _ in 0..100 {
|
|
||||||
let set_index = rng.gen_range(0, 1_000);
|
|
||||||
let blob_index = (set_index * NUM_DATA) + rng.gen_range(0, NUM_DATA);
|
|
||||||
|
|
||||||
assert_eq!(set_index, ErasureMeta::set_index_for(blob_index));
|
|
||||||
let e_meta = ErasureMeta::new(set_index);
|
|
||||||
|
|
||||||
assert_eq!(e_meta.start_index(), set_index * NUM_DATA);
|
|
||||||
let (data_end_idx, coding_end_idx) = e_meta.end_indexes();
|
|
||||||
assert_eq!(data_end_idx, (set_index + 1) * NUM_DATA);
|
|
||||||
assert_eq!(coding_end_idx, set_index * NUM_DATA + NUM_CODING as u64);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut e_meta = ErasureMeta::new(0);
|
|
||||||
|
|
||||||
assert_eq!(e_meta.data_index_in_set(0), Some(0));
|
|
||||||
assert_eq!(e_meta.data_index_in_set(NUM_DATA / 2), Some(NUM_DATA / 2));
|
|
||||||
assert_eq!(e_meta.data_index_in_set(NUM_DATA - 1), Some(NUM_DATA - 1));
|
|
||||||
assert_eq!(e_meta.data_index_in_set(NUM_DATA), None);
|
|
||||||
assert_eq!(e_meta.data_index_in_set(std::u64::MAX), None);
|
|
||||||
|
|
||||||
e_meta.set_index = 1;
|
|
||||||
|
|
||||||
assert_eq!(e_meta.data_index_in_set(0), None);
|
|
||||||
assert_eq!(e_meta.data_index_in_set(NUM_DATA - 1), None);
|
|
||||||
assert_eq!(e_meta.data_index_in_set(NUM_DATA), Some(0));
|
|
||||||
assert_eq!(
|
|
||||||
e_meta.data_index_in_set(NUM_DATA * 2 - 1),
|
|
||||||
Some(NUM_DATA - 1)
|
|
||||||
);
|
|
||||||
assert_eq!(e_meta.data_index_in_set(std::u64::MAX), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_meta_coding_present() {
|
|
||||||
let mut e_meta = ErasureMeta::default();
|
|
||||||
|
|
||||||
e_meta.set_coding_multi(0..NUM_CODING as u64, true);
|
|
||||||
for i in 0..NUM_CODING as u64 {
|
|
||||||
assert_eq!(e_meta.is_coding_present(i), true);
|
|
||||||
}
|
|
||||||
for i in NUM_CODING as u64..NUM_DATA as u64 {
|
|
||||||
assert_eq!(e_meta.is_coding_present(i), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
e_meta.set_index = ErasureMeta::set_index_for((NUM_DATA * 17) as u64);
|
|
||||||
let start_idx = e_meta.start_index();
|
|
||||||
e_meta.set_coding_multi(start_idx..start_idx + NUM_CODING as u64, true);
|
|
||||||
|
|
||||||
for i in start_idx..start_idx + NUM_CODING as u64 {
|
|
||||||
e_meta.set_coding_present(i, true);
|
|
||||||
assert_eq!(e_meta.is_coding_present(i), true);
|
|
||||||
}
|
|
||||||
for i in start_idx + NUM_CODING as u64..start_idx + NUM_DATA as u64 {
|
|
||||||
assert_eq!(e_meta.is_coding_present(i), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_erasure_meta_status() {
|
fn test_erasure_meta_status() {
|
||||||
use rand::{seq::SliceRandom, thread_rng};
|
use ErasureMetaStatus::*;
|
||||||
// Local constansts just used to avoid repetitive casts
|
|
||||||
const N_DATA: u64 = crate::erasure::NUM_DATA as u64;
|
|
||||||
const N_CODING: u64 = crate::erasure::NUM_CODING as u64;
|
|
||||||
|
|
||||||
let mut e_meta = ErasureMeta::default();
|
let set_index = 0;
|
||||||
|
|
||||||
|
let mut e_meta = ErasureMeta::new(set_index);
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
let data_indexes: Vec<u64> = (0..N_DATA).collect();
|
let mut index = Index::new(0);
|
||||||
let coding_indexes: Vec<u64> = (0..N_CODING).collect();
|
|
||||||
|
|
||||||
assert_eq!(e_meta.status(), ErasureMetaStatus::StillNeed(NUM_DATA));
|
|
||||||
|
|
||||||
e_meta.set_data_multi(0..N_DATA, true);
|
|
||||||
|
|
||||||
assert_eq!(e_meta.status(), ErasureMetaStatus::DataFull);
|
|
||||||
|
|
||||||
e_meta.size = 1;
|
e_meta.size = 1;
|
||||||
e_meta.set_coding_multi(0..N_CODING, true);
|
|
||||||
|
|
||||||
assert_eq!(e_meta.status(), ErasureMetaStatus::DataFull);
|
let data_indexes = 0..NUM_DATA as u64;
|
||||||
|
let coding_indexes = 0..NUM_CODING as u64;
|
||||||
|
|
||||||
for &idx in data_indexes.choose_multiple(&mut rng, NUM_CODING) {
|
assert_eq!(e_meta.status(&index), StillNeed(NUM_DATA));
|
||||||
e_meta.set_data_present(idx, false);
|
|
||||||
|
|
||||||
assert_eq!(e_meta.status(), ErasureMetaStatus::CanRecover);
|
index
|
||||||
|
.data_mut()
|
||||||
|
.set_many_present(data_indexes.clone().zip(repeat(true)));
|
||||||
|
|
||||||
|
assert_eq!(e_meta.status(&index), DataFull);
|
||||||
|
|
||||||
|
index
|
||||||
|
.coding_mut()
|
||||||
|
.set_many_present(coding_indexes.clone().zip(repeat(true)));
|
||||||
|
|
||||||
|
for &idx in data_indexes
|
||||||
|
.clone()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.choose_multiple(&mut rng, NUM_DATA)
|
||||||
|
{
|
||||||
|
index.data_mut().set_present(idx, false);
|
||||||
|
|
||||||
|
assert_eq!(e_meta.status(&index), CanRecover);
|
||||||
}
|
}
|
||||||
|
|
||||||
e_meta.set_data_multi(0..N_DATA, true);
|
index
|
||||||
|
.data_mut()
|
||||||
|
.set_many_present(data_indexes.zip(repeat(true)));
|
||||||
|
|
||||||
for &idx in coding_indexes.choose_multiple(&mut rng, NUM_CODING) {
|
for &idx in coding_indexes
|
||||||
e_meta.set_coding_present(idx, false);
|
.collect::<Vec<_>>()
|
||||||
|
.choose_multiple(&mut rng, NUM_CODING)
|
||||||
|
{
|
||||||
|
index.coding_mut().set_present(idx, false);
|
||||||
|
|
||||||
assert_eq!(e_meta.status(), ErasureMetaStatus::DataFull);
|
assert_eq!(e_meta.status(&index), DataFull);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_meta_data_present() {
|
|
||||||
let mut e_meta = ErasureMeta::default();
|
|
||||||
|
|
||||||
e_meta.set_data_multi(0..NUM_DATA as u64, true);
|
|
||||||
for i in 0..NUM_DATA as u64 {
|
|
||||||
assert_eq!(e_meta.is_data_present(i), true);
|
|
||||||
}
|
|
||||||
for i in NUM_DATA as u64..2 * NUM_DATA as u64 {
|
|
||||||
assert_eq!(e_meta.is_data_present(i), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
e_meta.set_index = ErasureMeta::set_index_for((NUM_DATA * 23) as u64);
|
|
||||||
let start_idx = e_meta.start_index();
|
|
||||||
e_meta.set_data_multi(start_idx..start_idx + NUM_DATA as u64, true);
|
|
||||||
|
|
||||||
for i in start_idx..start_idx + NUM_DATA as u64 {
|
|
||||||
assert_eq!(e_meta.is_data_present(i), true);
|
|
||||||
}
|
|
||||||
for i in start_idx - NUM_DATA as u64..start_idx {
|
|
||||||
assert_eq!(e_meta.is_data_present(i), false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,9 @@ impl Backend for Rocks {
|
||||||
type Error = rocksdb::Error;
|
type Error = rocksdb::Error;
|
||||||
|
|
||||||
fn open(path: &Path) -> Result<Rocks> {
|
fn open(path: &Path) -> Result<Rocks> {
|
||||||
use crate::blocktree::db::columns::{Coding, Data, DeadSlots, ErasureMeta, Orphans, Root, SlotMeta};
|
use crate::blocktree::db::columns::{
|
||||||
|
Coding, Data, DeadSlots, ErasureMeta, Index, Orphans, Root, SlotMeta,
|
||||||
|
};
|
||||||
|
|
||||||
fs::create_dir_all(&path)?;
|
fs::create_dir_all(&path)?;
|
||||||
|
|
||||||
|
@ -40,12 +42,14 @@ impl Backend for Rocks {
|
||||||
// Column family names
|
// Column family names
|
||||||
let meta_cf_descriptor = ColumnFamilyDescriptor::new(SlotMeta::NAME, get_cf_options());
|
let meta_cf_descriptor = ColumnFamilyDescriptor::new(SlotMeta::NAME, get_cf_options());
|
||||||
let data_cf_descriptor = ColumnFamilyDescriptor::new(Data::NAME, get_cf_options());
|
let data_cf_descriptor = ColumnFamilyDescriptor::new(Data::NAME, get_cf_options());
|
||||||
let dead_slots_cf_descriptor = ColumnFamilyDescriptor::new(DeadSlots::NAME, get_cf_options());
|
let dead_slots_cf_descriptor =
|
||||||
|
ColumnFamilyDescriptor::new(DeadSlots::NAME, get_cf_options());
|
||||||
let erasure_cf_descriptor = ColumnFamilyDescriptor::new(Coding::NAME, get_cf_options());
|
let erasure_cf_descriptor = ColumnFamilyDescriptor::new(Coding::NAME, get_cf_options());
|
||||||
let erasure_meta_cf_descriptor =
|
let erasure_meta_cf_descriptor =
|
||||||
ColumnFamilyDescriptor::new(ErasureMeta::NAME, get_cf_options());
|
ColumnFamilyDescriptor::new(ErasureMeta::NAME, get_cf_options());
|
||||||
let orphans_cf_descriptor = ColumnFamilyDescriptor::new(Orphans::NAME, get_cf_options());
|
let orphans_cf_descriptor = ColumnFamilyDescriptor::new(Orphans::NAME, get_cf_options());
|
||||||
let root_cf_descriptor = ColumnFamilyDescriptor::new(Root::NAME, get_cf_options());
|
let root_cf_descriptor = ColumnFamilyDescriptor::new(Root::NAME, get_cf_options());
|
||||||
|
let index_cf_descriptor = ColumnFamilyDescriptor::new(Index::NAME, get_cf_options());
|
||||||
|
|
||||||
let cfs = vec![
|
let cfs = vec![
|
||||||
meta_cf_descriptor,
|
meta_cf_descriptor,
|
||||||
|
@ -55,6 +59,7 @@ impl Backend for Rocks {
|
||||||
erasure_meta_cf_descriptor,
|
erasure_meta_cf_descriptor,
|
||||||
orphans_cf_descriptor,
|
orphans_cf_descriptor,
|
||||||
root_cf_descriptor,
|
root_cf_descriptor,
|
||||||
|
index_cf_descriptor,
|
||||||
];
|
];
|
||||||
|
|
||||||
// Open the database
|
// Open the database
|
||||||
|
@ -64,13 +69,16 @@ impl Backend for Rocks {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn columns(&self) -> Vec<&'static str> {
|
fn columns(&self) -> Vec<&'static str> {
|
||||||
use crate::blocktree::db::columns::{Coding, Data, DeadSlots, ErasureMeta, Orphans, Root, SlotMeta};
|
use crate::blocktree::db::columns::{
|
||||||
|
Coding, Data, DeadSlots, ErasureMeta, Index, Orphans, Root, SlotMeta,
|
||||||
|
};
|
||||||
|
|
||||||
vec![
|
vec![
|
||||||
Coding::NAME,
|
Coding::NAME,
|
||||||
ErasureMeta::NAME,
|
ErasureMeta::NAME,
|
||||||
DeadSlots::NAME,
|
DeadSlots::NAME,
|
||||||
Data::NAME,
|
Data::NAME,
|
||||||
|
Index::NAME,
|
||||||
Orphans::NAME,
|
Orphans::NAME,
|
||||||
Root::NAME,
|
Root::NAME,
|
||||||
SlotMeta::NAME,
|
SlotMeta::NAME,
|
||||||
|
@ -164,6 +172,25 @@ impl Column<Rocks> for cf::Data {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Column<Rocks> for cf::Index {
|
||||||
|
const NAME: &'static str = super::INDEX_CF;
|
||||||
|
type Index = u64;
|
||||||
|
|
||||||
|
fn key(slot: u64) -> Vec<u8> {
|
||||||
|
let mut key = vec![0; 8];
|
||||||
|
BigEndian::write_u64(&mut key[..], slot);
|
||||||
|
key
|
||||||
|
}
|
||||||
|
|
||||||
|
fn index(key: &[u8]) -> u64 {
|
||||||
|
BigEndian::read_u64(&key[..8])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypedColumn<Rocks> for cf::Index {
|
||||||
|
type Type = crate::blocktree::meta::Index;
|
||||||
|
}
|
||||||
|
|
||||||
impl Column<Rocks> for cf::DeadSlots {
|
impl Column<Rocks> for cf::DeadSlots {
|
||||||
const NAME: &'static str = super::DEAD_SLOTS_CF;
|
const NAME: &'static str = super::DEAD_SLOTS_CF;
|
||||||
type Index = u64;
|
type Index = u64;
|
||||||
|
|
|
@ -65,7 +65,8 @@ impl BroadcastRun for BroadcastBadBlobSizes {
|
||||||
w_b.meta.size = real_size;
|
w_b.meta.size = real_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
blocktree.write_shared_blobs(data_blobs.iter().chain(coding_blobs.iter()))?;
|
blocktree.write_shared_blobs(data_blobs.iter())?;
|
||||||
|
blocktree.put_shared_coding_blobs(coding_blobs.iter())?;
|
||||||
|
|
||||||
// 3) Start broadcast step
|
// 3) Start broadcast step
|
||||||
let bank_epoch = bank.get_stakers_epoch(bank.slot());
|
let bank_epoch = bank.get_stakers_epoch(bank.slot());
|
||||||
|
|
|
@ -73,7 +73,8 @@ impl BroadcastRun for BroadcastFakeBlobsRun {
|
||||||
self.last_blockhash = Hash::default();
|
self.last_blockhash = Hash::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
blocktree.write_shared_blobs(data_blobs.iter().chain(coding_blobs.iter()))?;
|
blocktree.write_shared_blobs(data_blobs.iter())?;
|
||||||
|
blocktree.put_shared_coding_blobs(coding_blobs.iter())?;
|
||||||
|
|
||||||
// Set the forwarded flag to true, so that the blobs won't be forwarded to peers
|
// Set the forwarded flag to true, so that the blobs won't be forwarded to peers
|
||||||
data_blobs
|
data_blobs
|
||||||
|
|
|
@ -52,7 +52,8 @@ impl BroadcastRun for FailEntryVerificationBroadcastRun {
|
||||||
&mut broadcast.coding_generator,
|
&mut broadcast.coding_generator,
|
||||||
);
|
);
|
||||||
|
|
||||||
blocktree.write_shared_blobs(data_blobs.iter().chain(coding_blobs.iter()))?;
|
blocktree.write_shared_blobs(data_blobs.iter())?;
|
||||||
|
blocktree.put_shared_coding_blobs(coding_blobs.iter())?;
|
||||||
|
|
||||||
// 3) Start broadcast step
|
// 3) Start broadcast step
|
||||||
let bank_epoch = bank.get_stakers_epoch(bank.slot());
|
let bank_epoch = bank.get_stakers_epoch(bank.slot());
|
||||||
|
|
|
@ -82,7 +82,9 @@ impl BroadcastRun for StandardBroadcastRun {
|
||||||
&mut broadcast.coding_generator,
|
&mut broadcast.coding_generator,
|
||||||
);
|
);
|
||||||
|
|
||||||
blocktree.write_shared_blobs(data_blobs.iter().chain(coding_blobs.iter()))?;
|
blocktree.write_shared_blobs(data_blobs.iter())?;
|
||||||
|
blocktree.put_shared_coding_blobs(coding_blobs.iter())?;
|
||||||
|
|
||||||
let to_blobs_elapsed = to_blobs_start.elapsed();
|
let to_blobs_elapsed = to_blobs_start.elapsed();
|
||||||
|
|
||||||
// 3) Start broadcast step
|
// 3) Start broadcast step
|
||||||
|
|
|
@ -138,23 +138,25 @@ impl Session {
|
||||||
|
|
||||||
if n < NUM_DATA {
|
if n < NUM_DATA {
|
||||||
let mut blob = Blob::new(&blocks[n]);
|
let mut blob = Blob::new(&blocks[n]);
|
||||||
|
blob.meta.size = blob.data_size() as usize;
|
||||||
|
|
||||||
data_size = blob.data_size() as usize - BLOB_HEADER_SIZE;
|
data_size = blob.data_size() as usize;
|
||||||
idx = n as u64 + block_start_idx;
|
idx = n as u64 + block_start_idx;
|
||||||
first_byte = blob.data[0];
|
first_byte = blob.data[0];
|
||||||
|
|
||||||
blob.set_size(data_size);
|
|
||||||
recovered_data.push(blob);
|
recovered_data.push(blob);
|
||||||
} else {
|
} else {
|
||||||
let mut blob = Blob::default();
|
let mut blob = Blob::default();
|
||||||
blob.data_mut()[..size].copy_from_slice(&blocks[n]);
|
blob.data[BLOB_HEADER_SIZE..BLOB_HEADER_SIZE + size].copy_from_slice(&blocks[n]);
|
||||||
|
blob.meta.size = size;
|
||||||
|
|
||||||
data_size = size;
|
data_size = size;
|
||||||
idx = (n as u64 + block_start_idx) - NUM_DATA as u64;
|
idx = n as u64 + block_start_idx - NUM_DATA as u64;
|
||||||
first_byte = blob.data[0];
|
first_byte = blob.data[0];
|
||||||
|
|
||||||
blob.set_slot(slot);
|
blob.set_slot(slot);
|
||||||
blob.set_index(idx);
|
blob.set_index(idx);
|
||||||
blob.set_size(data_size);
|
blob.set_coding();
|
||||||
recovered_coding.push(blob);
|
recovered_coding.push(blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,7 +242,7 @@ impl CodingGenerator {
|
||||||
if {
|
if {
|
||||||
let mut coding_ptrs: Vec<_> = coding_blobs
|
let mut coding_ptrs: Vec<_> = coding_blobs
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.map(|blob| &mut blob.data_mut()[..max_data_size])
|
.map(|blob| &mut blob.data[BLOB_HEADER_SIZE..BLOB_HEADER_SIZE + max_data_size])
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
self.session.encode(&data_ptrs, coding_ptrs.as_mut_slice())
|
self.session.encode(&data_ptrs, coding_ptrs.as_mut_slice())
|
||||||
|
@ -427,7 +429,7 @@ pub mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_erasure_generate_coding() {
|
fn test_generate_coding() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
|
|
||||||
// trivial case
|
// trivial case
|
||||||
|
@ -449,10 +451,10 @@ pub mod test {
|
||||||
assert_eq!(coding_blobs.len(), NUM_CODING);
|
assert_eq!(coding_blobs.len(), NUM_CODING);
|
||||||
|
|
||||||
for j in 0..NUM_CODING {
|
for j in 0..NUM_CODING {
|
||||||
assert_eq!(
|
let coding_blob = coding_blobs[j].read().unwrap();
|
||||||
coding_blobs[j].read().unwrap().index(),
|
|
||||||
((i / NUM_DATA) * NUM_DATA + j) as u64
|
//assert_eq!(coding_blob.index(), (i * NUM_DATA + j % NUM_CODING) as u64);
|
||||||
);
|
assert!(coding_blob.is_coding());
|
||||||
}
|
}
|
||||||
test_toss_and_recover(
|
test_toss_and_recover(
|
||||||
&coding_generator.session,
|
&coding_generator.session,
|
||||||
|
@ -654,6 +656,8 @@ pub mod test {
|
||||||
S: Borrow<SlotSpec>,
|
S: Borrow<SlotSpec>,
|
||||||
{
|
{
|
||||||
let mut coding_generator = CodingGenerator::default();
|
let mut coding_generator = CodingGenerator::default();
|
||||||
|
let keypair = Keypair::new();
|
||||||
|
let bytes = keypair.to_bytes();
|
||||||
|
|
||||||
specs.into_iter().map(move |spec| {
|
specs.into_iter().map(move |spec| {
|
||||||
let spec = spec.borrow();
|
let spec = spec.borrow();
|
||||||
|
@ -666,14 +670,14 @@ pub mod test {
|
||||||
let set_index = erasure_spec.set_index as usize;
|
let set_index = erasure_spec.set_index as usize;
|
||||||
let start_index = set_index * NUM_DATA;
|
let start_index = set_index * NUM_DATA;
|
||||||
|
|
||||||
let mut blobs = generate_test_blobs(0, NUM_DATA);
|
let mut blobs = generate_test_blobs(start_index, NUM_DATA);
|
||||||
index_blobs(
|
let keypair = Keypair::from_bytes(&bytes).unwrap();
|
||||||
&blobs,
|
index_blobs(&blobs, &keypair.pubkey(), start_index as u64, slot, 0);
|
||||||
&Keypair::new().pubkey(),
|
|
||||||
start_index as u64,
|
// Signing has to be deferred until all data/header fields are set correctly
|
||||||
slot,
|
blobs.iter().for_each(|blob| {
|
||||||
0,
|
blob.write().unwrap().sign(&keypair);
|
||||||
);
|
});
|
||||||
|
|
||||||
let mut coding_blobs = coding_generator.next(&blobs);
|
let mut coding_blobs = coding_generator.next(&blobs);
|
||||||
|
|
||||||
|
@ -738,9 +742,8 @@ pub mod test {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
let mut blob = Blob::default();
|
let mut blob = Blob::default();
|
||||||
blob.data_mut()[..data.len()].copy_from_slice(&data);
|
blob.data_mut()[..].copy_from_slice(&data);
|
||||||
blob.set_size(data.len());
|
blob.set_size(BLOB_DATA_SIZE);
|
||||||
blob.sign(&Keypair::new());
|
|
||||||
Arc::new(RwLock::new(blob))
|
Arc::new(RwLock::new(blob))
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -771,7 +774,7 @@ pub mod test {
|
||||||
if i < NUM_DATA {
|
if i < NUM_DATA {
|
||||||
&mut blob.data[..size]
|
&mut blob.data[..size]
|
||||||
} else {
|
} else {
|
||||||
&mut blob.data_mut()[..size]
|
&mut blob.data[BLOB_HEADER_SIZE..BLOB_HEADER_SIZE + size]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
|
@ -515,10 +515,10 @@ impl Blob {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn data(&self) -> &[u8] {
|
pub fn data(&self) -> &[u8] {
|
||||||
&self.data[BLOB_HEADER_SIZE..]
|
&self.data[BLOB_HEADER_SIZE..BLOB_HEADER_SIZE + BLOB_DATA_SIZE]
|
||||||
}
|
}
|
||||||
pub fn data_mut(&mut self) -> &mut [u8] {
|
pub fn data_mut(&mut self) -> &mut [u8] {
|
||||||
&mut self.data[BLOB_HEADER_SIZE..]
|
&mut self.data[BLOB_HEADER_SIZE..BLOB_HEADER_SIZE + BLOB_DATA_SIZE]
|
||||||
}
|
}
|
||||||
pub fn size(&self) -> usize {
|
pub fn size(&self) -> usize {
|
||||||
let size = self.data_size() as usize;
|
let size = self.data_size() as usize;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
use crate::blocktree::Blocktree;
|
use crate::blocktree::Blocktree;
|
||||||
use crate::cluster_info::ClusterInfo;
|
use crate::cluster_info::ClusterInfo;
|
||||||
use crate::leader_schedule_cache::LeaderScheduleCache;
|
use crate::leader_schedule_cache::LeaderScheduleCache;
|
||||||
use crate::packet::{Blob, SharedBlob, BLOB_HEADER_SIZE};
|
use crate::packet::{Blob, SharedBlob};
|
||||||
use crate::repair_service::{RepairService, RepairStrategy};
|
use crate::repair_service::{RepairService, RepairStrategy};
|
||||||
use crate::result::{Error, Result};
|
use crate::result::{Error, Result};
|
||||||
use crate::service::Service;
|
use crate::service::Service;
|
||||||
|
@ -28,11 +28,12 @@ pub const NUM_THREADS: u32 = 10;
|
||||||
fn retransmit_blobs(blobs: &[SharedBlob], retransmit: &BlobSender, id: &Pubkey) -> Result<()> {
|
fn retransmit_blobs(blobs: &[SharedBlob], retransmit: &BlobSender, id: &Pubkey) -> Result<()> {
|
||||||
let mut retransmit_queue: Vec<SharedBlob> = Vec::new();
|
let mut retransmit_queue: Vec<SharedBlob> = Vec::new();
|
||||||
for blob in blobs {
|
for blob in blobs {
|
||||||
|
let mut blob_guard = blob.write().unwrap();
|
||||||
// Don't add blobs generated by this node to the retransmit queue
|
// Don't add blobs generated by this node to the retransmit queue
|
||||||
if blob.read().unwrap().id() != *id {
|
if blob_guard.id() != *id && !blob_guard.is_coding() {
|
||||||
let mut w_blob = blob.write().unwrap();
|
//let mut w_blob = blob.write().unwrap();
|
||||||
w_blob.meta.forward = w_blob.should_forward();
|
blob_guard.meta.forward = blob_guard.should_forward();
|
||||||
w_blob.set_forwarded(false);
|
blob_guard.set_forwarded(false);
|
||||||
retransmit_queue.push(blob.clone());
|
retransmit_queue.push(blob.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,29 +53,17 @@ fn retransmit_blobs(blobs: &[SharedBlob], retransmit: &BlobSender, id: &Pubkey)
|
||||||
/// Process a blob: Add blob to the ledger window.
|
/// Process a blob: Add blob to the ledger window.
|
||||||
pub fn process_blobs(blobs: &[SharedBlob], blocktree: &Arc<Blocktree>) -> Result<()> {
|
pub fn process_blobs(blobs: &[SharedBlob], blocktree: &Arc<Blocktree>) -> Result<()> {
|
||||||
// make an iterator for insert_data_blobs()
|
// make an iterator for insert_data_blobs()
|
||||||
let blobs: Vec<_> = blobs.iter().map(move |blob| blob.read().unwrap()).collect();
|
//let blobs: Vec<_> = blobs.iter().map(move |blob| blob.read().unwrap()).collect();
|
||||||
|
|
||||||
blocktree.insert_data_blobs(blobs.iter().filter_map(|blob| {
|
blocktree.write_shared_blobs(
|
||||||
if !blob.is_coding() {
|
blobs
|
||||||
Some(&(**blob))
|
.iter()
|
||||||
} else {
|
.filter(|blob| !blob.read().unwrap().is_coding()),
|
||||||
None
|
|
||||||
}
|
|
||||||
}))?;
|
|
||||||
|
|
||||||
for blob in blobs {
|
|
||||||
// TODO: Once the original leader signature is added to the blob, make sure that
|
|
||||||
// the blob was originally generated by the expected leader for this slot
|
|
||||||
|
|
||||||
// Insert the new blob into block tree
|
|
||||||
if blob.is_coding() {
|
|
||||||
blocktree.put_coding_blob_bytes(
|
|
||||||
blob.slot(),
|
|
||||||
blob.index(),
|
|
||||||
&blob.data[..BLOB_HEADER_SIZE + blob.size()],
|
|
||||||
)?;
|
)?;
|
||||||
}
|
|
||||||
}
|
blocktree
|
||||||
|
.put_shared_coding_blobs(blobs.iter().filter(|blob| blob.read().unwrap().is_coding()))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,6 +204,8 @@ impl WindowService {
|
||||||
let bank_forks = bank_forks.clone();
|
let bank_forks = bank_forks.clone();
|
||||||
let t_window = Builder::new()
|
let t_window = Builder::new()
|
||||||
.name("solana-window".to_string())
|
.name("solana-window".to_string())
|
||||||
|
// TODO: Mark: Why is it overflowing
|
||||||
|
.stack_size(8 * 1024 * 1024)
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
let _exit = Finalizer::new(exit.clone());
|
let _exit = Finalizer::new(exit.clone());
|
||||||
let id = cluster_info.read().unwrap().id();
|
let id = cluster_info.read().unwrap().id();
|
||||||
|
|
|
@ -27,6 +27,7 @@ coverageFlags+=("-Coverflow-checks=off") # Disable overflow checks, which create
|
||||||
export RUSTFLAGS="${coverageFlags[*]}"
|
export RUSTFLAGS="${coverageFlags[*]}"
|
||||||
export CARGO_INCREMENTAL=0
|
export CARGO_INCREMENTAL=0
|
||||||
export RUST_BACKTRACE=1
|
export RUST_BACKTRACE=1
|
||||||
|
export RUST_MIN_STACK=8388608
|
||||||
|
|
||||||
echo "--- remove old coverage results"
|
echo "--- remove old coverage results"
|
||||||
if [[ -d target/cov ]]; then
|
if [[ -d target/cov ]]; then
|
||||||
|
|
Loading…
Reference in New Issue