Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Conversation

@svyatonik
Copy link
Contributor

@svyatonik svyatonik commented Jun 26, 2018

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 the BlockImportOperation and 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 same authorities_at value (the one that we've passed to the BlockImportOperation before). List entries for 'ancient' blocks (> 2048) are got pruned.

In the future (when we'll have 'digest signals' about changes), BlockImportOperation must be altered to mark that authorities_at has/has not changed in the block we're importing.

The same cache will probably be used later for the code_at cache.

@svyatonik svyatonik added A0-please_review Pull request needs code review. A1-onice labels Jun 26, 2018
@svyatonik svyatonik force-pushed the light_blockchain_cache branch from f5e88b3 to 702ea9f Compare June 29, 2018 10:47
// 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 {
Copy link
Contributor

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

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)) {
Copy link
Contributor

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

@svyatonik
Copy link
Contributor Author

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).

@svyatonik svyatonik added A5-grumble and removed A0-please_review Pull request needs code review. labels Jul 2, 2018
@svyatonik svyatonik added A0-please_review Pull request needs code review. and removed A5-grumble labels Jul 17, 2018
@gnunicorn gnunicorn self-assigned this Jul 23, 2018
Copy link
Contributor

@gnunicorn gnunicorn left a 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.

<<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> {
Copy link
Contributor

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),
Copy link
Contributor

@gnunicorn gnunicorn Jul 23, 2018

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?

Copy link
Contributor Author

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
Copy link
Contributor

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 {
Copy link
Contributor

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)?


/// 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>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please wrap.

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]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please wrap.

#[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()]));
Copy link
Contributor

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.


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<()> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please wrap.

}

/// 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>>) {
Copy link
Contributor

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<()>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please wrap.

@gnunicorn gnunicorn added A5-grumble and removed A0-please_review Pull request needs code review. labels Jul 23, 2018
@svyatonik svyatonik added A0-please_review Pull request needs code review. and removed A5-grumble labels Jul 23, 2018
Copy link
Contributor

@gnunicorn gnunicorn left a 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.
Copy link
Contributor

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?

@gnunicorn gnunicorn removed the A0-please_review Pull request needs code review. label Jul 24, 2018
@svyatonik
Copy link
Contributor Author

The meaning of L218 is about returning from the function if first entry of the list is for block later than last_to_prune. I.e. if there are entries for blocks [10, 20, 30, 40] and we're calling prune_entries(5) this line will fire, because the first list entry (10) is for block 10 > 5.

There are couple of tests for pruning - see best_authorities_are_updated and best_authorities_are_pruned.

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 best_authorities_are_pruned test (now it receives actual number of deleted entries) + added comments to the test. Thanks!

@svyatonik svyatonik added A0-please_review Pull request needs code review. and removed A6-mustntgrumble labels Jul 24, 2018
@gavofyork
Copy link
Member

conficts...

@gavofyork gavofyork merged commit 5e1cb6b into master Jul 29, 2018
@gavofyork gavofyork deleted the light_blockchain_cache branch July 29, 2018 11:21
dvdplm added a commit that referenced this pull request Jul 30, 2018
* 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)
  ...
liuchengxu pushed a commit to chainx-org/substrate that referenced this pull request Aug 23, 2021
* Improve code and dependencies

Signed-off-by: koushiro <[email protected]>

* remove confused alias

Signed-off-by: koushiro <[email protected]>
liuchengxu pushed a commit to autonomys/substrate that referenced this pull request Jun 3, 2022
helin6 pushed a commit to boolnetwork/substrate that referenced this pull request Jul 25, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

A0-please_review Pull request needs code review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants