Skip to content
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(resharding): generic trie update retain, including trie storage #12301

Merged
merged 8 commits into from
Oct 29, 2024

Conversation

Longarithm
Copy link
Member

@Longarithm Longarithm commented Oct 23, 2024

Starting #12324.

Implement retain_multi_range for TrieStorage (partial or disk trie), by introducing GenericTrieUpdate which works for both trie storage and memtrie.

  • ~100 lines are caused by changed indents in squash_node
  • ~200 is just a (temporary) copypaste of old trie storage logic to make it compatible with GenericTrieUpdate
  • remaining is the actual logic, including improving the resharding tests to ensure that memtrie and triestorage results are the same.

Some notes on the structure.

GenericTrieUpdate abstracts the way the updated nodes are structured. It doesn't matter what is the node type - it is abstracted to GenericTrieNodePtr. FlatStateValue is the name for the value update. We can migrate everything to actual FlatStateValue, but for now trie storage uses ValueHandle and I don't want to touch it yet.

The methods of GenericTrieUpdate define how to take/put nodes to memory. GenericNodeOrIndex stores a reference to old or updated node. For memtrie old node it is MemTrieNodeId, for triestorage it is CryptoHash.

Finally, we are able to call generic_retain_multi_range_recursive which uses GenericTrieUpdate and recursively descends to the trie, regardless on how it is stored.

If we agree on this, I can proceed later with migrating insert and delete as well, which will eventually lead to removing copypaste code.

Copy link

codecov bot commented Oct 23, 2024

Codecov Report

Attention: Patch coverage is 88.39286% with 39 lines in your changes missing coverage. Please review.

Project coverage is 71.26%. Comparing base (7de36d1) to head (9ed2168).
Report is 7 commits behind head on master.

Files with missing lines Patch % Lines
core/store/src/trie/mem/resharding.rs 87.39% 8 Missing and 7 partials ⚠️
core/store/src/trie/mem/updating.rs 91.27% 13 Missing ⚠️
core/store/src/trie/insert_delete.rs 85.00% 6 Missing and 3 partials ⚠️
core/store/src/trie/mod.rs 50.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #12301      +/-   ##
==========================================
+ Coverage   71.24%   71.26%   +0.01%     
==========================================
  Files         838      838              
  Lines      169020   169257     +237     
  Branches   169020   169257     +237     
==========================================
+ Hits       120424   120624     +200     
- Misses      43351    43384      +33     
- Partials     5245     5249       +4     
Flag Coverage Δ
backward-compatibility 0.16% <0.00%> (-0.01%) ⬇️
db-migration 0.16% <0.00%> (-0.01%) ⬇️
genesis-check 1.23% <0.00%> (-0.01%) ⬇️
integration-tests 39.02% <49.70%> (-0.05%) ⬇️
linux 70.70% <84.82%> (+0.01%) ⬆️
linux-nightly 70.85% <88.39%> (+0.02%) ⬆️
macos 50.43% <84.82%> (+0.12%) ⬆️
pytests 1.54% <0.00%> (-0.01%) ⬇️
sanity-checks 1.35% <0.00%> (-0.01%) ⬇️
unittests 64.22% <84.82%> (+0.08%) ⬆️
upgradability 0.21% <0.00%> (-0.01%) ⬇️

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

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@Longarithm Longarithm changed the title draft(resharding): proof for state witness feat(resharding): generic trie update retain, including trie storage Oct 28, 2024
@Longarithm Longarithm marked this pull request as ready for review October 28, 2024 11:21
@Longarithm Longarithm requested a review from a team as a code owner October 28, 2024 11:21
@Longarithm Longarithm requested a review from robin-near October 28, 2024 12:04
let mut child_memory_usage = 0;
for handle in path.into_iter().rev() {
// First, recompute memory usage, emulating the recursive descent.
let TrieNodeWithSize { node, mut memory_usage } = memory.destroy(handle);
Copy link
Contributor

Choose a reason for hiding this comment

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

I do not have much context, but is it ok that we do memory.destroy(handle) both here and inside squash_node() ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Haha! Yeah, looking at the code, that's what I thought as well, but seems like we are calling memory.store_at right after calling destroy which undoes this.

Comment on lines +444 to +445
memory.store_at(handle, TrieNodeWithSize { node, memory_usage });

Copy link
Contributor

Choose a reason for hiding this comment

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

Aah crap, this along with duplication of memory.destroy(handle) in squash_node and missing else case in if key_deleted completely confused me, but after staring at this for 10 min, I think it makes sense

}
}

