mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-13 19:45:54 +00:00
Optimize tree route to sync faster (#3665)
* Introduce HeaderMetadata and use it for tree_route. Add lowest_common_ancestor. * Add tests.
This commit is contained in:
@@ -35,6 +35,7 @@ use codec::{Decode, Encode};
|
||||
use primitives::Blake2Hasher;
|
||||
use sr_primitives::generic::{DigestItem, BlockId};
|
||||
use sr_primitives::traits::{Block as BlockT, Header as HeaderT, Zero, One, NumberFor};
|
||||
use header_metadata::{CachedHeaderMetadata, HeaderMetadata, HeaderMetadataCache};
|
||||
use crate::cache::{DbCacheSync, DbCache, ComplexBlockId, EntryType as CacheEntryType};
|
||||
use crate::utils::{self, meta_keys, Meta, db_err, read_db, block_id_to_lookup_key, read_meta};
|
||||
use crate::DatabaseSettings;
|
||||
@@ -60,6 +61,7 @@ pub struct LightStorage<Block: BlockT> {
|
||||
db: Arc<dyn KeyValueDB>,
|
||||
meta: RwLock<Meta<NumberFor<Block>, Block::Hash>>,
|
||||
cache: Arc<DbCacheSync<Block>>,
|
||||
header_metadata_cache: HeaderMetadataCache<Block>,
|
||||
}
|
||||
|
||||
impl<Block> LightStorage<Block>
|
||||
@@ -109,6 +111,7 @@ impl<Block> LightStorage<Block>
|
||||
db,
|
||||
meta: RwLock::new(meta),
|
||||
cache: Arc::new(DbCacheSync(RwLock::new(cache))),
|
||||
header_metadata_cache: HeaderMetadataCache::default(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -192,6 +195,31 @@ impl<Block> BlockchainHeaderBackend<Block> for LightStorage<Block>
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT> HeaderMetadata<Block> for LightStorage<Block> {
|
||||
type Error = ClientError;
|
||||
|
||||
fn header_metadata(&self, hash: Block::Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> {
|
||||
self.header_metadata_cache.header_metadata(hash).or_else(|_| {
|
||||
self.header(BlockId::hash(hash))?.map(|header| {
|
||||
let header_metadata = CachedHeaderMetadata::from(&header);
|
||||
self.header_metadata_cache.insert_header_metadata(
|
||||
header_metadata.hash,
|
||||
header_metadata.clone(),
|
||||
);
|
||||
header_metadata
|
||||
}).ok_or(ClientError::UnknownBlock("header not found in db".to_owned()))
|
||||
})
|
||||
}
|
||||
|
||||
fn insert_header_metadata(&self, hash: Block::Hash, metadata: CachedHeaderMetadata<Block>) {
|
||||
self.header_metadata_cache.insert_header_metadata(hash, metadata)
|
||||
}
|
||||
|
||||
fn remove_header_metadata(&self, hash: Block::Hash) {
|
||||
self.header_metadata_cache.remove_header_metadata(hash);
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT> LightStorage<Block> {
|
||||
// Get block changes trie root, if available.
|
||||
fn changes_trie_root(&self, block: BlockId<Block>) -> ClientResult<Option<Block::Hash>> {
|
||||
@@ -208,17 +236,18 @@ impl<Block: BlockT> LightStorage<Block> {
|
||||
/// In the case where the new best block is a block to be imported, `route_to`
|
||||
/// should be the parent of `best_to`. In the case where we set an existing block
|
||||
/// to be best, `route_to` should equal to `best_to`.
|
||||
fn set_head_with_transaction(&self, transaction: &mut DBTransaction, route_to: Block::Hash, best_to: (NumberFor<Block>, Block::Hash)) -> Result<(), client::error::Error> {
|
||||
fn set_head_with_transaction(
|
||||
&self,
|
||||
transaction: &mut DBTransaction,
|
||||
route_to: Block::Hash,
|
||||
best_to: (NumberFor<Block>, Block::Hash),
|
||||
) -> ClientResult<()> {
|
||||
let lookup_key = utils::number_and_hash_to_lookup_key(best_to.0, &best_to.1)?;
|
||||
|
||||
// handle reorg.
|
||||
let meta = self.meta.read();
|
||||
if meta.best_hash != Default::default() {
|
||||
let tree_route = ::client::blockchain::tree_route(
|
||||
|id| self.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
|
||||
BlockId::Hash(meta.best_hash),
|
||||
BlockId::Hash(route_to),
|
||||
)?;
|
||||
let tree_route = header_metadata::tree_route(self, meta.best_hash, route_to)?;
|
||||
|
||||
// update block number to hash lookup entries.
|
||||
for retracted in tree_route.retracted() {
|
||||
@@ -418,6 +447,12 @@ impl<Block> LightBlockchainStorage<Block> for LightStorage<Block>
|
||||
)?;
|
||||
transaction.put(columns::HEADER, &lookup_key, &header.encode());
|
||||
|
||||
let header_metadata = CachedHeaderMetadata::from(&header);
|
||||
self.header_metadata_cache.insert_header_metadata(
|
||||
header.hash().clone(),
|
||||
header_metadata,
|
||||
);
|
||||
|
||||
let is_genesis = number.is_zero();
|
||||
if is_genesis {
|
||||
self.cache.0.write().set_genesis_hash(hash);
|
||||
@@ -537,6 +572,7 @@ pub(crate) mod tests {
|
||||
use client::cht;
|
||||
use sr_primitives::generic::DigestItem;
|
||||
use sr_primitives::testing::{H256 as Hash, Header, Block as RawBlock, ExtrinsicWrapper};
|
||||
use header_metadata::{lowest_common_ancestor, tree_route};
|
||||
use super::*;
|
||||
|
||||
type Block = RawBlock<ExtrinsicWrapper<u32>>;
|
||||
@@ -781,11 +817,7 @@ pub(crate) mod tests {
|
||||
let b2 = insert_block(&db, HashMap::new(), || default_header(&b1, 2));
|
||||
|
||||
{
|
||||
let tree_route = ::client::blockchain::tree_route(
|
||||
|id| db.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
|
||||
BlockId::Hash(a3),
|
||||
BlockId::Hash(b2)
|
||||
).unwrap();
|
||||
let tree_route = tree_route(&db, a3, b2).unwrap();
|
||||
|
||||
assert_eq!(tree_route.common_block().hash, block0);
|
||||
assert_eq!(tree_route.retracted().iter().map(|r| r.hash).collect::<Vec<_>>(), vec![a3, a2, a1]);
|
||||
@@ -793,11 +825,7 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
{
|
||||
let tree_route = ::client::blockchain::tree_route(
|
||||
|id| db.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
|
||||
BlockId::Hash(a1),
|
||||
BlockId::Hash(a3),
|
||||
).unwrap();
|
||||
let tree_route = tree_route(&db, a1, a3).unwrap();
|
||||
|
||||
assert_eq!(tree_route.common_block().hash, a1);
|
||||
assert!(tree_route.retracted().is_empty());
|
||||
@@ -805,11 +833,7 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
{
|
||||
let tree_route = ::client::blockchain::tree_route(
|
||||
|id| db.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
|
||||
BlockId::Hash(a3),
|
||||
BlockId::Hash(a1),
|
||||
).unwrap();
|
||||
let tree_route = tree_route(&db, a3, a1).unwrap();
|
||||
|
||||
assert_eq!(tree_route.common_block().hash, a1);
|
||||
assert_eq!(tree_route.retracted().iter().map(|r| r.hash).collect::<Vec<_>>(), vec![a3, a2]);
|
||||
@@ -817,11 +841,7 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
{
|
||||
let tree_route = ::client::blockchain::tree_route(
|
||||
|id| db.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
|
||||
BlockId::Hash(a2),
|
||||
BlockId::Hash(a2),
|
||||
).unwrap();
|
||||
let tree_route = tree_route(&db, a2, a2).unwrap();
|
||||
|
||||
assert_eq!(tree_route.common_block().hash, a2);
|
||||
assert!(tree_route.retracted().is_empty());
|
||||
@@ -829,6 +849,63 @@ pub(crate) mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lowest_common_ancestor_works() {
|
||||
let db = LightStorage::new_test();
|
||||
let block0 = insert_block(&db, HashMap::new(), || default_header(&Default::default(), 0));
|
||||
|
||||
// fork from genesis: 3 prong.
|
||||
let a1 = insert_block(&db, HashMap::new(), || default_header(&block0, 1));
|
||||
let a2 = insert_block(&db, HashMap::new(), || default_header(&a1, 2));
|
||||
let a3 = insert_block(&db, HashMap::new(), || default_header(&a2, 3));
|
||||
|
||||
// fork from genesis: 2 prong.
|
||||
let b1 = insert_block(&db, HashMap::new(), || header_with_extrinsics_root(&block0, 1, Hash::from([1; 32])));
|
||||
let b2 = insert_block(&db, HashMap::new(), || default_header(&b1, 2));
|
||||
|
||||
{
|
||||
let lca = lowest_common_ancestor(&db, a3, b2).unwrap();
|
||||
|
||||
assert_eq!(lca.hash, block0);
|
||||
assert_eq!(lca.number, 0);
|
||||
}
|
||||
|
||||
{
|
||||
let lca = lowest_common_ancestor(&db, a1, a3).unwrap();
|
||||
|
||||
assert_eq!(lca.hash, a1);
|
||||
assert_eq!(lca.number, 1);
|
||||
}
|
||||
|
||||
{
|
||||
let lca = lowest_common_ancestor(&db, a3, a1).unwrap();
|
||||
|
||||
assert_eq!(lca.hash, a1);
|
||||
assert_eq!(lca.number, 1);
|
||||
}
|
||||
|
||||
{
|
||||
let lca = lowest_common_ancestor(&db, a2, a3).unwrap();
|
||||
|
||||
assert_eq!(lca.hash, a2);
|
||||
assert_eq!(lca.number, 2);
|
||||
}
|
||||
|
||||
{
|
||||
let lca = lowest_common_ancestor(&db, a2, a1).unwrap();
|
||||
|
||||
assert_eq!(lca.hash, a1);
|
||||
assert_eq!(lca.number, 1);
|
||||
}
|
||||
|
||||
{
|
||||
let lca = lowest_common_ancestor(&db, a2, a2).unwrap();
|
||||
|
||||
assert_eq!(lca.hash, a2);
|
||||
assert_eq!(lca.number, 2);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authorities_are_cached() {
|
||||
let db = LightStorage::new_test();
|
||||
|
||||
Reference in New Issue
Block a user