stop copying Blooms (#3379)

* stop copying Blooms

* fixup

* clippy
This commit is contained in:
Rob Walker 2019-03-20 11:06:39 -07:00 committed by GitHub
parent 13c9d3d4e1
commit df9fd2bc0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 115 additions and 43 deletions

View File

@ -130,14 +130,13 @@ pub fn process_blocktree(
warn!("entry0 not present");
return Err(BlocktreeProcessorError::LedgerVerificationFailed);
}
let entry0 = &entries[0];
let entry0 = entries.remove(0);
if !(entry0.is_tick() && entry0.verify(&last_entry_hash)) {
warn!("Ledger proof of history failed at entry0");
return Err(BlocktreeProcessorError::LedgerVerificationFailed);
}
last_entry_hash = entry0.hash;
entry_height += 1;
entries = entries.drain(1..).collect();
}
if !entries.is_empty() {

BIN
runtime/grow Normal file

Binary file not shown.

View File

@ -247,6 +247,7 @@ impl Bank {
// freeze is a one-way trip, idempotent
*hash = self.hash_internal_state();
}
self.status_cache.write().unwrap().freeze();
}
/// squash the parent's state up into this Bank,

View File

@ -1,11 +1,13 @@
use crate::bloom::{Bloom, BloomHashIndex};
use hashbrown::HashMap;
use log::*;
use solana_sdk::hash::Hash;
use solana_sdk::signature::Signature;
use std::collections::VecDeque;
use std::ops::Deref;
#[cfg(test)]
use std::ops::DerefMut;
use std::sync::Arc;
/// Each cache entry is designed to span ~1 second of signatures
const MAX_CACHE_ENTRIES: usize = solana_sdk::timing::MAX_HASH_AGE_IN_SECONDS;
@ -13,15 +15,51 @@ const MAX_CACHE_ENTRIES: usize = solana_sdk::timing::MAX_HASH_AGE_IN_SECONDS;
type FailureMap<T> = HashMap<Signature, T>;
#[derive(Clone)]
pub struct StatusCache<T> {
/// all signatures seen at this checkpoint
struct Status<T> {
/// all signatures seen during a hash period
signatures: Bloom<Signature>,
/// failures
failures: FailureMap<T>,
}
/// Merges are empty unless this is the root checkpoint which cannot be unrolled
merges: VecDeque<StatusCache<T>>,
impl<T: Clone> Status<T> {
fn new(blockhash: &Hash) -> Self {
let keys = (0..27).map(|i| blockhash.hash_at_index(i)).collect();
Status {
signatures: Bloom::new(38_340_234, keys),
failures: HashMap::default(),
}
}
fn has_signature(&self, sig: &Signature) -> bool {
self.signatures.contains(&sig)
}
fn add(&mut self, sig: &Signature) {
self.signatures.add(&sig);
}
fn clear(&mut self) {
self.failures.clear();
self.signatures.clear();
}
pub fn get_signature_status(&self, sig: &Signature) -> Option<Result<(), T>> {
if let Some(res) = self.failures.get(sig) {
return Some(Err(res.clone()));
} else if self.signatures.contains(sig) {
return Some(Ok(()));
}
None
}
}
#[derive(Clone)]
pub struct StatusCache<T> {
/// currently active status
active: Option<Status<T>>,
/// merges cover previous periods, and are read-only
merges: VecDeque<Arc<Status<T>>>,
}
impl<T: Clone> Default for StatusCache<T> {
@ -32,11 +70,9 @@ impl<T: Clone> Default for StatusCache<T> {
impl<T: Clone> StatusCache<T> {
pub fn new(blockhash: &Hash) -> Self {
let keys = (0..27).map(|i| blockhash.hash_at_index(i)).collect();
Self {
signatures: Bloom::new(38_340_234, keys),
failures: HashMap::new(),
merges: VecDeque::new(),
active: Some(Status::new(blockhash)),
merges: VecDeque::default(),
}
}
fn has_signature_merged(&self, sig: &Signature) -> bool {
@ -49,23 +85,40 @@ impl<T: Clone> StatusCache<T> {
}
/// test if a signature is known
pub fn has_signature(&self, sig: &Signature) -> bool {
self.signatures.contains(&sig) || self.has_signature_merged(sig)
self.active
.as_ref()
.map_or(false, |active| active.has_signature(&sig))
|| self.has_signature_merged(sig)
}
/// add a signature
pub fn add(&mut self, sig: &Signature) {
self.signatures.add(&sig)
if let Some(active) = self.active.as_mut() {
active.add(&sig);
}
}
/// Save an error status for a signature
pub fn save_failure_status(&mut self, sig: &Signature, err: T) {
assert!(self.has_signature(sig), "sig not found");
self.failures.insert(*sig, err);
assert!(
self.active
.as_ref()
.map_or(false, |active| active.has_signature(sig)),
"sig not found"
);
self.active
.as_mut()
.map(|active| active.failures.insert(*sig, err));
}
/// Forget all signatures. Useful for benchmarking.
pub fn clear(&mut self) {
self.failures.clear();
self.signatures.clear();
if let Some(active) = self.active.as_mut() {
active.clear();
}
self.merges = VecDeque::new();
}
fn get_signature_status_merged(&self, sig: &Signature) -> Option<Result<(), T>> {
for c in &self.merges {
if c.has_signature(sig) {
@ -75,32 +128,30 @@ impl<T: Clone> StatusCache<T> {
None
}
pub fn get_signature_status(&self, sig: &Signature) -> Option<Result<(), T>> {
if let Some(res) = self.failures.get(sig) {
return Some(Err(res.clone()));
} else if self.signatures.contains(sig) {
return Some(Ok(()));
}
self.get_signature_status_merged(sig)
self.active
.as_ref()
.and_then(|active| active.get_signature_status(sig))
.or_else(|| self.get_signature_status_merged(sig))
}
fn squash_parent_is_full(&mut self, parent: &Self) -> bool {
// flatten and squash the parent and its merges into self.merges,
// returns true if self is full
self.merges.push_back(StatusCache {
signatures: parent.signatures.clone(),
failures: parent.failures.clone(),
merges: VecDeque::new(),
});
for merge in &parent.merges {
self.merges.push_back(StatusCache {
signatures: merge.signatures.clone(),
failures: merge.failures.clone(),
merges: VecDeque::new(),
});
if parent.active.is_some() {
warn!("=========== FIXME: squash() on an active parent! ================");
}
self.merges.truncate(MAX_CACHE_ENTRIES);
// TODO: put this assert back in
//assert!(parent.active.is_none());
if self.merges.len() < MAX_CACHE_ENTRIES {
for merge in parent
.merges
.iter()
.take(MAX_CACHE_ENTRIES - self.merges.len())
{
self.merges.push_back(merge.clone());
}
}
self.merges.len() == MAX_CACHE_ENTRIES
}
@ -119,15 +170,21 @@ impl<T: Clone> StatusCache<T> {
/// Crate a new cache, pushing the old cache into the merged queue
pub fn new_cache(&mut self, blockhash: &Hash) {
let mut old = Self::new(blockhash);
std::mem::swap(&mut old.signatures, &mut self.signatures);
std::mem::swap(&mut old.failures, &mut self.failures);
assert!(old.merges.is_empty());
self.merges.push_front(old);
assert!(self.active.is_some());
let merge = self.active.replace(Status::new(blockhash));
self.merges.push_front(Arc::new(merge.unwrap()));
if self.merges.len() > MAX_CACHE_ENTRIES {
self.merges.pop_back();
}
}
pub fn freeze(&mut self) {
if let Some(active) = self.active.take() {
self.merges.push_front(Arc::new(active));
}
}
pub fn get_signature_status_all<U>(
checkpoints: &[U],
signature: &Signature,
@ -246,7 +303,7 @@ mod tests {
let blockhash = hash(blockhash.as_ref());
let mut second = BankStatusCache::new(&blockhash);
first.freeze();
second.squash(&[&first]);
assert_eq!(second.get_signature_status(&sig), Some(Ok(())));
@ -254,7 +311,6 @@ mod tests {
}
#[test]
#[ignore] // takes a lot of time or RAM or both..
fn test_status_cache_squash_overflow() {
let mut blockhash = hash(Hash::default().as_ref());
let mut cache = BankStatusCache::new(&blockhash);
@ -263,7 +319,9 @@ mod tests {
.map(|_| {
blockhash = hash(blockhash.as_ref());
BankStatusCache::new(&blockhash)
let mut p = BankStatusCache::new(&blockhash);
p.freeze();
p
})
.collect();
@ -332,4 +390,18 @@ mod tests {
false
);
}
#[test]
fn test_status_cache_freeze() {
let sig = Signature::default();
let blockhash = hash(Hash::default().as_ref());
let mut cache: StatusCache<()> = StatusCache::new(&blockhash);
cache.freeze();
cache.freeze();
cache.add(&sig);
assert_eq!(cache.has_signature(&sig), false);
}
}