Uh oh!
There was an error while loading. Please reload this page.
DB-based blockchain data cache for light nodes - #251
Conversation
f5e88b3 to
702ea9fCompare| // 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.
I think it might be a little better if the return value of prune_entries be destructed and named on the separate line
| 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.
unneeded space between .cache() and .and_then
svyatonik
commented
Jul 2, 2018
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.
Some house-style remarks, some docs and a few logic questions I'd like to have addressed.
| <<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> { |
| 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.
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.
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.
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.
this is some deep listing ... can we split this up to use less indentations (by using multiple .and_then or something)?
| /// 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>> { |
| 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]) { |
| #[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.
(and the one following: ) please wrap.
| 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<()> { |
| } | ||
| /// 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>>) { |
| 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<()>; |
gnunicorn
left a comment
There was a problem hiding this comment.
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.
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?
svyatonik
commented
Jul 24, 2018
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 |
gavofyork
commented
Jul 28, 2018
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 <koushiro.cqx@gmail.com> * remove confused alias Signed-off-by: koushiro <koushiro.cqx@gmail.com>
…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.