mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-23 05:06:43 +00:00
doc
Signed-off-by: jsvisa <delweng@gmail.com>
This commit is contained in:
parent
f5f0129f85
commit
0281e226bf
3 changed files with 47 additions and 145 deletions
|
|
@ -339,6 +339,8 @@ func (tree *layerTree) lookupStorage(accountHash common.Hash, slotHash common.Ha
|
|||
return l, nil
|
||||
}
|
||||
|
||||
// lookupNode returns the layer that is guaranteed to contain the trie node
|
||||
// data corresponding to the specified state root being queried.
|
||||
func (tree *layerTree) lookupNode(accountHash common.Hash, path string, state common.Hash) (layer, error) {
|
||||
// Hold the read lock to prevent the unexpected layer changes
|
||||
tree.lock.RLock()
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
// storageNodesShardCount is the number of shards used for storage nodes.
|
||||
const storageNodesShardCount = 16
|
||||
|
||||
// storageKey returns a key for uniquely identifying the storage slot.
|
||||
|
|
@ -52,10 +53,17 @@ type lookup struct {
|
|||
// where the slot was modified, with the order from oldest to newest.
|
||||
storages map[[64]byte][]common.Hash
|
||||
|
||||
// accountNodes represents the mutation history for specific account
|
||||
// trie nodes. The key is the trie path of the node, and the value is a slice
|
||||
// of **diff layer** IDs indicating where the account was modified,
|
||||
// with the order from oldest to newest.
|
||||
accountNodes map[string][]common.Hash
|
||||
|
||||
// Key is accountHash.Hex() + path, distributed across 16 shards
|
||||
// This eliminates the need for two-level map lookups
|
||||
// storageNodes represents the mutation history for specific storage
|
||||
// slot trie nodes, distributed across 16 shards for efficiency.
|
||||
// The key is the account address hash and the trie path of the node,
|
||||
// the value is a slice of **diff layer** IDs indicating where the
|
||||
// slot was modified, with the order from oldest to newest.
|
||||
storageNodes [storageNodesShardCount]map[string][]common.Hash
|
||||
|
||||
// descendant is the callback indicating whether the layer with
|
||||
|
|
@ -186,6 +194,17 @@ func (l *lookup) storageTip(accountHash common.Hash, slotHash common.Hash, state
|
|||
return common.Hash{}
|
||||
}
|
||||
|
||||
// nodeTip traverses the layer list associated with the given account and path
|
||||
// in reverse order to locate the first entry that either matches
|
||||
// the specified stateID or is a descendant of it.
|
||||
//
|
||||
// If found, the trie node data corresponding to the supplied stateID resides
|
||||
// in that layer. Otherwise, two scenarios are possible:
|
||||
//
|
||||
// (a) the trie node remains unmodified from the current disk layer up to
|
||||
// the state layer specified by the stateID: fallback to the disk layer for
|
||||
// data retrieval, (b) or the layer specified by the stateID is stale: reject
|
||||
// the data retrieval.
|
||||
func (l *lookup) nodeTip(accountHash common.Hash, path string, stateID common.Hash, base common.Hash) common.Hash {
|
||||
var list []common.Hash
|
||||
if accountHash == (common.Hash{}) {
|
||||
|
|
@ -448,17 +467,16 @@ func (l *lookup) removeStorageNodes(state common.Hash, nodes map[common.Hash]map
|
|||
for accountHash, slots := range nodes {
|
||||
accountHex := accountHash.Hex()
|
||||
for path := range slots {
|
||||
// Construct the combined key and find the correct shard
|
||||
shard := l.storageNodes[getStorageShardIndex(path)]
|
||||
key := accountHex + path
|
||||
shardIndex := getStorageShardIndex(path)
|
||||
found, list := removeFromList(l.storageNodes[shardIndex][key], state)
|
||||
found, list := removeFromList(shard[key], state)
|
||||
if !found {
|
||||
return fmt.Errorf("storage lookup is not found, %x %x, state: %x", accountHash, path, state)
|
||||
}
|
||||
if len(list) != 0 {
|
||||
l.storageNodes[shardIndex][key] = list
|
||||
shard[key] = list
|
||||
} else {
|
||||
delete(l.storageNodes[shardIndex], key)
|
||||
delete(shard, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package pathdb
|
|||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -43,173 +42,56 @@ func generateRandomStorageNodes(accountCount, nodesPerAccount int) map[common.Ha
|
|||
return storageNodes
|
||||
}
|
||||
|
||||
// addNodes is a helper method for testing that adds nodes to the lookup structure
|
||||
func (l *lookup) addNodes(stateHash common.Hash, accountNodes map[string]*trienode.Node, storageNodes map[common.Hash]map[string]*trienode.Node) {
|
||||
// Add account nodes
|
||||
for path := range accountNodes {
|
||||
list, exists := l.accountNodes[path]
|
||||
if !exists {
|
||||
list = make([]common.Hash, 0, 16)
|
||||
}
|
||||
list = append(list, stateHash)
|
||||
l.accountNodes[path] = list
|
||||
}
|
||||
|
||||
// Add storage nodes using single-level sharded structure
|
||||
for accountHash, slots := range storageNodes {
|
||||
accountHex := accountHash.Hex()
|
||||
|
||||
for path := range slots {
|
||||
// Construct the combined key but use only path for shard calculation
|
||||
key := accountHex + path
|
||||
shardIndex := getStorageShardIndex(path) // Use only path for sharding
|
||||
shardMap := l.storageNodes[shardIndex]
|
||||
|
||||
list, exists := shardMap[key]
|
||||
if !exists {
|
||||
list = make([]common.Hash, 0, 16)
|
||||
}
|
||||
list = append(list, stateHash)
|
||||
shardMap[key] = list
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAddNodes(b *testing.B) {
|
||||
tests := []struct {
|
||||
name string
|
||||
accountNodeCount int
|
||||
storageAccountCount int
|
||||
nodesPerAccount int
|
||||
}{
|
||||
{
|
||||
name: "Small-100-accounts-10-nodes",
|
||||
accountNodeCount: 100,
|
||||
storageAccountCount: 100,
|
||||
nodesPerAccount: 10,
|
||||
},
|
||||
{
|
||||
name: "Medium-500-accounts-20-nodes",
|
||||
accountNodeCount: 500,
|
||||
storageAccountCount: 500,
|
||||
nodesPerAccount: 20,
|
||||
},
|
||||
{
|
||||
name: "Large-2000-accounts-40-nodes",
|
||||
accountNodeCount: 2000,
|
||||
storageAccountCount: 2000,
|
||||
nodesPerAccount: 40,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
b.Run(tc.name, func(b *testing.B) {
|
||||
accountNodes := generateRandomAccountNodes(tc.accountNodeCount)
|
||||
storageNodes := generateRandomStorageNodes(tc.storageAccountCount, tc.nodesPerAccount)
|
||||
storageNodes := generateRandomStorageNodes(tc.accountNodeCount, tc.nodesPerAccount)
|
||||
|
||||
lookup := &lookup{
|
||||
accountNodes: make(map[string][]common.Hash),
|
||||
}
|
||||
|
||||
// Initialize all 16 storage node shards
|
||||
for i := 0; i < 16; i++ {
|
||||
for i := 0; i < storageNodesShardCount; i++ {
|
||||
lookup.storageNodes[i] = make(map[string][]common.Hash)
|
||||
}
|
||||
|
||||
var stateHash common.Hash
|
||||
rand.Read(stateHash[:])
|
||||
var state common.Hash
|
||||
rand.Read(state[:])
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Clear the nodes maps for each iteration
|
||||
lookup.accountNodes = make(map[string][]common.Hash)
|
||||
// Reinitialize all 16 storage node shards
|
||||
for j := 0; j < 16; j++ {
|
||||
// Reset the lookup instance for each benchmark iteration
|
||||
for j := 0; j < storageNodesShardCount; j++ {
|
||||
lookup.storageNodes[j] = make(map[string][]common.Hash)
|
||||
}
|
||||
|
||||
lookup.addNodes(stateHash, accountNodes, storageNodes)
|
||||
lookup.addStorageNodes(state, storageNodes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConcurrentStorageNodesUpdate(b *testing.T) {
|
||||
// Create a lookup instance
|
||||
lookup := &lookup{
|
||||
accountNodes: make(map[string][]common.Hash),
|
||||
}
|
||||
// Initialize all storage node shards
|
||||
for i := 0; i < storageNodesShardCount; i++ {
|
||||
lookup.storageNodes[i] = make(map[string][]common.Hash)
|
||||
}
|
||||
|
||||
// Create test data with known shard distribution
|
||||
testData := map[common.Hash]map[string]*trienode.Node{}
|
||||
|
||||
// Create accounts that will distribute across different shards
|
||||
for i := 0; i < 100; i++ {
|
||||
var accountHash common.Hash
|
||||
accountHash[0] = byte(i)
|
||||
|
||||
testData[accountHash] = make(map[string]*trienode.Node)
|
||||
|
||||
// Create paths that will hash to different shards
|
||||
for j := 0; j < 10; j++ {
|
||||
path := fmt.Sprintf("path_%d_%d", i, j)
|
||||
var nodeHash common.Hash
|
||||
nodeHash[0] = byte(j)
|
||||
|
||||
testData[accountHash][path] = &trienode.Node{Hash: nodeHash}
|
||||
}
|
||||
}
|
||||
|
||||
// Add nodes using the concurrent method
|
||||
var stateHash common.Hash
|
||||
stateHash[0] = 0x42
|
||||
lookup.addNodes(stateHash, nil, testData)
|
||||
|
||||
// Verify that all nodes were added correctly
|
||||
totalNodes := 0
|
||||
for accountHash, slots := range testData {
|
||||
accountHex := accountHash.Hex()
|
||||
for path := range slots {
|
||||
key := accountHex + path
|
||||
shardIndex := getStorageShardIndex(path)
|
||||
|
||||
list, exists := lookup.storageNodes[shardIndex][key]
|
||||
if !exists {
|
||||
b.Errorf("Node not found: account=%x, path=%s, shard=%d", accountHash, path, shardIndex)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(list) != 1 {
|
||||
b.Errorf("Expected 1 state hash, got %d: account=%x, path=%s", len(list), accountHash, path)
|
||||
continue
|
||||
}
|
||||
|
||||
if list[0] != stateHash {
|
||||
b.Errorf("Expected state hash %x, got %x: account=%x, path=%s", stateHash, list[0], accountHash, path)
|
||||
continue
|
||||
}
|
||||
|
||||
totalNodes++
|
||||
}
|
||||
}
|
||||
|
||||
expectedTotal := 100 * 10 // 100 accounts * 10 nodes each
|
||||
if totalNodes != expectedTotal {
|
||||
b.Errorf("Expected %d total nodes, got %d", expectedTotal, totalNodes)
|
||||
}
|
||||
|
||||
// Verify shard distribution
|
||||
for i := 0; i < storageNodesShardCount; i++ {
|
||||
shardSize := len(lookup.storageNodes[i])
|
||||
if shardSize == 0 {
|
||||
b.Logf("Shard %d is empty", i)
|
||||
} else {
|
||||
b.Logf("Shard %d has %d entries", i, shardSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue