diff --git a/substrate/client/db/src/lib.rs b/substrate/client/db/src/lib.rs index 8196a750557a8484ce72fc906b81a9301b1fe808..983459cfebe33ea35738b0d4db27bdb82659e7f0 100644 --- a/substrate/client/db/src/lib.rs +++ b/substrate/client/db/src/lib.rs @@ -49,6 +49,9 @@ use std::sync::Arc; use std::path::{Path, PathBuf}; use std::io; use std::collections::{HashMap, HashSet}; +use parking_lot::{Mutex, RwLock}; +use linked_hash_map::LinkedHashMap; +use log::{trace, debug, warn}; use sc_client_api::{ UsageInfo, MemoryInfo, IoInfo, MemorySize, @@ -63,7 +66,6 @@ use codec::{Decode, Encode}; use hash_db::Prefix; use sp_trie::{MemoryDB, PrefixedMemoryDB, prefixed_key}; use sp_database::Transaction; -use parking_lot::RwLock; use sp_core::ChangesTrieConfiguration; use sp_core::offchain::storage::{OffchainOverlayedChange, OffchainOverlayedChanges}; use sp_core::storage::{well_known_keys, ChildInfo}; @@ -83,7 +85,6 @@ use sc_state_db::StateDb; use sp_blockchain::{CachedHeaderMetadata, HeaderMetadata, HeaderMetadataCache}; use crate::storage_cache::{CachingState, SyncingCachingState, SharedCache, new_shared_cache}; use crate::stats::StateUsageStats; -use log::{trace, debug, warn}; // Re-export the Database trait so that one can pass an implementation of it. pub use sp_database::Database; @@ -93,6 +94,7 @@ pub use sc_state_db::PruningMode; pub use bench::BenchmarkingState; const MIN_BLOCKS_TO_KEEP_CHANGES_TRIES_FOR: u32 = 32768; +const CACHE_HEADERS: usize = 8; /// Default value for storage cache child ratio. const DEFAULT_CHILD_RATIO: (usize, usize) = (1, 10); @@ -352,12 +354,24 @@ impl<'a> sc_state_db::MetaDb for StateMetaDb<'a> { } } +fn cache_header<Hash: std::cmp::Eq + std::hash::Hash, Header>( + cache: &mut LinkedHashMap<Hash, Option<Header>>, + hash: Hash, + header: Option<Header>, +) { + cache.insert(hash, header); + while cache.len() > CACHE_HEADERS { + cache.pop_front(); + } +} + /// Block database pub struct BlockchainDb<Block: BlockT> { db: Arc<dyn Database<DbHash>>, meta: Arc<RwLock<Meta<NumberFor<Block>, Block::Hash>>>, leaves: RwLock<LeafSet<Block::Hash, NumberFor<Block>>>, header_metadata_cache: Arc<HeaderMetadataCache<Block>>, + header_cache: Mutex<LinkedHashMap<Block::Hash, Option<Block::Header>>>, } impl<Block: BlockT> BlockchainDb<Block> { @@ -369,6 +383,7 @@ impl<Block: BlockT> BlockchainDb<Block> { leaves: RwLock::new(leaves), meta: Arc::new(RwLock::new(meta)), header_metadata_cache: Arc::new(HeaderMetadataCache::default()), + header_cache: Default::default(), }) } @@ -407,7 +422,20 @@ impl<Block: BlockT> BlockchainDb<Block> { impl<Block: BlockT> sc_client_api::blockchain::HeaderBackend<Block> for BlockchainDb<Block> { fn header(&self, id: BlockId<Block>) -> ClientResult<Option<Block::Header>> { - utils::read_header(&*self.db, columns::KEY_LOOKUP, columns::HEADER, id) + match &id { + BlockId::Hash(h) => { + let mut cache = self.header_cache.lock(); + if let Some(result) = cache.get_refresh(h) { + return Ok(result.clone()); + } + let header = utils::read_header(&*self.db, columns::KEY_LOOKUP, columns::HEADER, id)?; + cache_header(&mut cache, h.clone(), header.clone()); + Ok(header) + } + BlockId::Number(_) => { + utils::read_header(&*self.db, columns::KEY_LOOKUP, columns::HEADER, id) + } + } } fn info(&self) -> sc_client_api::blockchain::Info<Block> { @@ -424,12 +452,7 @@ impl<Block: BlockT> sc_client_api::blockchain::HeaderBackend<Block> for Blockcha fn status(&self, id: BlockId<Block>) -> ClientResult<sc_client_api::blockchain::BlockStatus> { let exists = match id { - BlockId::Hash(_) => read_db( - &*self.db, - columns::KEY_LOOKUP, - columns::HEADER, - id - )?.is_some(), + BlockId::Hash(_) => self.header(id)?.is_some(), BlockId::Number(n) => n <= self.meta.read().best_number, }; match exists { @@ -1117,12 +1140,6 @@ impl<Block: BlockT> Backend<Block> { hash, )?; - let header_metadata = CachedHeaderMetadata::from(&pending_block.header); - self.blockchain.insert_header_metadata( - header_metadata.hash, - header_metadata, - ); - transaction.set_from_vec(columns::HEADER, &lookup_key, pending_block.header.encode()); if let Some(body) = &pending_block.body { transaction.set_from_vec(columns::BODY, &lookup_key, body.encode()); @@ -1271,7 +1288,7 @@ impl<Block: BlockT> Backend<Block> { meta_updates.push((hash, number, pending_block.leaf_state.is_best(), finalized)); - Some((number, hash, enacted, retracted, displaced_leaf, is_best, cache)) + Some((pending_block.header, number, hash, enacted, retracted, displaced_leaf, is_best, cache)) } else { None }; @@ -1297,7 +1314,11 @@ impl<Block: BlockT> Backend<Block> { self.storage.db.commit(transaction)?; + // Apply all in-memory state shanges. + // Code beyond this point can't fail. + if let Some(( + header, number, hash, enacted, @@ -1306,6 +1327,12 @@ impl<Block: BlockT> Backend<Block> { is_best, mut cache, )) = imported { + let header_metadata = CachedHeaderMetadata::from(&header); + self.blockchain.insert_header_metadata( + header_metadata.hash, + header_metadata, + ); + cache_header(&mut self.blockchain.header_cache.lock(), hash, Some(header)); cache.sync_cache( &enacted, &retracted, diff --git a/substrate/client/service/src/client/client.rs b/substrate/client/service/src/client/client.rs index d423fdee39b6cbebe368b222001e79131459c297..e8d748011bc1770d8f28db5bd3f287b970d60259 100644 --- a/substrate/client/service/src/client/client.rs +++ b/substrate/client/service/src/client/client.rs @@ -1159,12 +1159,12 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where /// Prepare in-memory header that is used in execution environment. fn prepare_environment_block(&self, parent: &BlockId<Block>) -> sp_blockchain::Result<Block::Header> { - let parent_header = self.backend.blockchain().expect_header(*parent)?; + let parent_hash = self.backend.blockchain().expect_block_hash_from_id(parent)?; Ok(<<Block as BlockT>::Header as HeaderT>::new( self.backend.blockchain().expect_block_number_from_id(parent)? + One::one(), Default::default(), Default::default(), - parent_header.hash(), + parent_hash, Default::default(), )) } diff --git a/substrate/primitives/blockchain/src/backend.rs b/substrate/primitives/blockchain/src/backend.rs index 1328dfb5752fc0544e33c6b9934965dd3158dded..326acd6b9bd43f69f1329b8df005ecd1f73bedc1 100644 --- a/substrate/primitives/blockchain/src/backend.rs +++ b/substrate/primitives/blockchain/src/backend.rs @@ -53,7 +53,7 @@ pub trait HeaderBackend<Block: BlockT>: Send + Sync { /// Convert an arbitrary block ID into a block hash. fn block_number_from_id(&self, id: &BlockId<Block>) -> Result<Option<NumberFor<Block>>> { match *id { - BlockId::Hash(_) => Ok(self.header(*id)?.map(|h| h.number().clone())), + BlockId::Hash(h) => self.number(h), BlockId::Number(n) => Ok(Some(n)), } }