pub type UpdatedMemTrieNodeWithSize = GenericUpdatedTrieNodeWithSize<MemTrieNodeId, FlatStateValue>;
Copy link
Contributor

Choose a reason for hiding this comment

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

Confirming.... It seems like we don't use the size parameter for MemTrieNodes anywhere? Could you please confirm this? In which case we can remove this type in favor of GenericUpdatedTrieNode without size?

Copy link
Contributor

Choose a reason for hiding this comment

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

Like, I mean, get rid of type GenericUpdatedTrieNodeWithSize, only continue to use type GenericUpdatedTrieNode and implement size separately for UpdatedTrieStorageNode i.e. GenericUpdatedTrieNode<TrieStorageNodePtr, ValueHandle>.

That should make the code easier to reason about?

Copy link
Member Author

@Longarithm Longarithm Oct 28, 2024

Choose a reason for hiding this comment

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

I don't see how to do it without adding comparable overhead.
The GenericUpdatedTrieNode serves as a generic enum. Adding size to it is the same as implementing GenericUpdatedTrieNodeWithSize.
If I look from GenericTrieUpdate perspective, both node and memory has to be taken simultaneously if generic_get_node is called, and "node with size" struct seems to be useful here again.
Could you be more specific on how to get rid of GenericUpdatedTrieNodeWithSize?

The reason why we need a change in memtries is described in comment to MemTrieUpdate::generic_take_node.

Copy link
Contributor

Choose a reason for hiding this comment

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

Let me explore a bit myself on this and then suggest

Copy link
Contributor

Choose a reason for hiding this comment

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

Aah crap, you're right! I was under the impression that memory calculation happens independently at the end of update/insert function, but seems like it's built into the function call. I suppose what we have currently is best even though I believe it's irrelevant for memtries? Or have I got that detail wrong?

Copy link
Contributor

Choose a reason for hiding this comment

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

Spoke offline

Copy link
Contributor

@shreyan-gupta shreyan-gupta left a comment

Choose a reason for hiding this comment

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

I hope we can get to generic_insert_delete soon!
Please do take a look at the Size interface and check if we really need it for memtrie side of things?

core/store/src/trie/mem/resharding.rs Show resolved Hide resolved
core/store/src/trie/mem/resharding.rs Show resolved Hide resolved
core/store/src/trie/mem/resharding.rs Outdated Show resolved Hide resolved
/// An updated node with its memory usage.
/// Needed to recompute subtree function (memory usage) on the fly.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GenericUpdatedTrieNodeWithSize<GenericTrieNodePtr, FlatStateValue> {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Is there anything conceptually linking GenericUpdatedTrieNodeWithSize with FlatStateValue (the enum?). I was expecting something like GenericValueHandle.

Copy link
Contributor

@robin-near robin-near left a comment

Choose a reason for hiding this comment

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

LGTM, overall structure looks great; didn't dig into details too much but I trust testing as well as other people who have reviewed this :)


/// An updated node - a node that will eventually become an in-memory trie node.
/// Trait for trie values to get their length.
pub trait HasLength {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: perhaps name this something a little more specific, like HasValueLength; HasLength is a very generic name when reading the trait bound in another file.

node: GenericNodeOrIndex<GenericTrieNodePtr>,
) -> Result<GenericUpdatedNodeId, StorageError>;

/// Takes a node from the set of updated nodes, setting it to None.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: It might help readers to add a high level summary of the slot mechanism to the trait's documentation. This stuff itself may not be so confusing but when I was reading the slot mechanism for the first time before, I was very confused when I also had to understand a bunch of other things in the update logic.

///
/// Note that it has nothing to do with `TrieUpdate` used for runtime to store
/// temporary state changes (TODO(#12324) - consider renaming it).
pub(crate) trait GenericTrieUpdate<'a, GenericTrieNodePtr, GenericValueHandle> {
Copy link
Contributor

Choose a reason for hiding this comment

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

nice comment!

Copy link
Contributor

@Trisfald Trisfald left a comment

Choose a reason for hiding this comment

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

🚀

@Longarithm Longarithm added this pull request to the merge queue Oct 29, 2024
Merged via the queue into near:master with commit 598a2f4 Oct 29, 2024
29 checks passed
@Longarithm Longarithm deleted the sw-proof branch October 29, 2024 11:00
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.

5 participants