-
Notifications
You must be signed in to change notification settings - Fork 632
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: construct state sync parts using flat storage #8927
Conversation
Needed for #8927. 1. From what I see in https://doc.rust-lang.org/rust-by-example/scope/lifetime/explicit.html, lifetimes introduce restriction over passed variables - they must outlive function lifetime. But this doesn't make sense for iterator. It doesn't save much memory, because iterator is expected to return lots of vectors anyway. And slices `from` and `to` are converted to vectors inside anyway. This allows us to construct DB keys inside `iter_flat_state_entries`. 2. Construction of DB keys is hidden inside `flat` module now. You give state keys and receive state keys back, without knowing how they are stored on the lower level. ## Testing @jbajic would you like to cover `iter_flat_state_entries` with some test? This function will be covered by integration tests for state parts anyway and it is not used much currently. But it seems nice to have a test which creates flat storage for, say, 3 shards, writes some KV pairs to it and then checks that flat state iterator gives us correct data.
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.
overall LGTM!
core/store/src/flat/manager.rs
Outdated
match flat_storages.get(&shard_uid) { | ||
Some(flat_storage) => flat_storage.clone(), | ||
None => { | ||
debug!(target: "chain", "FlatStorage is not ready"); |
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.
nit: I guess "store"
target would be more appropriate than "chain"
here
core/store/src/flat/store_helper.rs
Outdated
shard_layout: &ShardLayout, | ||
shard_id: u64, | ||
) -> Result<bool, StorageError> { | ||
pub fn key_belongs_to_shard(key: &Box<[u8]>, shard_uid: &ShardUId) -> Result<bool, StorageError> { |
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.
let's fix key type since we are changing this code: key: &[u8]
core/store/src/trie/shard_tries.rs
Outdated
self.0.flat_storage_manager.chunk_view(shard_uid, block_hash, is_view); | ||
let flat_storage_chunk_view = block_hash | ||
.map(|block_hash| self.0.flat_storage_manager.chunk_view(shard_uid, block_hash)) | ||
.flatten(); |
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.
nit: use .and_then
instead of .map(..).flatten()
core/store/src/trie/state_parts.rs
Outdated
}; | ||
let key_end = key_end.as_deref(); | ||
|
||
Ok(flat_storage_chunk_view.iter_flat_state_entries(key_begin, key_end)) |
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.
nit: use iter_flat_state_entries(key_begin.as_deref(), key_end.as_deref())
instead of redefining variables, that looks a bit weird
let in_memory_created_nodes = | ||
trie_values.iter().filter(|entry| !disk_read_hashes.contains(&hash(*entry))).count(); | ||
let trie_creation_duration = trie_creation_start.elapsed(); | ||
tracing::info!( |
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 suggest to consider avoiding manually formatting string for logging in general and use structured logging instead: %values_ref, ..., ?trie_creation_duration, ...
. This s a good practice because it still results in very readable logs, but avoids unnecessary string formatting overhead when log level is not enabled. Also I find that to be a bit easier to grep
in the logs. Not a strong opinion here, just something to consider.
core/store/src/trie/state_parts.rs
Outdated
) -> Result<impl Iterator<Item = (Vec<u8>, Box<[u8]>)> + 'a, StorageError> { | ||
let flat_storage_chunk_view = match &self.flat_storage_chunk_view { | ||
None => { | ||
return Err(StorageError::StorageInconsistentState(format!( |
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 guess we don't need format!
here
Gennerate inner part of state part using flat storage using idea present in #8984. In short, if flat storage head corresponds to the state root for which we sync state, it is enough to read only boundary nodes, and inner trie part can be reconstructed using range of KV pairs from state. The main logic for that is contained in `Trie::get_trie_nodes_for_part_with_flat_storage`. It requires couple of minor changes: * now we allow creating "view" `Trie`s with flat storage as well. As before, we want to avoid creating non-view `Tries` because `TrieCache` accesses may be blocking for chunk processing * `get_head_hash` and `shard_uid` methods for `FlatStorage` allowing to make correct range query to flat storage * `FlatStateValue` moved to `primitives` to allow more general access * prometheus metrics * integration test checking that flat storage is used during normal block processing on client (or wait for #9090) https://nayduck.near.org/#/run/3023 Big sanity test `get_trie_nodes_for_part_with_flat_storage` covering all scenarios I could think of: * results with/without flat storage must match * result with incorrect flat storage must be an error * result with flat storage and missing intermediate node should be still okay
Gennerate inner part of state part using flat storage using idea present in near#8984. In short, if flat storage head corresponds to the state root for which we sync state, it is enough to read only boundary nodes, and inner trie part can be reconstructed using range of KV pairs from state. The main logic for that is contained in `Trie::get_trie_nodes_for_part_with_flat_storage`. It requires couple of minor changes: * now we allow creating "view" `Trie`s with flat storage as well. As before, we want to avoid creating non-view `Tries` because `TrieCache` accesses may be blocking for chunk processing * `get_head_hash` and `shard_uid` methods for `FlatStorage` allowing to make correct range query to flat storage * `FlatStateValue` moved to `primitives` to allow more general access * prometheus metrics * integration test checking that flat storage is used during normal block processing on client (or wait for near#9090) https://nayduck.near.org/#/run/3023 Big sanity test `get_trie_nodes_for_part_with_flat_storage` covering all scenarios I could think of: * results with/without flat storage must match * result with incorrect flat storage must be an error * result with flat storage and missing intermediate node should be still okay
Gennerate inner part of state part using flat storage using idea present in near#8984. In short, if flat storage head corresponds to the state root for which we sync state, it is enough to read only boundary nodes, and inner trie part can be reconstructed using range of KV pairs from state. The main logic for that is contained in `Trie::get_trie_nodes_for_part_with_flat_storage`. It requires couple of minor changes: * now we allow creating "view" `Trie`s with flat storage as well. As before, we want to avoid creating non-view `Tries` because `TrieCache` accesses may be blocking for chunk processing * `get_head_hash` and `shard_uid` methods for `FlatStorage` allowing to make correct range query to flat storage * `FlatStateValue` moved to `primitives` to allow more general access * prometheus metrics * integration test checking that flat storage is used during normal block processing on client (or wait for near#9090) https://nayduck.near.org/#/run/3023 Big sanity test `get_trie_nodes_for_part_with_flat_storage` covering all scenarios I could think of: * results with/without flat storage must match * result with incorrect flat storage must be an error * result with flat storage and missing intermediate node should be still okay
Gennerate inner part of state part using flat storage using idea present in #8984. In short, if flat storage head corresponds to the state root for which we sync state, it is enough to read only boundary nodes, and inner trie part can be reconstructed using range of KV pairs from state. The main logic for that is contained in `Trie::get_trie_nodes_for_part_with_flat_storage`. It requires couple of minor changes: * now we allow creating "view" `Trie`s with flat storage as well. As before, we want to avoid creating non-view `Tries` because `TrieCache` accesses may be blocking for chunk processing * `get_head_hash` and `shard_uid` methods for `FlatStorage` allowing to make correct range query to flat storage * `FlatStateValue` moved to `primitives` to allow more general access * prometheus metrics * integration test checking that flat storage is used during normal block processing on client (or wait for #9090) https://nayduck.near.org/#/run/3023 Big sanity test `get_trie_nodes_for_part_with_flat_storage` covering all scenarios I could think of: * results with/without flat storage must match * result with incorrect flat storage must be an error * result with flat storage and missing intermediate node should be still okay
Gennerate inner part of state part using flat storage using idea present in #8984.
For reference - this makes state sync 100x faster, now SS for shard 3 should complete in 30 minutes :) https://near.zulipchat.com/#narrow/stream/297873-pagoda.2Fnode/topic/State.20Sync.20update.202023-05-23/near/360715229
In short, if flat storage head corresponds to the state root for which we sync state, it is enough to read only boundary nodes, and inner trie part can be reconstructed using range of KV pairs from state. The main logic for that is contained in
Trie::get_trie_nodes_for_part_with_flat_storage
.It requires couple of minor changes:
Trie
s with flat storage as well. As before, we want to avoid creating non-viewTries
becauseTrieCache
accesses may be blocking for chunk processingget_head_hash
andshard_uid
methods forFlatStorage
allowing to make correct range query to flat storageFlatStateValue
moved toprimitives
to allow more general accessTODO
Testing
https://nayduck.near.org/#/run/3023
Big sanity test
get_trie_nodes_for_part_with_flat_storage
covering all scenarios I could think of: