mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-03 00:31:50 +00:00
Keep track of failed head chains and prevent re-lookups (#1534)
## Overview There are forked chains which get referenced by blocks and attestations on a network. Typically if these chains are very long, we stop looking up the chain and downvote the peer. In extreme circumstances, many peers are on many chains, the chains can be very deep and become time consuming performing lookups. This PR adds a cache to known failed chain lookups. This prevents us from starting a parent-lookup (or stopping one half way through) if we have attempted the chain lookup in the past.
This commit is contained in:
8
common/lru_cache/Cargo.toml
Normal file
8
common/lru_cache/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "lru_cache"
|
||||
version = "0.1.0"
|
||||
authors = ["Sigma Prime <contact@sigmaprime.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
fnv = "1.0.7"
|
||||
7
common/lru_cache/src/lib.rs
Normal file
7
common/lru_cache/src/lib.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
//! A library to provide fast and efficient LRU Cache's without updating.
|
||||
|
||||
mod space;
|
||||
mod time;
|
||||
|
||||
pub use space::LRUCache;
|
||||
pub use time::LRUTimeCache;
|
||||
93
common/lru_cache/src/space.rs
Normal file
93
common/lru_cache/src/space.rs
Normal file
@@ -0,0 +1,93 @@
|
||||
///! This implements a time-based LRU cache for fast checking of duplicates
|
||||
use fnv::FnvHashSet;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
/// Cache that stores keys until the size is used up. Does not update elements for efficiency.
|
||||
pub struct LRUCache<Key>
|
||||
where
|
||||
Key: Eq + std::hash::Hash + Clone,
|
||||
{
|
||||
/// The duplicate cache.
|
||||
map: FnvHashSet<Key>,
|
||||
/// An ordered list of keys by order.
|
||||
list: VecDeque<Key>,
|
||||
// The max size of the cache,
|
||||
size: usize,
|
||||
}
|
||||
|
||||
impl<Key> LRUCache<Key>
|
||||
where
|
||||
Key: Eq + std::hash::Hash + Clone,
|
||||
{
|
||||
pub fn new(size: usize) -> Self {
|
||||
LRUCache {
|
||||
map: FnvHashSet::default(),
|
||||
list: VecDeque::new(),
|
||||
size,
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines if the key is in the cache.
|
||||
pub fn contains(&self, key: &Key) -> bool {
|
||||
self.map.contains(key)
|
||||
}
|
||||
|
||||
// Inserts new elements and removes any expired elements.
|
||||
//
|
||||
// If the key was not present this returns `true`. If the value was already present this
|
||||
// returns `false`.
|
||||
pub fn insert(&mut self, key: Key) -> bool {
|
||||
// check the cache before removing elements
|
||||
let result = self.map.insert(key.clone());
|
||||
|
||||
// add the new key to the list, if it doesn't already exist.
|
||||
if result {
|
||||
self.list.push_back(key);
|
||||
}
|
||||
// remove any overflow keys
|
||||
self.update();
|
||||
result
|
||||
}
|
||||
|
||||
/// Removes any expired elements from the cache.
|
||||
fn update(&mut self) {
|
||||
// remove any expired results
|
||||
for _ in 0..self.map.len().saturating_sub(self.size) {
|
||||
if let Some(key) = self.list.pop_front() {
|
||||
self.map.remove(&key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn cache_added_entries_exist() {
|
||||
let mut cache = LRUCache::new(5);
|
||||
|
||||
cache.insert("t");
|
||||
cache.insert("e");
|
||||
|
||||
// Should report that 't' and 't' already exists
|
||||
assert!(!cache.insert("t"));
|
||||
assert!(!cache.insert("e"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_entries_get_removed() {
|
||||
let mut cache = LRUCache::new(2);
|
||||
|
||||
cache.insert("t");
|
||||
assert!(!cache.insert("t"));
|
||||
cache.insert("e");
|
||||
assert!(!cache.insert("e"));
|
||||
// add another element to clear the first key
|
||||
cache.insert("s");
|
||||
assert!(!cache.insert("s"));
|
||||
// should be removed from the cache
|
||||
assert!(cache.insert("t"));
|
||||
}
|
||||
}
|
||||
126
common/lru_cache/src/time.rs
Normal file
126
common/lru_cache/src/time.rs
Normal file
@@ -0,0 +1,126 @@
|
||||
///! This implements a time-based LRU cache for fast checking of duplicates
|
||||
use fnv::FnvHashSet;
|
||||
use std::collections::VecDeque;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
struct Element<Key> {
|
||||
/// The key being inserted.
|
||||
key: Key,
|
||||
/// The instant the key was inserted.
|
||||
inserted: Instant,
|
||||
}
|
||||
|
||||
pub struct LRUTimeCache<Key> {
|
||||
/// The duplicate cache.
|
||||
map: FnvHashSet<Key>,
|
||||
/// An ordered list of keys by insert time.
|
||||
list: VecDeque<Element<Key>>,
|
||||
/// The time elements remain in the cache.
|
||||
ttl: Duration,
|
||||
}
|
||||
|
||||
impl<Key> LRUTimeCache<Key>
|
||||
where
|
||||
Key: Eq + std::hash::Hash + Clone,
|
||||
{
|
||||
pub fn new(ttl: Duration) -> Self {
|
||||
LRUTimeCache {
|
||||
map: FnvHashSet::default(),
|
||||
list: VecDeque::new(),
|
||||
ttl,
|
||||
}
|
||||
}
|
||||
|
||||
// Inserts new elements and removes any expired elements.
|
||||
//
|
||||
// If the key was not present this returns `true`. If the value was already present this
|
||||
// returns `false`.
|
||||
pub fn insert_update(&mut self, key: Key) -> bool {
|
||||
// check the cache before removing elements
|
||||
let result = self.map.insert(key.clone());
|
||||
|
||||
let now = Instant::now();
|
||||
|
||||
// remove any expired results
|
||||
while let Some(element) = self.list.pop_front() {
|
||||
if element.inserted + self.ttl > now {
|
||||
self.list.push_front(element);
|
||||
break;
|
||||
}
|
||||
self.map.remove(&element.key);
|
||||
}
|
||||
|
||||
// add the new key to the list, if it doesn't already exist.
|
||||
if result {
|
||||
self.list.push_back(Element { key, inserted: now });
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
// Inserts new element does not expire old elements.
|
||||
//
|
||||
// If the key was not present this returns `true`. If the value was already present this
|
||||
// returns `false`.
|
||||
pub fn insert(&mut self, key: Key) -> bool {
|
||||
// check the cache before removing elements
|
||||
let result = self.map.insert(key.clone());
|
||||
|
||||
// add the new key to the list, if it doesn't already exist.
|
||||
if result {
|
||||
self.list.push_back(Element {
|
||||
key,
|
||||
inserted: Instant::now(),
|
||||
});
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Removes any expired elements from the cache.
|
||||
pub fn update(&mut self) {
|
||||
let now = Instant::now();
|
||||
// remove any expired results
|
||||
while let Some(element) = self.list.pop_front() {
|
||||
if element.inserted + self.ttl > now {
|
||||
self.list.push_front(element);
|
||||
break;
|
||||
}
|
||||
self.map.remove(&element.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn cache_added_entries_exist() {
|
||||
let mut cache = LRUTimeCache::new(Duration::from_secs(10));
|
||||
|
||||
cache.insert("t");
|
||||
cache.insert("e");
|
||||
|
||||
// Should report that 't' and 't' already exists
|
||||
assert!(!cache.insert("t"));
|
||||
assert!(!cache.insert("e"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_entries_expire() {
|
||||
let mut cache = LRUTimeCache::new(Duration::from_millis(100));
|
||||
|
||||
cache.insert_update("t");
|
||||
assert!(!cache.insert_update("t"));
|
||||
cache.insert_update("e");
|
||||
assert!(!cache.insert_update("t"));
|
||||
assert!(!cache.insert_update("e"));
|
||||
// sleep until cache expiry
|
||||
std::thread::sleep(Duration::from_millis(101));
|
||||
// add another element to clear previous cache
|
||||
cache.insert_update("s");
|
||||
|
||||
// should be removed from the cache
|
||||
assert!(cache.insert_update("t"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user