diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 1efea541be..71ff821bb9 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -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, diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 54e1b4c4c1..750bf55927 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -200,7 +200,7 @@ var ( utils.MetricsInfluxDBTokenFlag, utils.MetricsInfluxDBBucketFlag, utils.MetricsInfluxDBOrganizationFlag, - utils.MetricsStateSizeFlag, + utils.StateSizeTrackingFlag, } ) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index e8e16f869f..2328d8545c 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -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 diff --git a/core/blockchain.go b/core/blockchain.go index 83c09336d7..cb7fb0c4e3 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -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 +} diff --git a/core/state/state_sizer.go b/core/state/state_sizer.go index 67ae35f379..9e623cd7c4 100644 --- a/core/state/state_sizer.go +++ b/core/state/state_sizer.go @@ -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 } diff --git a/eth/api_debug.go b/eth/api_debug.go index 188dee11aa..6934e20d9c 100644 --- a/eth/api_debug.go +++ b/eth/api_debug.go @@ -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 +} diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index e81e23ef16..81c30a1f97 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -468,6 +468,11 @@ web3._extend({ call: 'debug_sync', params: 1 }), + new web3._extend.Method({ + name: 'stateSize', + call: 'debug_stateSize', + params: 0, + }), ], properties: [] });