-
Notifications
You must be signed in to change notification settings - Fork 2.7k
DB-based blockchain data cache for light nodes #251
Conversation
f5e88b3 to
702ea9f
Compare
substrate/client/db/src/light.rs
Outdated
| // prune authorities from 'ancient' blocks | ||
| let mut update_best_authorities = best_authorities.is_some(); | ||
| if let Some(ancient_number) = number.checked_sub(AUTHORITIES_ENTRIES_TO_KEEP) { | ||
| if self.cache.authorities_at_cache().prune_entries(&mut transaction, As::sa(ancient_number))?.1 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it might be a little better if the return value of prune_entries be destructed and named on the separate line
substrate/client/src/client.rs
Outdated
| self.executor.call(id, "authorities",&[]) | ||
| .and_then(|r| Vec::<AuthorityId>::decode(&mut &r.return_data[..]) | ||
| .ok_or(error::ErrorKind::AuthLenInvalid.into())) | ||
| match self.backend.blockchain().cache() .and_then(|cache| cache.authorities_at(*id)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unneeded space between .cache() and .and_then
|
As @pepyakin suggested (externally), it would be good to to leave at least 1 entry in cache (the newest one) when pruning. Will do (I hope tonight/tomorrow). |
gnunicorn
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some house-style remarks, some docs and a few logic questions I'd like to have addressed.
substrate/client/db/src/cache.rs
Outdated
| <<Block as BlockT>::Header as HeaderT>::Number: As<u64>, | ||
| { | ||
| /// Create new cache. | ||
| pub fn new(db: Arc<KeyValueDB>, block_index_column: Option<u32>, authorities_column: Option<u32>) -> ClientResult<Self> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please wrap.
| fn authorities_at(&self, at: BlockId<Block>) -> Option<Vec<AuthorityId>> { | ||
| let authorities_at = read_id(&*self.db, self.block_index_column, at).and_then(|at| match at { | ||
| Some(at) => self.authorities_at.value_at_key(at), | ||
| None => Ok(None), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am a bit puzzled about this. How is it possible that we'd ever come to None in here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My guess - when we're calling authorities_at(BlockId::Hash(unknown_hash)). Or am I wrong? :)
Anyway - it is the cache, I'd prefer to write cache code without any extra expects, since this is an optimization anyway.
| } | ||
|
|
||
| /// Database-backed blockchain cache which holds its entries as a list. | ||
| /// The meta column holds the pointer to the best known cache entry and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is the definition of best in this context?
| Some(block) => { | ||
| let valid_from = db_key_to_number(&block)?; | ||
| read_storage_entry::<Block, T>(&*db, column, valid_from) | ||
| .map(|entry| Some(Entry { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is some deep listing ... can we split this up to use less indentations (by using multiple .and_then or something)?
substrate/client/db/src/cache.rs
Outdated
|
|
||
| /// Commits the new best pending value to the database. Returns Some if best entry must | ||
| /// be updated after transaction is committed. | ||
| pub fn commit_best_entry(&self, transaction: &mut DBTransaction, valid_from: <<Block as BlockT>::Header as HeaderT>::Number, pending_value: Option<T>) -> Option<Entry<<<Block as BlockT>::Header as HeaderT>::Number, T>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please wrap.
substrate/client/src/client.rs
Outdated
| Block: BlockT, | ||
| { | ||
| fn import_block(&self, block: Block, justification: ::bft::Justification<Block::Hash>) { | ||
| fn import_block(&self, block: Block, justification: ::bft::Justification<Block::Hash>, authorities: &[AuthorityId]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please wrap.
substrate/client/src/client.rs
Outdated
| #[test] | ||
| fn client_uses_authorities_from_blockchain_cache() { | ||
| let client = test_client::new(); | ||
| test_client::client::in_mem::cache_authorities_at(client.backend().blockchain(), Default::default(), Some(vec![[1u8; 32].into()])); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(and the one following: ) please wrap.
substrate/client/src/in_mem.rs
Outdated
|
|
||
| impl<Block: BlockT> light::blockchain::Storage<Block> for Blockchain<Block> { | ||
| fn import_header(&self, is_new_best: bool, header: Block::Header) -> error::Result<()> { | ||
| fn import_header(&self, is_new_best: bool, header: Block::Header, authorities: Option<Vec<AuthorityId>>) -> error::Result<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please wrap.
substrate/client/src/in_mem.rs
Outdated
| } | ||
|
|
||
| /// Insert authorities entry into in-memory blockchain cache. Extracted as a separate function to use it in tests. | ||
| pub fn cache_authorities_at<Block: BlockT>(blockchain: &Blockchain<Block>, at: Block::Hash, authorities: Option<Vec<AuthorityId>>) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please wrap.
| pub trait Storage<Block: BlockT>: BlockchainHeaderBackend<Block> { | ||
| /// Store new header. | ||
| fn import_header(&self, is_new_best: bool, header: Block::Header) -> ClientResult<()>; | ||
| fn import_header(&self, is_new_best: bool, header: Block::Header, authorities: Option<Vec<AuthorityId>>) -> ClientResult<()>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please wrap.
gnunicorn
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, looks good. Just one last clarification - maybe I am reading this wrong?!?
| } | ||
|
|
||
| /// Prune all entries from the beginning up to the block (including entry at the number). Returns | ||
| /// the number of pruned entries. Pruning never deletes the latest entry in the cache. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have test for that? Because if I understand L218 correctly, we early quit the function without actually pruning if our loop finds the last entry, no?
|
The meaning of L218 is about returning from the function if first entry of the list is for block later than There are couple of tests for pruning - see Thanks to your comment, I've found an issue with incorrect reporting of number of pruned entries - they have included the last entry even if it was not physically deleted (actually it was deleted and then inserted back into the cache). Fixed it + fixed |
|
conficts... |
* master: (86 commits) Make contract a separate runtime module (#345) Version bump (#450) DB-based blockchain data cache for light nodes (#251) Update libp2p again (#445) Update version on git head change (#444) Fix the public key of bootnode 3 (#441) Update libp2p (#442) Switch to the master branch of libp2p (#427) Export ws port 9944 and add doc (#440) Iterate over overlay to decide which keys to purge (#436) Exit signal gets its own trait (#433) Add docker image (#375) Reset peers.json if the content is not loadable (#405) Limit number of incoming connections (#391) Fix memory leaks in libp2p (#432) Do not queue empty blocks set for import (#431) 5 random fixes (#1) (#435) Chore: fix typo (#434) Prevent building invalid blocks (#430) Better logging for public key mismatch (#429) ...
* Improve code and dependencies Signed-off-by: koushiro <[email protected]> * remove confused alias Signed-off-by: koushiro <[email protected]>
…armer Subspace networking (farmer)
on top of #250
this PR is for this commit only
This PR is about implementing
authorities_at(and in future -code_at) cache as was first mentioned in this comment.So idea is: when we're importing block, we're checking justification, which includes fetching
authorities_at(at the parent' block state) from remote node (on light clients). We're adding this data to theBlockImportOperationand committing it into the list-style db-cache when committing transaction. The cache is organized in the linked list form. Meta column holds the reference to the tail node (most recent entry in our case) + every node keeps the reference to the previous list element. Blocks between two nodes are guaranteed to have the sameauthorities_atvalue (the one that we've passed to theBlockImportOperationbefore). List entries for 'ancient' blocks (> 2048) are got pruned.In the future (when we'll have 'digest signals' about changes),
BlockImportOperationmust be altered to mark thatauthorities_athas/has not changed in the block we're importing.The same cache will probably be used later for the
code_atcache.