Skip to content

Avoid querying unwritten values from changesets during historical lookups#2966

Merged
gakonst merged 1 commit intoparadigmxyz:mainfrom
petertdavies:optimize-historical-lookups
Jun 5, 2023
Merged

Avoid querying unwritten values from changesets during historical lookups#2966
gakonst merged 1 commit intoparadigmxyz:mainfrom
petertdavies:optimize-historical-lookups

Conversation

@petertdavies
Copy link
Contributor

This PR avoids querying the plain state or changesets for accounts and storage values if the history tables show the value has not been written. This will return incorrect results for queries about genesis accounts if you synced prior to #2606.

<1% of historical queries will incur an extra cursor.prev(). I have not benchmarked the cost of this, but I assume it worth it to save full lookups in the PlainState/Changeset tables.

The semantics of return value of storage() have changed when the value is zero. The old semantics were "If the key has ever been written (including after the requested block) return Some(StorageValue::ZERO), else return None. The new semantics are "If the key has not been written prior to the requested block return None, else return Some(StorageValue::ZERO).

@petertdavies petertdavies force-pushed the optimize-historical-lookups branch from 3ca965f to eb2e59f Compare June 2, 2023 16:16
@codecov-commenter
Copy link

codecov-commenter commented Jun 2, 2023

Codecov Report

Merging #2966 (eb2e59f) into main (5c7c660) will increase coverage by 0.01%.
The diff coverage is 98.05%.

❗ Your organization is not using the GitHub App Integration. As a result you may experience degraded service beginning May 15th. Please install the Github App Integration for your organization. Read more.

@@            Coverage Diff             @@
##             main    #2966      +/-   ##
==========================================
+ Coverage   70.97%   70.99%   +0.01%     
==========================================
  Files         520      520              
  Lines       67144    67195      +51     
==========================================
+ Hits        47656    47704      +48     
- Misses      19488    19491       +3     
Flag Coverage Δ
integration-tests 17.27% <0.00%> (-0.04%) ⬇️
unit-tests 65.89% <98.05%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
...storage/provider/src/providers/state/historical.rs 87.98% <98.05%> (+2.38%) ⬆️

... and 5 files with indirect coverage changes

@gakonst
Copy link
Member

gakonst commented Jun 3, 2023

@joshieDo PTAL

Copy link
Member

@gakonst gakonst left a comment

Choose a reason for hiding this comment

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

love it. makes a ton of sense.

Comment on lines +52 to +67
if let Some(chunk) =
cursor.seek(history_key)?.filter(|(key, _)| key.key == address).map(|x| x.1 .0)
{
let chunk = chunk.enable_rank();
let rank = chunk.rank(self.block_number as usize);
if rank == 0 && !cursor.prev()?.is_some_and(|(key, _)| key.key == address) {
return Ok(HistoryInfo::NotWritten)
}
if rank < chunk.len() {
Ok(HistoryInfo::InChangeset(chunk.select(rank) as u64))
} else {
Ok(HistoryInfo::InPlainState)
}
} else {
Ok(HistoryInfo::NotWritten)
}
Copy link
Member

Choose a reason for hiding this comment

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

It could be helpful if we had some inline ascii diagram showing how this works, I think basically it says:

If there's a value in the history table then it is:

  • Not Written: If there are 0 elements in that entry AND there exists a previous entry in the table for that key, meaning it was previously written and now there was no change. We use this later for the optimization in the changeset read. In practice this is going to be rare as in most cases it will berank != 0 and the if statement will short circuit.
  • InChangeset: If the number of items less than block number in that bucket is less than the items in the bucket, return the corresponding changeset key.
  • InPlainState: If it's larger than the bucket len, then it means it's in the plain state

If there's no value, also return NotWritten.

) -> Result<HistoryInfo> {
// history key to search IntegerList of block number changesets.
let history_key = StorageShardedKey::new(address, storage_key, self.block_number);
let mut cursor = self.tx.cursor_read::<tables::StorageHistory>()?;
Copy link
Member

Choose a reason for hiding this comment

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

same idea as above here but with the duptable making it a little more verbose

@gakonst gakonst added this pull request to the merge queue Jun 5, 2023
Merged via the queue into paradigmxyz:main with commit 37fe447 Jun 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants