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.MetricsInfluxDBTokenFlag,
utils.MetricsInfluxDBBucketFlag, utils.MetricsInfluxDBBucketFlag,
utils.MetricsInfluxDBOrganizationFlag, utils.MetricsInfluxDBOrganizationFlag,
utils.MetricsStateSizeFlag, utils.StateSizeTrackingFlag,
utils.TxLookupLimitFlag, utils.TxLookupLimitFlag,
utils.VMTraceFlag, utils.VMTraceFlag,
utils.VMTraceJsonConfigFlag, utils.VMTraceJsonConfigFlag,

View file

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

View file

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

View file

@ -2814,3 +2814,8 @@ func (bc *BlockChain) SetTrieFlushInterval(interval time.Duration) {
func (bc *BlockChain) GetTrieFlushInterval() time.Duration { func (bc *BlockChain) GetTrieFlushInterval() time.Duration {
return time.Duration(bc.flushInterval.Load()) 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" "time"
"github.com/ethereum/go-ethereum/common" "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/core/rawdb"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
) )
@ -40,25 +38,10 @@ const (
statEvictThreshold = 128 // the depth of statistic to be preserved 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. // Database key scheme for states.
var ( var (
accountKeySize = int64(len(rawdb.SnapshotAccountPrefix) + common.HashLength) 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)) accountTrienodePrefixSize = int64(len(rawdb.TrieNodeAccountPrefix))
storageTrienodePrefixSize = int64(len(rawdb.TrieNodeStoragePrefix) + common.HashLength) storageTrienodePrefixSize = int64(len(rawdb.TrieNodeStoragePrefix) + common.HashLength)
codeKeySize = int64(len(rawdb.CodePrefix) + common.HashLength) codeKeySize = int64(len(rawdb.CodePrefix) + common.HashLength)
@ -246,6 +229,9 @@ type SizeTracker struct {
abort chan struct{} abort chan struct{}
aborted chan struct{} aborted chan struct{}
updateCh chan *stateUpdate updateCh chan *stateUpdate
mu sync.RWMutex
latestStats *SizeStats
} }
// NewSizeTracker creates a new state size tracker and starts it automatically // NewSizeTracker creates a new state size tracker and starts it automatically
@ -313,7 +299,12 @@ func (t *SizeTracker) run() {
} }
stat := base.add(diff) stat := base.add(diff)
stats[u.root] = stat 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]) heap.Push(&h, stats[u.root])
for u.blockNumber-h[0].BlockNumber > statEvictThreshold { for u.blockNumber-h[0].BlockNumber > statEvictThreshold {
@ -415,6 +406,13 @@ wait:
if err := apply(result.root, result.stat); err != nil { if err := apply(result.root, result.stat); err != nil {
return nil, err 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)) log.Info("Measured persistent state size", "root", result.root, "number", result.blockNumber, "stat", result.stat, "elapsed", common.PrettyDuration(result.elapsed))
return stats, nil return stats, nil
@ -600,20 +598,9 @@ func (t *SizeTracker) iterateTableParallel(closed chan struct{}, prefix []byte,
return totalCount, totalBytes, nil return totalCount, totalBytes, nil
} }
func (t *SizeTracker) upload(stats SizeStats) { // GetLatestStats returns the latest state size statistics, or nil if not available
log.Debug("Uploading state size", "number", stats.BlockNumber, "root", stats.StateRoot, "stat", stats) func (t *SizeTracker) GetLatestStats() *SizeStats {
blockInfoGauge.Update(metrics.GaugeInfoValue{ t.mu.RLock()
"number": hexutil.Uint64(stats.BlockNumber).String(), defer t.mu.RUnlock()
"hash": stats.StateRoot.Hex(), return t.latestStats
})
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)
} }

View file

@ -443,3 +443,19 @@ func (api *DebugAPI) GetTrieFlushInterval() (string, error) {
} }
return api.eth.blockchain.GetTrieFlushInterval().String(), nil 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', call: 'debug_sync',
params: 1 params: 1
}), }),
new web3._extend.Method({
name: 'stateSize',
call: 'debug_stateSize',
params: 0,
}),
], ],
properties: [] properties: []
}); });