use std::time::{Instant, Duration};
use tetsy_util_mem::{MallocSizeOf, MallocSizeOfOps, MallocSizeOfExt};
use common_types::encoded;
use common_types::BlockNumber;
use common_types::receipt::Receipt;
use vapory_types::{H256, U256};
use memory_cache::MemoryLruCache;
use tetsy_stats::Corpus;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct CacheSizes {
pub headers: usize,
pub canon_hashes: usize,
pub bodies: usize,
pub receipts: usize,
pub chain_score: usize,
}
impl Default for CacheSizes {
fn default() -> Self {
const MB: usize = 1024 * 1024;
CacheSizes {
headers: 10 * MB,
canon_hashes: 3 * MB,
bodies: 20 * MB,
receipts: 10 * MB,
chain_score: 7 * MB,
}
}
}
pub struct Cache {
headers: MemoryLruCache<H256, encoded::Header>,
canon_hashes: MemoryLruCache<BlockNumber, H256>,
bodies: MemoryLruCache<H256, encoded::Body>,
receipts: MemoryLruCache<H256, Vec<Receipt>>,
chain_score: MemoryLruCache<H256, U256>,
corpus: Option<(Corpus<U256>, Instant)>,
corpus_expiration: Duration,
}
impl Cache {
pub fn new(sizes: CacheSizes, corpus_expiration: Duration) -> Self {
Cache {
headers: MemoryLruCache::new(sizes.headers),
canon_hashes: MemoryLruCache::new(sizes.canon_hashes),
bodies: MemoryLruCache::new(sizes.bodies),
receipts: MemoryLruCache::new(sizes.receipts),
chain_score: MemoryLruCache::new(sizes.chain_score),
corpus: None,
corpus_expiration,
}
}
pub fn block_header(&mut self, hash: &H256) -> Option<encoded::Header> {
self.headers.get_mut(hash).cloned()
}
pub fn block_hash(&mut self, num: BlockNumber) -> Option<H256> {
self.canon_hashes.get_mut(&num).map(|h| *h)
}
pub fn block_body(&mut self, hash: &H256) -> Option<encoded::Body> {
self.bodies.get_mut(hash).cloned()
}
pub fn block_receipts(&mut self, hash: &H256) -> Option<Vec<Receipt>> {
self.receipts.get_mut(hash).cloned()
}
pub fn chain_score(&mut self, hash: &H256) -> Option<U256> {
self.chain_score.get_mut(hash).map(|h| *h)
}
pub fn insert_block_header(&mut self, hash: H256, hdr: encoded::Header) {
self.headers.insert(hash, hdr);
}
pub fn insert_block_hash(&mut self, num: BlockNumber, hash: H256) {
self.canon_hashes.insert(num, hash);
}
pub fn insert_block_body(&mut self, hash: H256, body: encoded::Body) {
self.bodies.insert(hash, body);
}
pub fn insert_block_receipts(&mut self, hash: H256, receipts: Vec<Receipt>) {
self.receipts.insert(hash, receipts);
}
pub fn insert_chain_score(&mut self, hash: H256, score: U256) {
self.chain_score.insert(hash, score);
}
pub fn gas_price_corpus(&self) -> Option<Corpus<U256>> {
let now = Instant::now();
self.corpus.as_ref().and_then(|&(ref corpus, ref tm)| {
if *tm + self.corpus_expiration >= now {
Some(corpus.clone())
} else {
None
}
})
}
pub fn set_gas_price_corpus(&mut self, corpus: Corpus<U256>) {
self.corpus = Some((corpus, Instant::now()))
}
pub fn mem_used(&self) -> usize {
self.malloc_size_of()
}
}
impl MallocSizeOf for Cache {
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
self.headers.current_size()
+ self.canon_hashes.current_size()
+ self.bodies.current_size()
+ self.receipts.current_size()
+ self.chain_score.current_size()
}
}
#[cfg(test)]
mod tests {
use super::Cache;
use std::time::Duration;
#[test]
fn corpus_inaccessible() {
let duration = Duration::from_secs(20);
let mut cache = Cache::new(Default::default(), duration.clone());
cache.set_gas_price_corpus(vec![].into());
assert_eq!(cache.gas_price_corpus(), Some(vec![].into()));
{
let corpus_time = &mut cache.corpus.as_mut().unwrap().1;
*corpus_time = *corpus_time - duration;
}
assert!(cache.gas_price_corpus().is_none());
}
}