@@ -18,6 +18,7 @@ package state
18
18
19
19
import (
20
20
"context"
21
+ "encoding/json"
21
22
"sync/atomic"
22
23
"time"
23
24
@@ -26,42 +27,33 @@ import (
26
27
"github.com/ethereum/go-ethereum/crypto"
27
28
"github.com/ethereum/go-ethereum/ethdb"
28
29
"github.com/ethereum/go-ethereum/log"
29
- "github.com/ethereum/go-ethereum/rlp"
30
30
"github.com/ethereum/go-ethereum/triedb"
31
31
"golang.org/x/sync/errgroup"
32
32
)
33
33
34
- // StateSizeMetrics represents the current state size statistics
35
- type StateSizeMetrics struct {
34
+ // stateSizeMetrics represents the current state size statistics
35
+ type stateSizeMetrics struct {
36
36
Root common.Hash // Root hash of the state trie
37
- AccountCount uint64
38
- AccountBytes uint64
39
- StorageCount uint64
40
- StorageBytes uint64
41
- TrieNodeCount uint64
42
- TrieNodeBytes uint64
43
- ContractCount uint64
44
- ContractBytes uint64
37
+ AccountCount int64
38
+ AccountBytes int64
39
+ StorageCount int64
40
+ StorageBytes int64
41
+ TrieNodeCount int64
42
+ TrieNodeBytes int64
43
+ ContractCount int64
44
+ ContractBytes int64
45
45
}
46
46
47
47
// StateSizeGenerator handles the initialization and tracking of state size metrics
48
48
type StateSizeGenerator struct {
49
- db ethdb.KeyValueStore
50
- triedb * triedb.Database
51
-
52
- // Generator state
53
- abort chan struct {}
54
- done chan struct {}
55
-
56
- // Async message channel for updates
57
- updateChan chan * stateUpdate
58
-
59
- // Metrics state (only modified by generate() goroutine)
60
- metrics * StateSizeMetrics
61
- buffered * StateSizeMetrics
62
-
63
- // Initialization state
64
- initialized atomic.Bool
49
+ db ethdb.KeyValueStore
50
+ triedb * triedb.Database
51
+ abort chan struct {}
52
+ done chan struct {}
53
+ updateChan chan * stateUpdate // Async message channel for updates
54
+ metrics * stateSizeMetrics
55
+ buffered * stateSizeMetrics
56
+ initialized atomic.Bool // Initialization state
65
57
}
66
58
67
59
// NewStateSizeGenerator creates a new state size generator and starts it automatically
@@ -72,8 +64,8 @@ func NewStateSizeGenerator(db ethdb.KeyValueStore, triedb *triedb.Database, root
72
64
abort : make (chan struct {}),
73
65
done : make (chan struct {}),
74
66
updateChan : make (chan * stateUpdate , 1000 ), // Buffered channel for updates
75
- metrics : & StateSizeMetrics {Root : root },
76
- buffered : & StateSizeMetrics {Root : root },
67
+ metrics : & stateSizeMetrics {Root : root },
68
+ buffered : & stateSizeMetrics {Root : root },
77
69
}
78
70
79
71
// Start the generator automatically
@@ -82,13 +74,12 @@ func NewStateSizeGenerator(db ethdb.KeyValueStore, triedb *triedb.Database, root
82
74
return g
83
75
}
84
76
85
- // Stop terminates the background generation
77
+ // Stop terminates the background generation and persists the metrics.
86
78
func (g * StateSizeGenerator ) Stop () {
87
79
close (g .abort )
88
80
89
81
<- g .done
90
82
91
- // Persist metrics after all the goroutines were stopped
92
83
g .persistMetrics ()
93
84
}
94
85
@@ -119,7 +110,20 @@ func (g *StateSizeGenerator) generate() {
119
110
120
111
case <- initDone :
121
112
// Initialization completed, merge buffered metrics
122
- g .mergeBufferedMetrics ()
113
+ if g .buffered != nil {
114
+ log .Info ("Merging buffered metrics into main metrics" )
115
+ g .metrics .Root = g .buffered .Root
116
+ g .metrics .AccountCount += g .buffered .AccountCount
117
+ g .metrics .AccountBytes += g .buffered .AccountBytes
118
+ g .metrics .StorageCount += g .buffered .StorageCount
119
+ g .metrics .StorageBytes += g .buffered .StorageBytes
120
+ g .metrics .TrieNodeCount += g .buffered .TrieNodeCount
121
+ g .metrics .TrieNodeBytes += g .buffered .TrieNodeBytes
122
+ g .metrics .ContractCount += g .buffered .ContractCount
123
+ g .metrics .ContractBytes += g .buffered .ContractBytes
124
+
125
+ g .buffered = nil
126
+ }
123
127
initDone = nil // Clear the channel to prevent future selects
124
128
}
125
129
}
@@ -174,30 +178,12 @@ func (g *StateSizeGenerator) initialize() chan struct{} {
174
178
return initDone
175
179
}
176
180
177
- // mergeBufferedMetrics merges buffered metrics into main metrics
178
- func (g * StateSizeGenerator ) mergeBufferedMetrics () {
179
- if g .buffered != nil {
180
- log .Info ("Merging buffered metrics into main metrics" )
181
- g .metrics .Root = g .buffered .Root
182
- g .metrics .AccountCount += g .buffered .AccountCount
183
- g .metrics .AccountBytes += g .buffered .AccountBytes
184
- g .metrics .StorageCount += g .buffered .StorageCount
185
- g .metrics .StorageBytes += g .buffered .StorageBytes
186
- g .metrics .TrieNodeCount += g .buffered .TrieNodeCount
187
- g .metrics .TrieNodeBytes += g .buffered .TrieNodeBytes
188
- g .metrics .ContractCount += g .buffered .ContractCount
189
- g .metrics .ContractBytes += g .buffered .ContractBytes
190
-
191
- g .buffered = nil
192
- }
193
- }
194
-
195
181
// handleUpdate processes a single update with proper root continuity checking
196
182
func (g * StateSizeGenerator ) handleUpdate (update * stateUpdate , initialized bool ) {
197
183
// Calculate the diff
198
184
diff := g .calculateUpdateDiff (update )
199
185
200
- var targetMetrics * StateSizeMetrics
186
+ var targetMetrics * stateSizeMetrics
201
187
if initialized {
202
188
targetMetrics = g .metrics
203
189
} else {
@@ -233,8 +219,8 @@ func (g *StateSizeGenerator) handleUpdate(update *stateUpdate, initialized bool)
233
219
}
234
220
235
221
// calculateUpdateDiff calculates the diff for a state update
236
- func (g * StateSizeGenerator ) calculateUpdateDiff (update * stateUpdate ) StateSizeMetrics {
237
- var diff StateSizeMetrics
222
+ func (g * StateSizeGenerator ) calculateUpdateDiff (update * stateUpdate ) stateSizeMetrics {
223
+ var diff stateSizeMetrics
238
224
239
225
// Calculate account changes
240
226
for addr , oldValue := range update .accountsOrigin {
@@ -252,7 +238,7 @@ func (g *StateSizeGenerator) calculateUpdateDiff(update *stateUpdate) StateSizeM
252
238
diff .AccountCount += 1
253
239
diff .AccountBytes += common .HashLength
254
240
}
255
- diff .AccountBytes += uint64 (len (newValue ) - len (oldValue ))
241
+ diff .AccountBytes += int64 (len (newValue ) - len (oldValue ))
256
242
}
257
243
258
244
// Calculate storage changes
@@ -285,7 +271,7 @@ func (g *StateSizeGenerator) calculateUpdateDiff(update *stateUpdate) StateSizeM
285
271
diff .StorageCount += 1
286
272
diff .StorageBytes += common .HashLength
287
273
}
288
- diff .StorageBytes += uint64 (len (newValue ) - len (oldValue ))
274
+ diff .StorageBytes += int64 (len (newValue ) - len (oldValue ))
289
275
}
290
276
}
291
277
@@ -294,21 +280,21 @@ func (g *StateSizeGenerator) calculateUpdateDiff(update *stateUpdate) StateSizeM
294
280
for path , n := range subset .Nodes {
295
281
if len (n .Blob ) == 0 {
296
282
diff .TrieNodeCount -= 1
297
- diff .TrieNodeBytes -= uint64 (len (path ) + common .HashLength )
283
+ diff .TrieNodeBytes -= int64 (len (path ) + common .HashLength )
298
284
}
299
285
prev , ok := subset .Origins [path ]
300
286
if ok {
301
287
diff .TrieNodeCount += 1
302
- diff .TrieNodeBytes += uint64 (len (path ) + common .HashLength )
288
+ diff .TrieNodeBytes += int64 (len (path ) + common .HashLength )
303
289
}
304
- diff .TrieNodeBytes += uint64 (len (n .Blob ) - len (prev ))
290
+ diff .TrieNodeBytes += int64 (len (n .Blob ) - len (prev ))
305
291
}
306
292
}
307
293
308
294
// Calculate code changes
309
295
for _ , code := range update .codes {
310
296
diff .ContractCount += 1
311
- diff .ContractBytes += uint64 (len (code .blob ) + common .HashLength )
297
+ diff .ContractBytes += int64 (len (code .blob ) + common .HashLength )
312
298
}
313
299
314
300
return diff
@@ -333,15 +319,15 @@ func (g *StateSizeGenerator) hasExistingMetrics() bool {
333
319
return false
334
320
}
335
321
336
- var existed StateSizeMetrics
337
- if err := rlp . DecodeBytes (data , & existed ); err != nil {
338
- log .Warn ("Failed to decode existing state size metrics" , "err" , err )
322
+ var existed stateSizeMetrics
323
+ if err := json . Unmarshal (data , & existed ); err != nil {
324
+ log .Warn ("Failed to decode existed state size metrics" , "err" , err )
339
325
return false
340
326
}
341
327
342
328
// Check if the existing metrics root matches our current root
343
329
if (g .metrics .Root != common.Hash {}) && existed .Root != g .metrics .Root {
344
- log .Info ("Existing state size metrics found but root mismatch" , "existing " , existed .Root , "current" , g .metrics .Root )
330
+ log .Info ("Existing state size metrics found but root mismatch" , "existed " , existed .Root , "current" , g .metrics .Root )
345
331
return false
346
332
}
347
333
@@ -370,11 +356,11 @@ func (g *StateSizeGenerator) initializeMetrics() error {
370
356
371
357
// Metrics will be directly updated by each goroutine
372
358
var (
373
- accountCount , accountBytes uint64
374
- storageCount , storageBytes uint64
375
- trieAccountNodeCount , trieAccountNodeBytes uint64
376
- trieStorageNodeCount , trieStorageNodeBytes uint64
377
- contractCount , contractBytes uint64
359
+ accountCount , accountBytes int64
360
+ storageCount , storageBytes int64
361
+ trieAccountNodeCount , trieAccountNodeBytes int64
362
+ trieStorageNodeCount , trieStorageNodeBytes int64
363
+ contractCount , contractBytes int64
378
364
)
379
365
380
366
// Start all table iterations concurrently with direct metric updates
@@ -428,7 +414,6 @@ func (g *StateSizeGenerator) initializeMetrics() error {
428
414
return err
429
415
}
430
416
431
- // Update metrics (safe since we're in the single writer goroutine)
432
417
g .metrics .AccountCount = accountCount
433
418
g .metrics .AccountBytes = accountBytes
434
419
g .metrics .StorageCount = storageCount
@@ -445,17 +430,17 @@ func (g *StateSizeGenerator) initializeMetrics() error {
445
430
}
446
431
447
432
// iterateTable performs iteration over a specific table and returns the results
448
- func (g * StateSizeGenerator ) iterateTable (ctx context.Context , prefix []byte , name string ) (uint64 , uint64 , error ) {
433
+ func (g * StateSizeGenerator ) iterateTable (ctx context.Context , prefix []byte , name string ) (int64 , int64 , error ) {
449
434
log .Info ("Iterating over state size" , "table" , name )
450
435
start := time .Now ()
451
436
452
- var count , bytes uint64
437
+ var count , bytes int64
453
438
iter := g .db .NewIterator (prefix , nil )
454
439
defer iter .Release ()
455
440
456
441
for iter .Next () {
457
442
count ++
458
- bytes += uint64 (len (iter .Key ()) + len (iter .Value ()))
443
+ bytes += int64 (len (iter .Key ()) + len (iter .Value ()))
459
444
460
445
// Check for cancellation periodically for performance
461
446
if count % 10000 == 0 {
@@ -480,19 +465,20 @@ func (g *StateSizeGenerator) iterateTable(ctx context.Context, prefix []byte, na
480
465
}
481
466
482
467
func (g * StateSizeGenerator ) updateMetrics () {
483
- accountCountGauge .Update (int64 ( g .metrics .AccountCount ) )
484
- accountBytesGauge .Update (int64 ( g .metrics .AccountBytes ) )
485
- storageCountGauge .Update (int64 ( g .metrics .StorageCount ) )
486
- storageBytesGauge .Update (int64 ( g .metrics .StorageBytes ) )
487
- trienodeCountGauge .Update (int64 ( g .metrics .TrieNodeCount ) )
488
- trienodeBytesGauge .Update (int64 ( g .metrics .TrieNodeBytes ) )
489
- contractCountGauge .Update (int64 ( g .metrics .ContractCount ) )
490
- contractBytesGauge .Update (int64 ( g .metrics .ContractBytes ) )
468
+ accountCountGauge .Update (g .metrics .AccountCount )
469
+ accountBytesGauge .Update (g .metrics .AccountBytes )
470
+ storageCountGauge .Update (g .metrics .StorageCount )
471
+ storageBytesGauge .Update (g .metrics .StorageBytes )
472
+ trienodeCountGauge .Update (g .metrics .TrieNodeCount )
473
+ trienodeBytesGauge .Update (g .metrics .TrieNodeBytes )
474
+ contractCountGauge .Update (g .metrics .ContractCount )
475
+ contractBytesGauge .Update (g .metrics .ContractBytes )
491
476
}
492
477
493
478
// persistMetrics saves the current metrics to the database
494
479
func (g * StateSizeGenerator ) persistMetrics () {
495
- data , err := rlp .EncodeToBytes (* g .metrics )
480
+ // RLP doesn't support int64, so we use JSON for simplicity
481
+ data , err := json .Marshal (* g .metrics )
496
482
if err != nil {
497
483
log .Error ("Failed to encode state size metrics" , "err" , err )
498
484
return
0 commit comments