state: debug_stateSize to retrieve the latest state size

This commit is contained in:
jsvisa 2025-09-03 06:12:16 +08:00 committed by Gary Rong
parent c99f43e41a
commit 70cd55dcd2
7 changed files with 59 additions and 47 deletions

View file

@ -108,7 +108,7 @@ if one is set. Otherwise it prints the genesis from the datadir.`,
utils.MetricsInfluxDBTokenFlag,
utils.MetricsInfluxDBBucketFlag,
utils.MetricsInfluxDBOrganizationFlag,
utils.MetricsStateSizeFlag,
utils.StateSizeTrackingFlag,
utils.TxLookupLimitFlag,
utils.VMTraceFlag,
utils.VMTraceJsonConfigFlag,

View file

@ -200,7 +200,7 @@ var (
utils.MetricsInfluxDBTokenFlag,
utils.MetricsInfluxDBBucketFlag,
utils.MetricsInfluxDBOrganizationFlag,
utils.MetricsStateSizeFlag,
utils.StateSizeTrackingFlag,
}
)

View file

@ -628,6 +628,12 @@ var (
TakesFile: true,
Category: flags.MiscCategory,
}
StateSizeTrackingFlag = &cli.BoolFlag{
Name: "state-size-tracking",
Usage: "Enable state size tracking",
Value: ethconfig.Defaults.EnableStateSizeTracking,
Category: flags.MiscCategory,
}
// RPC settings
IPCDisabledFlag = &cli.BoolFlag{
@ -966,13 +972,6 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
Value: metrics.DefaultConfig.InfluxDBOrganization,
Category: flags.MetricsCategory,
}
MetricsStateSizeFlag = &cli.BoolFlag{
Name: "metrics.statesize",
Usage: "Enable state size tracking for metrics collection",
Value: ethconfig.Defaults.EnableStateSizeTracking,
Category: flags.MetricsCategory,
}
)
var (
@ -1734,7 +1733,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
}
}
if ctx.Bool(MetricsEnabledFlag.Name) && ctx.Bool(MetricsStateSizeFlag.Name) {
if ctx.Bool(StateSizeTrackingFlag.Name) {
log.Info("Enabling state size metrics")
cfg.EnableStateSizeTracking = true
}
@ -2221,8 +2220,8 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
// - DATADIR/triedb/verkle.journal
TrieJournalDirectory: stack.ResolvePath("triedb"),
// Enable state size tracking if metrics and state size metrics are both enabled
EnableStateSizeTracking: ctx.Bool(MetricsEnabledFlag.Name) && ctx.Bool(MetricsStateSizeFlag.Name),
// Enable state size tracking if enabled
EnableStateSizeTracking: ctx.Bool(StateSizeTrackingFlag.Name),
}
if options.ArchiveMode && !options.Preimages {
options.Preimages = true

View file

@ -2814,3 +2814,8 @@ func (bc *BlockChain) SetTrieFlushInterval(interval time.Duration) {
func (bc *BlockChain) GetTrieFlushInterval() time.Duration {
return time.Duration(bc.flushInterval.Load())
}
// StateSizer returns the state size tracker, or nil if it's not initialized
func (bc *BlockChain) StateSizer() *state.SizeTracker {
return bc.stateSizer
}

View file

@ -26,12 +26,10 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/triedb"
"golang.org/x/sync/errgroup"
)
@ -40,25 +38,10 @@ const (
statEvictThreshold = 128 // the depth of statistic to be preserved
)
// Metrics for uploading the state statistics.
var (
blockInfoGauge = metrics.NewRegisteredGaugeInfo("state/size/block", nil)
accountsGauge = metrics.NewRegisteredGauge("state/size/account/count", nil)
accountBytesGauge = metrics.NewRegisteredGauge("state/size/account/bytes", nil)
storagesGauge = metrics.NewRegisteredGauge("state/size/storage/count", nil)
storageBytesGauge = metrics.NewRegisteredGauge("state/size/storage/bytes", nil)
accountTrienodesGauge = metrics.NewRegisteredGauge("state/size/trienode/account/count", nil)
accountTrienodeBytesGauge = metrics.NewRegisteredGauge("state/size/trienode/account/bytes", nil)
storageTrienodesGauge = metrics.NewRegisteredGauge("state/size/trienode/storage/count", nil)
storageTrienodeBytesGauge = metrics.NewRegisteredGauge("state/size/trienode/storage/bytes", nil)
contractCodesGauge = metrics.NewRegisteredGauge("state/size/contractcode/count", nil)
contractCodeBytesGauge = metrics.NewRegisteredGauge("state/size/contractcode/bytes", nil)
)
// Database key scheme for states.
var (
accountKeySize = int64(len(rawdb.SnapshotAccountPrefix) + common.HashLength)
storageKeySize = int64(len(rawdb.SnapshotStoragePrefix) + common.HashLength + common.HashLength)
storageKeySize = int64(len(rawdb.SnapshotStoragePrefix) + common.HashLength*2)
accountTrienodePrefixSize = int64(len(rawdb.TrieNodeAccountPrefix))
storageTrienodePrefixSize = int64(len(rawdb.TrieNodeStoragePrefix) + common.HashLength)
codeKeySize = int64(len(rawdb.CodePrefix) + common.HashLength)
@ -246,6 +229,9 @@ type SizeTracker struct {
abort chan struct{}
aborted chan struct{}
updateCh chan *stateUpdate
mu sync.RWMutex
latestStats *SizeStats
}
// NewSizeTracker creates a new state size tracker and starts it automatically
@ -313,7 +299,12 @@ func (t *SizeTracker) run() {
}
stat := base.add(diff)
stats[u.root] = stat
t.upload(stat)
log.Info("Update state size", "number", stat.BlockNumber, "root", stat.StateRoot, "stat", stat)
// Update latest stats
t.mu.Lock()
t.latestStats = &stat
t.mu.Unlock()
heap.Push(&h, stats[u.root])
for u.blockNumber-h[0].BlockNumber > statEvictThreshold {
@ -415,6 +406,13 @@ wait:
if err := apply(result.root, result.stat); err != nil {
return nil, err
}
// Set initial latest stats
stats[result.root] = result.stat
t.mu.Lock()
t.latestStats = &result.stat
t.mu.Unlock()
log.Info("Measured persistent state size", "root", result.root, "number", result.blockNumber, "stat", result.stat, "elapsed", common.PrettyDuration(result.elapsed))
return stats, nil
@ -600,20 +598,9 @@ func (t *SizeTracker) iterateTableParallel(closed chan struct{}, prefix []byte,
return totalCount, totalBytes, nil
}
func (t *SizeTracker) upload(stats SizeStats) {
log.Debug("Uploading state size", "number", stats.BlockNumber, "root", stats.StateRoot, "stat", stats)
blockInfoGauge.Update(metrics.GaugeInfoValue{
"number": hexutil.Uint64(stats.BlockNumber).String(),
"hash": stats.StateRoot.Hex(),
})
accountsGauge.Update(stats.Accounts)
accountBytesGauge.Update(stats.AccountBytes)
storagesGauge.Update(stats.Storages)
storageBytesGauge.Update(stats.StorageBytes)
accountTrienodesGauge.Update(stats.AccountTrienodes)
accountTrienodeBytesGauge.Update(stats.AccountTrienodeBytes)
storageTrienodesGauge.Update(stats.StorageTrienodes)
storageTrienodeBytesGauge.Update(stats.StorageTrienodeBytes)
contractCodesGauge.Update(stats.ContractCodes)
contractCodeBytesGauge.Update(stats.ContractCodeBytes)
// GetLatestStats returns the latest state size statistics, or nil if not available
func (t *SizeTracker) GetLatestStats() *SizeStats {
t.mu.RLock()
defer t.mu.RUnlock()
return t.latestStats
}

View file

@ -443,3 +443,19 @@ func (api *DebugAPI) GetTrieFlushInterval() (string, error) {
}
return api.eth.blockchain.GetTrieFlushInterval().String(), nil
}
// StateSize returns the current state size statistics from the state size tracker.
// Returns an error if the state size tracker is not initialized or if stats are not ready.
func (api *DebugAPI) StateSize() (*state.SizeStats, error) {
sizer := api.eth.blockchain.StateSizer()
if sizer == nil {
return nil, errors.New("state size tracker is not enabled")
}
stats := sizer.GetLatestStats()
if stats == nil {
return nil, errors.New("state size statistics are not ready yet")
}
return stats, nil
}

View file

@ -468,6 +468,11 @@ web3._extend({
call: 'debug_sync',
params: 1
}),
new web3._extend.Method({
name: 'stateSize',
call: 'debug_stateSize',
params: 0,
}),
],
properties: []
});