@@ -502,8 +502,10 @@ type Syncer struct {
502
502
storageHealed uint64 // Number of storage slots downloaded during the healing stage
503
503
storageHealedBytes common.StorageSize // Number of raw storage bytes persisted to disk during the healing stage
504
504
505
- startTime time.Time // Time instance when snapshot sync started
506
- logTime time.Time // Time instance when status was last reported
505
+ startTime time.Time // Time instance when snapshot sync started
506
+ healStartTime time.Time // Time instance when the state healing started
507
+ syncTimeOnce sync.Once // Ensure that the state sync time is uploaded only once
508
+ logTime time.Time // Time instance when status was last reported
507
509
508
510
pend sync.WaitGroup // Tracks network request goroutines for graceful shutdown
509
511
lock sync.RWMutex // Protects fields that can change outside of sync (peers, reqs, root)
@@ -685,6 +687,14 @@ func (s *Syncer) Sync(root common.Hash, cancel chan struct{}) error {
685
687
s .cleanStorageTasks ()
686
688
s .cleanAccountTasks ()
687
689
if len (s .tasks ) == 0 && s .healer .scheduler .Pending () == 0 {
690
+ // State healing phase completed, record the elapsed time in metrics.
691
+ // Note: healing may be rerun in subsequent cycles to fill gaps between
692
+ // pivot states (e.g., if chain sync takes longer).
693
+ if ! s .healStartTime .IsZero () {
694
+ stateHealTimeGauge .Inc (int64 (time .Since (s .healStartTime )))
695
+ log .Info ("State healing phase is completed" , "elapsed" , common .PrettyDuration (time .Since (s .healStartTime )))
696
+ s .healStartTime = time.Time {}
697
+ }
688
698
return nil
689
699
}
690
700
// Assign all the data retrieval tasks to any free peers
@@ -693,7 +703,17 @@ func (s *Syncer) Sync(root common.Hash, cancel chan struct{}) error {
693
703
s .assignStorageTasks (storageResps , storageReqFails , cancel )
694
704
695
705
if len (s .tasks ) == 0 {
696
- // Sync phase done, run heal phase
706
+ // State sync phase completed, record the elapsed time in metrics.
707
+ // Note: the initial state sync runs only once, regardless of whether
708
+ // a new cycle is started later. Any state differences in subsequent
709
+ // cycles will be handled by the state healer.
710
+ s .syncTimeOnce .Do (func () {
711
+ stateSyncTimeGauge .Update (int64 (time .Since (s .startTime )))
712
+ log .Info ("State sync phase is completed" , "elapsed" , common .PrettyDuration (time .Since (s .startTime )))
713
+ })
714
+ if s .healStartTime .IsZero () {
715
+ s .healStartTime = time .Now ()
716
+ }
697
717
s .assignTrienodeHealTasks (trienodeHealResps , trienodeHealReqFails , cancel )
698
718
s .assignBytecodeHealTasks (bytecodeHealResps , bytecodeHealReqFails , cancel )
699
719
}
0 commit comments