mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-24 21:56:43 +00:00
set state root
Signed-off-by: jsvisa <delweng@gmail.com>
This commit is contained in:
parent
325b24125a
commit
b1e0361d32
3 changed files with 256 additions and 152 deletions
|
|
@ -17,7 +17,8 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"context"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -28,6 +29,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/triedb"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
// State size metrics
|
// State size metrics
|
||||||
|
|
@ -62,16 +64,18 @@ type StateSizeGenerator struct {
|
||||||
triedb *triedb.Database
|
triedb *triedb.Database
|
||||||
|
|
||||||
// Generator state
|
// Generator state
|
||||||
running bool
|
abort chan struct{}
|
||||||
abort chan struct{}
|
done chan struct{}
|
||||||
done chan struct{}
|
|
||||||
|
|
||||||
// Async message channel for updates
|
// Async message channel for updates
|
||||||
updateChan chan *stateUpdate
|
updateChan chan *stateUpdate
|
||||||
|
|
||||||
// Metrics state
|
// Metrics state (only modified by generate() goroutine)
|
||||||
metrics *StateSizeMetrics
|
metrics *StateSizeMetrics
|
||||||
buffered *StateSizeMetrics
|
buffered *StateSizeMetrics
|
||||||
|
|
||||||
|
// Initialization state
|
||||||
|
initialized atomic.Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStateSizeGenerator creates a new state size generator and starts it automatically
|
// NewStateSizeGenerator creates a new state size generator and starts it automatically
|
||||||
|
|
@ -87,143 +91,156 @@ func NewStateSizeGenerator(db ethdb.KeyValueStore, triedb *triedb.Database, root
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the generator automatically
|
// Start the generator automatically
|
||||||
g.running = true
|
|
||||||
go g.generate()
|
go g.generate()
|
||||||
|
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop terminates the background generation
|
// Stop terminates the background generation
|
||||||
func (g *StateSizeGenerator) Stop() {
|
func (g *StateSizeGenerator) Stop() {
|
||||||
if !g.running {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Signal the goroutine to stop
|
|
||||||
close(g.abort)
|
close(g.abort)
|
||||||
|
|
||||||
// Wait for the goroutine to actually finish
|
|
||||||
<-g.done
|
<-g.done
|
||||||
|
|
||||||
// Now it's safe to persist metrics since the goroutine has stopped
|
// Persist metrics after all the goroutines were stopped
|
||||||
g.running = false
|
|
||||||
g.persistMetrics()
|
g.persistMetrics()
|
||||||
}
|
}
|
||||||
|
|
||||||
// isRunning returns true if the generator is currently running
|
|
||||||
func (g *StateSizeGenerator) IsRunning() bool {
|
|
||||||
return g.running
|
|
||||||
}
|
|
||||||
|
|
||||||
// waitForCompletion waits for the generator to complete (useful for testing or graceful shutdown)
|
|
||||||
func (g *StateSizeGenerator) WaitForCompletion() {
|
|
||||||
if g.running {
|
|
||||||
<-g.done
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate performs the state size initialization and handles updates
|
// generate performs the state size initialization and handles updates
|
||||||
func (g *StateSizeGenerator) generate() {
|
func (g *StateSizeGenerator) generate() {
|
||||||
defer close(g.done)
|
defer close(g.done)
|
||||||
|
|
||||||
var inited bool
|
initDone := g.initialize()
|
||||||
var initDone chan struct{}
|
|
||||||
|
|
||||||
if g.hasExistingMetrics() {
|
|
||||||
log.Info("State size metrics already initialized")
|
|
||||||
inited = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for snapshot generator to complete
|
|
||||||
snapDone := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
defer close(snapDone)
|
|
||||||
|
|
||||||
for !g.triedb.SnapshotCompleted() {
|
|
||||||
select {
|
|
||||||
case <-g.abort:
|
|
||||||
log.Info("State size generation aborted during snapshot")
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
time.Sleep(10 * time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case update := <-g.updateChan:
|
case update := <-g.updateChan:
|
||||||
g.handleUpdate(update, inited)
|
g.handleUpdate(update, g.initialized.Load())
|
||||||
|
|
||||||
case <-g.abort:
|
case <-g.abort:
|
||||||
log.Info("State size generation aborted")
|
log.Info("State size generation aborted")
|
||||||
// Wait for initialization goroutine to finish if it's running
|
|
||||||
|
// Wait for initialization to complete with timeout
|
||||||
if initDone != nil {
|
if initDone != nil {
|
||||||
select {
|
select {
|
||||||
case <-initDone:
|
case <-initDone:
|
||||||
|
log.Debug("Initialization completed before abort")
|
||||||
case <-time.After(5 * time.Second):
|
case <-time.After(5 * time.Second):
|
||||||
log.Warn("Initialization goroutine did not finish in time")
|
log.Warn("Initialization did not finish in time during abort")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
case <-snapDone:
|
|
||||||
if !inited {
|
|
||||||
initDone = make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
defer close(initDone)
|
|
||||||
start := time.Now()
|
|
||||||
log.Info("Starting state size initialization")
|
|
||||||
g.initializeMetrics()
|
|
||||||
log.Info("Completed state size initialization", "elapsed", time.Since(start))
|
|
||||||
inited = true
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
case <-initDone:
|
case <-initDone:
|
||||||
// Initialization completed, merge buffered metrics if needed
|
// Initialization completed, merge buffered metrics
|
||||||
if g.buffered != nil && g.buffered.Root != (common.Hash{}) {
|
g.mergeBufferedMetrics()
|
||||||
log.Info("Merging buffered metrics into main metrics")
|
initDone = nil // Clear the channel to prevent future selects
|
||||||
g.metrics.AccountCount += g.buffered.AccountCount
|
|
||||||
g.metrics.AccountBytes += g.buffered.AccountBytes
|
|
||||||
g.metrics.StorageCount += g.buffered.StorageCount
|
|
||||||
g.metrics.StorageBytes += g.buffered.StorageBytes
|
|
||||||
g.metrics.TrieNodeCount += g.buffered.TrieNodeCount
|
|
||||||
g.metrics.TrieNodeBytes += g.buffered.TrieNodeBytes
|
|
||||||
g.metrics.ContractCount += g.buffered.ContractCount
|
|
||||||
g.metrics.ContractBytes += g.buffered.ContractBytes
|
|
||||||
// Reset buffered metrics
|
|
||||||
g.buffered = &StateSizeMetrics{Root: g.metrics.Root}
|
|
||||||
}
|
|
||||||
initDone = nil // Clear the channel
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleUpdate processes a single update with proper root continuity checking
|
// initialize starts the initialization process if not already initialized
|
||||||
func (g *StateSizeGenerator) handleUpdate(update *stateUpdate, inited bool) {
|
func (g *StateSizeGenerator) initialize() chan struct{} {
|
||||||
// TODO: Check if the update root matches the current metrics root
|
initDone := make(chan struct{})
|
||||||
|
|
||||||
|
// Check if we already have existing metrics
|
||||||
|
if g.hasExistingMetrics() {
|
||||||
|
log.Info("State size metrics already initialized")
|
||||||
|
g.initialized.Store(true)
|
||||||
|
close(initDone)
|
||||||
|
return initDone
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for snapshot completion and then initialize
|
||||||
|
go func() {
|
||||||
|
defer close(initDone)
|
||||||
|
|
||||||
|
LOOP:
|
||||||
|
// Wait for snapshot generator to complete first
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-g.abort:
|
||||||
|
log.Info("State size initialization aborted during snapshot wait")
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
root, done := g.triedb.SnapshotCompleted()
|
||||||
|
if done {
|
||||||
|
g.metrics.Root = root
|
||||||
|
break LOOP
|
||||||
|
}
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start actual initialization
|
||||||
|
start := time.Now()
|
||||||
|
log.Info("Starting state size initialization")
|
||||||
|
if err := g.initializeMetrics(); err != nil {
|
||||||
|
log.Error("Failed to initialize state size metrics", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
g.initialized.Store(true)
|
||||||
|
|
||||||
|
log.Info("Completed state size initialization", "elapsed", time.Since(start))
|
||||||
|
}()
|
||||||
|
|
||||||
|
return initDone
|
||||||
|
}
|
||||||
|
|
||||||
|
// mergeBufferedMetrics merges buffered metrics into main metrics
|
||||||
|
func (g *StateSizeGenerator) mergeBufferedMetrics() {
|
||||||
|
if g.buffered != nil && g.buffered.Root != (common.Hash{}) {
|
||||||
|
log.Info("Merging buffered metrics into main metrics")
|
||||||
|
g.metrics.AccountCount += g.buffered.AccountCount
|
||||||
|
g.metrics.AccountBytes += g.buffered.AccountBytes
|
||||||
|
g.metrics.StorageCount += g.buffered.StorageCount
|
||||||
|
g.metrics.StorageBytes += g.buffered.StorageBytes
|
||||||
|
g.metrics.TrieNodeCount += g.buffered.TrieNodeCount
|
||||||
|
g.metrics.TrieNodeBytes += g.buffered.TrieNodeBytes
|
||||||
|
g.metrics.ContractCount += g.buffered.ContractCount
|
||||||
|
g.metrics.ContractBytes += g.buffered.ContractBytes
|
||||||
|
|
||||||
|
// Reset buffered metrics
|
||||||
|
g.buffered = &StateSizeMetrics{Root: g.metrics.Root}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleUpdate processes a single update with proper root continuity checking
|
||||||
|
func (g *StateSizeGenerator) handleUpdate(update *stateUpdate, initialized bool) {
|
||||||
// Calculate the diff
|
// Calculate the diff
|
||||||
diff := g.calculateUpdateDiff(update)
|
diff := g.calculateUpdateDiff(update)
|
||||||
|
|
||||||
var m *StateSizeMetrics
|
var targetMetrics *StateSizeMetrics
|
||||||
if inited {
|
if initialized {
|
||||||
m = g.metrics
|
targetMetrics = g.metrics
|
||||||
} else {
|
} else {
|
||||||
m = g.buffered
|
targetMetrics = g.buffered
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: When to merge the buffered metrics into the main metrics
|
// Check root continuity - the update should build on our current state
|
||||||
m.Root = update.root
|
if targetMetrics.Root != (common.Hash{}) && targetMetrics.Root != update.originRoot {
|
||||||
m.AccountCount += diff.AccountCount
|
log.Warn("State update root discontinuity detected",
|
||||||
m.AccountBytes += diff.AccountBytes
|
"current", targetMetrics.Root,
|
||||||
m.StorageCount += diff.StorageCount
|
"updateOrigin", update.originRoot,
|
||||||
m.StorageBytes += diff.StorageBytes
|
"updateNew", update.root)
|
||||||
m.TrieNodeCount += diff.TrieNodeCount
|
// For now, we accept the discontinuity but log it
|
||||||
m.TrieNodeBytes += diff.TrieNodeBytes
|
// In production, you might want to reset metrics or handle differently
|
||||||
m.ContractCount += diff.ContractCount
|
}
|
||||||
m.ContractBytes += diff.ContractBytes
|
|
||||||
|
|
||||||
// Fire the metrics only if the initialization is done
|
// Update to the new state root
|
||||||
if inited {
|
targetMetrics.Root = update.root
|
||||||
|
targetMetrics.AccountCount += diff.AccountCount
|
||||||
|
targetMetrics.AccountBytes += diff.AccountBytes
|
||||||
|
targetMetrics.StorageCount += diff.StorageCount
|
||||||
|
targetMetrics.StorageBytes += diff.StorageBytes
|
||||||
|
targetMetrics.TrieNodeCount += diff.TrieNodeCount
|
||||||
|
targetMetrics.TrieNodeBytes += diff.TrieNodeBytes
|
||||||
|
targetMetrics.ContractCount += diff.ContractCount
|
||||||
|
targetMetrics.ContractBytes += diff.ContractBytes
|
||||||
|
|
||||||
|
// Fire the metrics and persist only if initialization is done
|
||||||
|
if initialized {
|
||||||
g.updateMetrics()
|
g.updateMetrics()
|
||||||
g.persistMetrics()
|
g.persistMetrics()
|
||||||
}
|
}
|
||||||
|
|
@ -313,25 +330,60 @@ func (g *StateSizeGenerator) calculateUpdateDiff(update *stateUpdate) StateSizeM
|
||||||
|
|
||||||
// Track is an async method used to send the state update to the generator.
|
// Track is an async method used to send the state update to the generator.
|
||||||
// It ignores empty updates (where no state changes occurred).
|
// It ignores empty updates (where no state changes occurred).
|
||||||
|
// If the channel is full, it drops the update to avoid blocking.
|
||||||
func (g *StateSizeGenerator) Track(update *stateUpdate) {
|
func (g *StateSizeGenerator) Track(update *stateUpdate) {
|
||||||
if update == nil || update.empty() {
|
if update == nil || update.empty() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
g.updateChan <- update
|
g.updateChan <- update
|
||||||
}
|
}
|
||||||
|
|
||||||
// hasExistingMetrics checks if state size metrics already exist in the database
|
// hasExistingMetrics checks if state size metrics already exist in the database
|
||||||
|
// and if they are continuous with the current root
|
||||||
func (g *StateSizeGenerator) hasExistingMetrics() bool {
|
func (g *StateSizeGenerator) hasExistingMetrics() bool {
|
||||||
// Check for existing metrics by looking for a marker key
|
data := rawdb.ReadStateSizeMetrics(g.db)
|
||||||
marker := rawdb.ReadStateSizeMetrics(g.db)
|
if data == nil {
|
||||||
// TODO: check if the marker's root is the same as the current root
|
return false
|
||||||
return marker != nil
|
}
|
||||||
|
|
||||||
|
var existed StateSizeMetrics
|
||||||
|
if err := rlp.DecodeBytes(data, &existed); err != nil {
|
||||||
|
log.Warn("Failed to decode existing state size metrics", "err", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the existing metrics root matches our current root
|
||||||
|
if (g.metrics.Root != common.Hash{}) && existed.Root != g.metrics.Root {
|
||||||
|
log.Info("Existing state size metrics found but root mismatch", "existing", existed.Root, "current", g.metrics.Root)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Root matches - load the existing metrics
|
||||||
|
log.Info("Loading existing state size metrics", "root", existed.Root)
|
||||||
|
g.metrics = &existed
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// initializeMetrics performs the actual metrics initialization
|
// initializeMetrics performs the actual metrics initialization using errgroup
|
||||||
func (g *StateSizeGenerator) initializeMetrics() {
|
func (g *StateSizeGenerator) initializeMetrics() error {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-g.abort:
|
||||||
|
cancel() // Cancel context when abort is signaled
|
||||||
|
case <-ctx.Done():
|
||||||
|
// Context already cancelled
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Create errgroup with context
|
||||||
|
group, ctx := errgroup.WithContext(ctx)
|
||||||
|
|
||||||
|
// Metrics will be directly updated by each goroutine
|
||||||
var (
|
var (
|
||||||
wg sync.WaitGroup
|
|
||||||
accountCount, accountBytes uint64
|
accountCount, accountBytes uint64
|
||||||
storageCount, storageBytes uint64
|
storageCount, storageBytes uint64
|
||||||
trieAccountNodeCount, trieAccountNodeBytes uint64
|
trieAccountNodeCount, trieAccountNodeBytes uint64
|
||||||
|
|
@ -339,49 +391,58 @@ func (g *StateSizeGenerator) initializeMetrics() {
|
||||||
contractCount, contractBytes uint64
|
contractCount, contractBytes uint64
|
||||||
)
|
)
|
||||||
|
|
||||||
iterate := func(prefix []byte, name string, count, bytes uint64) {
|
// Start all table iterations concurrently with direct metric updates
|
||||||
defer wg.Done()
|
group.Go(func() error {
|
||||||
|
count, bytes, err := g.iterateTable(ctx, rawdb.SnapshotAccountPrefix, "account")
|
||||||
log.Info("Iterating over state size", "table", name)
|
if err != nil {
|
||||||
defer func(st time.Time) {
|
return err
|
||||||
log.Info("Finished iterating over state size", "table", name, "count", count, "bytes", bytes, "elapsed", common.PrettyDuration(time.Since(st)))
|
|
||||||
}(time.Now())
|
|
||||||
|
|
||||||
iter := g.db.NewIterator(prefix, nil)
|
|
||||||
defer iter.Release()
|
|
||||||
for iter.Next() {
|
|
||||||
count++
|
|
||||||
bytes += uint64(len(iter.Key()) + len(iter.Value()))
|
|
||||||
|
|
||||||
// Check for abort
|
|
||||||
select {
|
|
||||||
case <-g.abort:
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
accountCount, accountBytes = count, bytes
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
group.Go(func() error {
|
||||||
|
count, bytes, err := g.iterateTable(ctx, rawdb.SnapshotStoragePrefix, "storage")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
storageCount, storageBytes = count, bytes
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
group.Go(func() error {
|
||||||
|
count, bytes, err := g.iterateTable(ctx, rawdb.TrieNodeAccountPrefix, "trie account node")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
trieAccountNodeCount, trieAccountNodeBytes = count, bytes
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
group.Go(func() error {
|
||||||
|
count, bytes, err := g.iterateTable(ctx, rawdb.TrieNodeStoragePrefix, "trie storage node")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
trieStorageNodeCount, trieStorageNodeBytes = count, bytes
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
group.Go(func() error {
|
||||||
|
count, bytes, err := g.iterateTable(ctx, rawdb.CodePrefix, "contract code")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
contractCount, contractBytes = count, bytes
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// Wait for all goroutines to complete
|
||||||
|
if err := group.Wait(); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tables := []struct {
|
// Update metrics (safe since we're in the single writer goroutine)
|
||||||
prefix []byte
|
|
||||||
name string
|
|
||||||
count *uint64
|
|
||||||
bytes *uint64
|
|
||||||
}{
|
|
||||||
{rawdb.SnapshotAccountPrefix, "account", &accountCount, &accountBytes},
|
|
||||||
{rawdb.SnapshotStoragePrefix, "storage", &storageCount, &storageBytes},
|
|
||||||
{rawdb.TrieNodeAccountPrefix, "trie account node", &trieAccountNodeCount, &trieAccountNodeBytes},
|
|
||||||
{rawdb.TrieNodeStoragePrefix, "trie storage node", &trieStorageNodeCount, &trieStorageNodeBytes},
|
|
||||||
{rawdb.CodePrefix, "contract code", &contractCount, &contractBytes},
|
|
||||||
}
|
|
||||||
wg.Add(len(tables))
|
|
||||||
for _, table := range tables {
|
|
||||||
go iterate(table.prefix, table.name, *table.count, *table.bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
// Update metrics
|
|
||||||
g.metrics.AccountCount = accountCount
|
g.metrics.AccountCount = accountCount
|
||||||
g.metrics.AccountBytes = accountBytes
|
g.metrics.AccountBytes = accountBytes
|
||||||
g.metrics.StorageCount = storageCount
|
g.metrics.StorageCount = storageCount
|
||||||
|
|
@ -393,10 +454,46 @@ func (g *StateSizeGenerator) initializeMetrics() {
|
||||||
|
|
||||||
g.updateMetrics()
|
g.updateMetrics()
|
||||||
g.persistMetrics()
|
g.persistMetrics()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterateTable performs iteration over a specific table and returns the results
|
||||||
|
func (g *StateSizeGenerator) iterateTable(ctx context.Context, prefix []byte, name string) (uint64, uint64, error) {
|
||||||
|
log.Info("Iterating over state size", "table", name)
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
var count, bytes uint64
|
||||||
|
iter := g.db.NewIterator(prefix, nil)
|
||||||
|
defer iter.Release()
|
||||||
|
|
||||||
|
for iter.Next() {
|
||||||
|
count++
|
||||||
|
bytes += uint64(len(iter.Key()) + len(iter.Value()))
|
||||||
|
|
||||||
|
// Check for cancellation periodically for performance
|
||||||
|
if count%10000 == 0 {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
log.Info("State size iteration cancelled", "table", name, "count", count)
|
||||||
|
return 0, 0, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for iterator errors
|
||||||
|
if err := iter.Error(); err != nil {
|
||||||
|
log.Error("Iterator error during state size calculation", "table", name, "err", err)
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Finished iterating over state size", "table", name, "count", count, "bytes", bytes, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||||
|
|
||||||
|
return count, bytes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *StateSizeGenerator) updateMetrics() {
|
func (g *StateSizeGenerator) updateMetrics() {
|
||||||
// Update global metrics
|
|
||||||
stateSizeAccountsCountMeter.Mark(int64(g.metrics.AccountCount))
|
stateSizeAccountsCountMeter.Mark(int64(g.metrics.AccountCount))
|
||||||
stateSizeAccountsBytesMeter.Mark(int64(g.metrics.AccountBytes))
|
stateSizeAccountsBytesMeter.Mark(int64(g.metrics.AccountBytes))
|
||||||
stateSizeStorageCountMeter.Mark(int64(g.metrics.StorageCount))
|
stateSizeStorageCountMeter.Mark(int64(g.metrics.StorageCount))
|
||||||
|
|
|
||||||
|
|
@ -377,10 +377,10 @@ func (db *Database) Disk() ethdb.Database {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SnapshotCompleted returns the indicator if the snapshot is completed.
|
// SnapshotCompleted returns the indicator if the snapshot is completed.
|
||||||
func (db *Database) SnapshotCompleted() bool {
|
func (db *Database) SnapshotCompleted() (common.Hash, bool) {
|
||||||
pdb, ok := db.backend.(*pathdb.Database)
|
pdb, ok := db.backend.(*pathdb.Database)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return common.Hash{}, false
|
||||||
}
|
}
|
||||||
return pdb.SnapshotCompleted()
|
return pdb.SnapshotCompleted()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -682,7 +682,14 @@ func (db *Database) StorageIterator(root common.Hash, account common.Hash, seek
|
||||||
return newFastStorageIterator(db, root, account, seek)
|
return newFastStorageIterator(db, root, account, seek)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SnapshotCompleted returns the indicator if the snapshot generation is completed.
|
// SnapshotCompleted returns the snapshot root if the snapshot generation is completed.
|
||||||
func (db *Database) SnapshotCompleted() bool {
|
func (db *Database) SnapshotCompleted() (common.Hash, bool) {
|
||||||
return !db.waitSync && db.tree.bottom().genComplete()
|
if db.waitSync {
|
||||||
|
return common.Hash{}, false
|
||||||
|
}
|
||||||
|
dl := db.tree.bottom()
|
||||||
|
if dl.genComplete() {
|
||||||
|
return dl.rootHash(), true
|
||||||
|
}
|
||||||
|
return common.Hash{}, false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue