trie: while fast syncing, don't keep trie nodes in memory (#3186)

This commit is contained in:
Péter Szilágyi 2016-10-21 18:34:17 +03:00 committed by Felix Lange
parent 437c1e40b2
commit f8608a5228

View File

@ -31,9 +31,9 @@ var ErrNotRequested = errors.New("not requested")
// request represents a scheduled or already in-flight state retrieval request.
type request struct {
hash common.Hash // Hash of the node data content to retrieve
data []byte // Data content of the node, cached until all subtrees complete
object *node // Target node to populate with retrieved data (hashnode originally)
hash common.Hash // Hash of the node data content to retrieve
data []byte // Data content of the node, cached until all subtrees complete
raw bool // Whether this is a raw entry (code) or a trie node
parents []*request // Parent state nodes referencing this entry (notify all upon completion)
depth int // Depth level within the trie the node is located to prioritise DFS
@ -86,9 +86,7 @@ func (s *TrieSync) AddSubTrie(root common.Hash, depth int, parent common.Hash, c
return
}
// Assemble the new sub-trie sync request
node := node(hashNode(root.Bytes()))
req := &request{
object: &node,
hash: root,
depth: depth,
callback: callback,
@ -120,6 +118,7 @@ func (s *TrieSync) AddRawEntry(hash common.Hash, depth int, parent common.Hash)
// Assemble the new sub-trie sync request
req := &request{
hash: hash,
raw: true,
depth: depth,
}
// If this sub-trie has a designated parent, link them together
@ -152,7 +151,7 @@ func (s *TrieSync) Process(results []SyncResult) (int, error) {
return i, ErrNotRequested
}
// If the item is a raw entry request, commit directly
if request.object == nil {
if request.raw {
request.data = item.Data
s.commit(request, nil)
continue
@ -162,11 +161,10 @@ func (s *TrieSync) Process(results []SyncResult) (int, error) {
if err != nil {
return i, err
}
*request.object = node
request.data = item.Data
// Create and schedule a request for all the children nodes
requests, err := s.children(request)
requests, err := s.children(request, node)
if err != nil {
return i, err
}
@ -203,27 +201,25 @@ func (s *TrieSync) schedule(req *request) {
// children retrieves all the missing children of a state trie entry for future
// retrieval scheduling.
func (s *TrieSync) children(req *request) ([]*request, error) {
func (s *TrieSync) children(req *request, object node) ([]*request, error) {
// Gather all the children of the node, irrelevant whether known or not
type child struct {
node *node
node node
depth int
}
children := []child{}
switch node := (*req.object).(type) {
switch node := (object).(type) {
case *shortNode:
node = node.copy() // Prevents linking all downloaded nodes together.
children = []child{{
node: &node.Val,
node: node.Val,
depth: req.depth + len(node.Key),
}}
case *fullNode:
node = node.copy()
for i := 0; i < 17; i++ {
if node.Children[i] != nil {
children = append(children, child{
node: &node.Children[i],
node: node.Children[i],
depth: req.depth + 1,
})
}
@ -236,23 +232,21 @@ func (s *TrieSync) children(req *request) ([]*request, error) {
for _, child := range children {
// Notify any external watcher of a new key/value node
if req.callback != nil {
if node, ok := (*child.node).(valueNode); ok {
if node, ok := (child.node).(valueNode); ok {
if err := req.callback(node, req.hash); err != nil {
return nil, err
}
}
}
// If the child references another node, resolve or schedule
if node, ok := (*child.node).(hashNode); ok {
if node, ok := (child.node).(hashNode); ok {
// Try to resolve the node from the local database
blob, _ := s.database.Get(node)
if local, err := decodeNode(node[:], blob, 0); local != nil && err == nil {
*child.node = local
continue
}
// Locally unknown node, schedule for retrieval
requests = append(requests, &request{
object: child.node,
hash: common.BytesToHash(node),
parents: []*request{req},
depth: child.depth,