Remove getNodeData experimental feature (#4559)

This commit is contained in:
Andrew Ashikhmin 2022-06-29 14:23:00 +02:00 committed by GitHub
parent 880a339456
commit 8f86c5d615
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 1 additions and 176 deletions

View File

@ -4,46 +4,9 @@ import (
"os" "os"
"strconv" "strconv"
"sync" "sync"
"sync/atomic"
"time" "time"
) )
// atomic: bit 0 is the value, bit 1 is the initialized flag
var getNodeData uint32
const (
gndValueFlag = 1 << iota
gndInitializedFlag
)
// IsGetNodeData indicates whether the GetNodeData functionality should be enabled.
// By default that's driven by the presence or absence of DISABLE_GET_NODE_DATA environment variable.
func IsGetNodeData() bool {
x := atomic.LoadUint32(&getNodeData)
if x&gndInitializedFlag != 0 { // already initialized
return x&gndValueFlag != 0
}
RestoreGetNodeData()
return IsGetNodeData()
}
// RestoreGetNodeData enables or disables the GetNodeData functionality
// according to the presence or absence of GET_NODE_DATA environment variable.
func RestoreGetNodeData() {
_, envVarSet := os.LookupEnv("GET_NODE_DATA")
OverrideGetNodeData(envVarSet)
}
// OverrideGetNodeData allows to explicitly enable or disable the GetNodeData functionality.
func OverrideGetNodeData(val bool) {
if val {
atomic.StoreUint32(&getNodeData, gndInitializedFlag|gndValueFlag)
} else {
atomic.StoreUint32(&getNodeData, gndInitializedFlag)
}
}
var ( var (
bigRoTx uint bigRoTx uint
getBigRoTx sync.Once getBigRoTx sync.Once

View File

@ -25,11 +25,9 @@ import (
libcommon "github.com/ledgerwatch/erigon-lib/common" libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/common/cmp" "github.com/ledgerwatch/erigon-lib/common/cmp"
"github.com/ledgerwatch/erigon/common" "github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/common/debug"
"github.com/ledgerwatch/erigon/core/types/accounts" "github.com/ledgerwatch/erigon/core/types/accounts"
"github.com/ledgerwatch/erigon/crypto" "github.com/ledgerwatch/erigon/crypto"
"github.com/ledgerwatch/erigon/ethdb" "github.com/ledgerwatch/erigon/ethdb"
"github.com/ledgerwatch/log/v3"
) )
var ( var (
@ -52,8 +50,6 @@ type Trie struct {
root node root node
newHasherFunc func() *hasher newHasherFunc func() *hasher
hashMap map[common.Hash]node
} }
// New creates a trie with an existing root node from db. // New creates a trie with an existing root node from db.
@ -67,7 +63,6 @@ type Trie struct {
func New(root common.Hash) *Trie { func New(root common.Hash) *Trie {
trie := &Trie{ trie := &Trie{
newHasherFunc: func() *hasher { return newHasher( /*valueNodesRlpEncoded = */ false) }, newHasherFunc: func() *hasher { return newHasher( /*valueNodesRlpEncoded = */ false) },
hashMap: make(map[common.Hash]node),
} }
if (root != common.Hash{}) && root != EmptyRoot { if (root != common.Hash{}) && root != EmptyRoot {
trie.root = hashNode{hash: root[:]} trie.root = hashNode{hash: root[:]}
@ -80,7 +75,6 @@ func New(root common.Hash) *Trie {
func NewTestRLPTrie(root common.Hash) *Trie { func NewTestRLPTrie(root common.Hash) *Trie {
trie := &Trie{ trie := &Trie{
newHasherFunc: func() *hasher { return newHasher( /*valueNodesRlpEncoded = */ true) }, newHasherFunc: func() *hasher { return newHasher( /*valueNodesRlpEncoded = */ true) },
hashMap: make(map[common.Hash]node),
} }
if (root != common.Hash{}) && root != EmptyRoot { if (root != common.Hash{}) && root != EmptyRoot {
trie.root = hashNode{hash: root[:]} trie.root = hashNode{hash: root[:]}
@ -515,7 +509,6 @@ func (t *Trie) insertRecursive(origNode node, key []byte, pos int, value node) (
// replacing nodes except accounts // replacing nodes except accounts
if !origNok { if !origNok {
t.evictSubtreeFromHashMap(origNode)
return true, value return true, value
} }
} }
@ -538,14 +531,12 @@ func (t *Trie) insertRecursive(origNode node, key []byte, pos int, value node) (
if matchlen == len(n.Key) || n.Key[matchlen] == 16 { if matchlen == len(n.Key) || n.Key[matchlen] == 16 {
updated, nn = t.insertRecursive(n.Val, key, pos+matchlen, value) updated, nn = t.insertRecursive(n.Val, key, pos+matchlen, value)
if updated { if updated {
t.evictNodeFromHashMap(n)
n.Val = nn n.Val = nn
n.ref.len = 0 n.ref.len = 0
} }
newNode = n newNode = n
} else { } else {
// Otherwise branch out at the index where they differ. // Otherwise branch out at the index where they differ.
t.evictNodeFromHashMap(n)
var c1 node var c1 node
if len(n.Key) == matchlen+1 { if len(n.Key) == matchlen+1 {
c1 = n.Val c1 = n.Val
@ -588,7 +579,6 @@ func (t *Trie) insertRecursive(origNode node, key []byte, pos int, value node) (
case i1: case i1:
updated, nn = t.insertRecursive(n.child1, key, pos+1, value) updated, nn = t.insertRecursive(n.child1, key, pos+1, value)
if updated { if updated {
t.evictNodeFromHashMap(n)
n.child1 = nn n.child1 = nn
n.ref.len = 0 n.ref.len = 0
} }
@ -596,13 +586,11 @@ func (t *Trie) insertRecursive(origNode node, key []byte, pos int, value node) (
case i2: case i2:
updated, nn = t.insertRecursive(n.child2, key, pos+1, value) updated, nn = t.insertRecursive(n.child2, key, pos+1, value)
if updated { if updated {
t.evictNodeFromHashMap(n)
n.child2 = nn n.child2 = nn
n.ref.len = 0 n.ref.len = 0
} }
newNode = n newNode = n
default: default:
t.evictNodeFromHashMap(n)
var child node var child node
if len(key) == pos+1 { if len(key) == pos+1 {
child = value child = value
@ -622,7 +610,6 @@ func (t *Trie) insertRecursive(origNode node, key []byte, pos int, value node) (
case *fullNode: case *fullNode:
child := n.Children[key[pos]] child := n.Children[key[pos]]
if child == nil { if child == nil {
t.evictNodeFromHashMap(n)
if len(key) == pos+1 { if len(key) == pos+1 {
n.Children[key[pos]] = value n.Children[key[pos]] = value
} else { } else {
@ -633,7 +620,6 @@ func (t *Trie) insertRecursive(origNode node, key []byte, pos int, value node) (
} else { } else {
updated, nn = t.insertRecursive(child, key, pos+1, value) updated, nn = t.insertRecursive(child, key, pos+1, value)
if updated { if updated {
t.evictNodeFromHashMap(n)
n.Children[key[pos]] = nn n.Children[key[pos]] = nn
n.ref.len = 0 n.ref.len = 0
} }
@ -760,14 +746,6 @@ func (t *Trie) hook(hex []byte, n node, hash []byte) error {
} }
func (t *Trie) touchAll(n node, hex []byte, del bool, incarnation uint64) { func (t *Trie) touchAll(n node, hex []byte, del bool, incarnation uint64) {
if del {
t.evictNodeFromHashMap(n)
} else if len(n.reference()) == common.HashLength && debug.IsGetNodeData() {
var key common.Hash
copy(key[:], n.reference())
t.hashMap[key] = n
}
switch n := n.(type) { switch n := n.(type) {
case *shortNode: case *shortNode:
if _, ok := n.Val.(valueNode); !ok { if _, ok := n.Val.(valueNode); !ok {
@ -819,7 +797,6 @@ func (t *Trie) convertToShortNode(child node, pos uint) node {
// might not be loaded yet, resolve it just for this // might not be loaded yet, resolve it just for this
// check. // check.
if short, ok := child.(*shortNode); ok { if short, ok := child.(*shortNode); ok {
t.evictNodeFromHashMap(child)
k := make([]byte, len(short.Key)+1) k := make([]byte, len(short.Key)+1)
k[0] = byte(pos) k[0] = byte(pos)
copy(k[1:], short.Key) copy(k[1:], short.Key)
@ -854,7 +831,6 @@ func (t *Trie) deleteRecursive(origNode node, key []byte, keyStart int, preserve
} }
if removeNodeEntirely { if removeNodeEntirely {
t.evictNodeFromHashMap(n)
updated = true updated = true
touchKey := key[:keyStart+matchlen] touchKey := key[:keyStart+matchlen]
if touchKey[len(touchKey)-1] == 16 { if touchKey[len(touchKey)-1] == 16 {
@ -871,7 +847,6 @@ func (t *Trie) deleteRecursive(origNode node, key []byte, keyStart int, preserve
if !updated { if !updated {
newNode = n newNode = n
} else { } else {
t.evictNodeFromHashMap(n)
if nn == nil { if nn == nil {
newNode = nil newNode = nil
} else { } else {
@ -905,7 +880,6 @@ func (t *Trie) deleteRecursive(origNode node, key []byte, keyStart int, preserve
if !updated { if !updated {
newNode = n newNode = n
} else { } else {
t.evictNodeFromHashMap(n)
if nn == nil { if nn == nil {
newNode = t.convertToShortNode(n.child2, uint(i2)) newNode = t.convertToShortNode(n.child2, uint(i2))
} else { } else {
@ -919,7 +893,6 @@ func (t *Trie) deleteRecursive(origNode node, key []byte, keyStart int, preserve
if !updated { if !updated {
newNode = n newNode = n
} else { } else {
t.evictNodeFromHashMap(n)
if nn == nil { if nn == nil {
newNode = t.convertToShortNode(n.child1, uint(i1)) newNode = t.convertToShortNode(n.child1, uint(i1))
} else { } else {
@ -940,7 +913,6 @@ func (t *Trie) deleteRecursive(origNode node, key []byte, keyStart int, preserve
if !updated { if !updated {
newNode = n newNode = n
} else { } else {
t.evictNodeFromHashMap(n)
n.Children[key[keyStart]] = nn n.Children[key[keyStart]] = nn
// Check how many non-nil entries are left after deleting and // Check how many non-nil entries are left after deleting and
// reduce the full node to a short node if only one entry is // reduce the full node to a short node if only one entry is
@ -1078,13 +1050,7 @@ func (t *Trie) Reset() {
} }
func (t *Trie) getHasher() *hasher { func (t *Trie) getHasher() *hasher {
h := t.newHasherFunc() return t.newHasherFunc()
if debug.IsGetNodeData() {
h.callback = func(key common.Hash, nd node) {
t.hashMap[key] = nd
}
}
return h
} }
// DeepHash returns internal hash of a node reachable by the specified key prefix. // DeepHash returns internal hash of a node reachable by the specified key prefix.
@ -1135,8 +1101,6 @@ func (t *Trie) EvictNode(hex []byte) {
// can work with other nodes type // can work with other nodes type
} }
t.evictSubtreeFromHashMap(nd)
var hn common.Hash var hn common.Hash
if nd == nil { if nd == nil {
fmt.Printf("nd == nil, hex %x, parent node: %T\n", hex, parent) fmt.Printf("nd == nil, hex %x, parent node: %T\n", hex, parent)
@ -1214,65 +1178,3 @@ func (t *Trie) TrieSize() int {
func (t *Trie) NumberOfAccounts() int { func (t *Trie) NumberOfAccounts() int {
return calcSubtreeNodes(t.root) return calcSubtreeNodes(t.root)
} }
// GetNodeByHash gets node's RLP by hash.
func (t *Trie) GetNodeByHash(hash common.Hash) []byte {
nd := t.hashMap[hash]
if nd == nil {
return nil
}
h := t.getHasher()
defer returnHasherToPool(h)
rlp, err := h.hashChildren(nd, 0)
if err != nil {
log.Warn("GetNodeByHash error while producing node RLP", "err", err)
return nil
}
return libcommon.Copy(rlp)
}
func (t *Trie) evictNodeFromHashMap(nd node) {
if !debug.IsGetNodeData() || nd == nil {
return
}
if len(nd.reference()) == common.HashLength {
var key common.Hash
copy(key[:], nd.reference())
delete(t.hashMap, key)
}
}
func (t *Trie) evictSubtreeFromHashMap(n node) {
if !debug.IsGetNodeData() {
return
}
t.evictNodeFromHashMap(n)
switch n := n.(type) {
case *shortNode:
if _, ok := n.Val.(valueNode); !ok {
t.evictSubtreeFromHashMap(n.Val)
}
case *duoNode:
t.evictSubtreeFromHashMap(n.child1)
t.evictSubtreeFromHashMap(n.child2)
case *fullNode:
for _, child := range n.Children {
if child != nil {
t.evictSubtreeFromHashMap(child)
}
}
case *accountNode:
if n.storage != nil {
t.evictSubtreeFromHashMap(n.storage)
}
}
}
// HashMapSize returns the number of entries in trie's hash map.
func (t *Trie) HashMapSize() int {
return len(t.hashMap)
}

View File

@ -27,7 +27,6 @@ import (
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/ledgerwatch/erigon/common" "github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/common/debug"
"github.com/ledgerwatch/erigon/core/types/accounts" "github.com/ledgerwatch/erigon/core/types/accounts"
"github.com/ledgerwatch/erigon/crypto" "github.com/ledgerwatch/erigon/crypto"
"github.com/ledgerwatch/erigon/rlp" "github.com/ledgerwatch/erigon/rlp"
@ -282,45 +281,6 @@ func TestDeepHash(t *testing.T) {
} }
} }
func TestHashMapLeak(t *testing.T) {
debug.OverrideGetNodeData(true)
defer debug.OverrideGetNodeData(false)
// freeze the randomness
random := rand.New(rand.NewSource(794656320434))
// now create a trie with some small and some big leaves
trie := newEmpty()
nTouches := 256 * 10
var key [1]byte
var val [8]byte
for i := 0; i < nTouches; i++ {
key[0] = byte(random.Intn(256))
binary.BigEndian.PutUint64(val[:], random.Uint64())
option := random.Intn(3)
if option == 0 {
// small leaf node
trie.Update(key[:], val[:])
} else if option == 1 {
// big leaf node
trie.Update(key[:], crypto.Keccak256(val[:]))
} else {
// test delete as well
trie.Delete(key[:])
}
}
// check the size of trie's hash map
trie.Hash()
nHashes := trie.HashMapSize()
nExpected := 1 + 16 + 256/3
assert.GreaterOrEqual(t, nHashes, nExpected*7/8)
assert.LessOrEqual(t, nHashes, nExpected*9/8)
}
func genRandomByteArrayOfLen(length uint) []byte { func genRandomByteArrayOfLen(length uint) []byte {
array := make([]byte, length) array := make([]byte, length)
for i := uint(0); i < length; i++ { for i := uint(0); i < length; i++ {