@@ -111,6 +111,12 @@ func (cwm *coordinationWindowMetrics) recordWindowStart(window *coordinationWind
111111 cwm .mu .Lock ()
112112 defer cwm .mu .Unlock ()
113113
114+ cwm .initializeWindowIfNeeded (window )
115+ }
116+
117+ // initializeWindowIfNeeded initializes window metrics if they don't exist.
118+ // This function assumes the caller already holds cwm.mu.Lock().
119+ func (cwm * coordinationWindowMetrics ) initializeWindowIfNeeded (window * coordinationWindow ) {
114120 windowIndex := window .index ()
115121 if windowIndex == 0 {
116122 // Invalid window, skip
@@ -152,6 +158,11 @@ func (cwm *coordinationWindowMetrics) recordWindowEnd(window *coordinationWindow
152158 return
153159 }
154160
161+ // Don't overwrite EndTime if it's already been set
162+ if ! wm .EndTime .IsZero () {
163+ return
164+ }
165+
155166 wm .EndTime = time .Now ()
156167 wm .Duration = wm .EndTime .Sub (wm .StartTime )
157168
@@ -162,9 +173,6 @@ func (cwm *coordinationWindowMetrics) recordWindowEnd(window *coordinationWindow
162173 clientinfo .MetricCoordinationWindowDurationSeconds ,
163174 wm .Duration ,
164175 )
165-
166- // Record window-level gauges
167- cwm .recordWindowGauges (windowIndex , wm )
168176 }
169177}
170178
@@ -191,7 +199,8 @@ func (cwm *coordinationWindowMetrics) recordWalletCoordination(
191199 wm , exists := cwm .windows [windowIndex ]
192200 if ! exists {
193201 // Window not initialized, initialize it now
194- cwm .recordWindowStart (window )
202+ // Note: we already hold the lock, so use the lock-free helper
203+ cwm .initializeWindowIfNeeded (window )
195204 wm = cwm .windows [windowIndex ]
196205 }
197206
@@ -246,37 +255,6 @@ func (cwm *coordinationWindowMetrics) recordWalletCoordination(
246255 wm .WalletCoordinationDetails = append (wm .WalletCoordinationDetails , detail )
247256}
248257
249- // recordWindowGauges records gauge metrics for a specific window.
250- func (cwm * coordinationWindowMetrics ) recordWindowGauges (
251- windowIndex uint64 ,
252- wm * windowMetrics ,
253- ) {
254- // Record window-level gauges with window index suffix
255- // These allow tracking individual window metrics
256- windowSuffix := fmt .Sprintf ("_window_%d" , windowIndex )
257-
258- cwm .performanceMetrics .SetGauge (
259- clientinfo .MetricCoordinationWindowWalletsCoordinated + windowSuffix ,
260- float64 (wm .WalletsCoordinated ),
261- )
262- cwm .performanceMetrics .SetGauge (
263- clientinfo .MetricCoordinationWindowWalletsSuccessful + windowSuffix ,
264- float64 (wm .WalletsSuccessful ),
265- )
266- cwm .performanceMetrics .SetGauge (
267- clientinfo .MetricCoordinationWindowWalletsFailed + windowSuffix ,
268- float64 (wm .WalletsFailed ),
269- )
270- cwm .performanceMetrics .SetGauge (
271- clientinfo .MetricCoordinationWindowTotalFaults + windowSuffix ,
272- float64 (wm .TotalFaults ),
273- )
274- cwm .performanceMetrics .SetGauge (
275- clientinfo .MetricCoordinationWindowCoordinationBlock + windowSuffix ,
276- float64 (wm .CoordinationBlock ),
277- )
278- }
279-
280258// GetWindowMetrics returns metrics for a specific window.
281259func (cwm * coordinationWindowMetrics ) GetWindowMetrics (windowIndex uint64 ) (* windowMetrics , bool ) {
282260 cwm .mu .RLock ()
@@ -287,9 +265,8 @@ func (cwm *coordinationWindowMetrics) GetWindowMetrics(windowIndex uint64) (*win
287265 return nil , false
288266 }
289267
290- // Return a copy to avoid race conditions
291- wmCopy := * wm
292- return & wmCopy , true
268+ // Return a deep copy to avoid race conditions
269+ return wm .deepCopy (), true
293270}
294271
295272// GetRecentWindows returns metrics for the most recent N windows.
@@ -317,12 +294,11 @@ func (cwm *coordinationWindowMetrics) GetRecentWindows(limit int) []*windowMetri
317294 indices = indices [:limit ]
318295 }
319296
320- // Return copies
297+ // Return deep copies
321298 result := make ([]* windowMetrics , 0 , len (indices ))
322299 for _ , idx := range indices {
323300 wm := cwm .windows [idx ]
324- wmCopy := * wm
325- result = append (result , & wmCopy )
301+ result = append (result , wm .deepCopy ())
326302 }
327303
328304 return result
@@ -376,8 +352,7 @@ func (cwm *coordinationWindowMetrics) GetSummary() WindowMetricsSummary {
376352 summary .TotalWalletsFailed += wm .WalletsFailed
377353 summary .TotalFaults += wm .TotalFaults
378354
379- wmCopy := * wm
380- summary .Windows = append (summary .Windows , & wmCopy )
355+ summary .Windows = append (summary .Windows , wm .deepCopy ())
381356 }
382357
383358 return summary
@@ -393,6 +368,53 @@ type WindowMetricsSummary struct {
393368 Windows []* windowMetrics `json:"windows"`
394369}
395370
371+ // deepCopy creates a deep copy of windowMetrics, properly copying all maps and slices.
372+ func (wm * windowMetrics ) deepCopy () * windowMetrics {
373+ if wm == nil {
374+ return nil
375+ }
376+
377+ wmCopy := & windowMetrics {
378+ WindowIndex : wm .WindowIndex ,
379+ CoordinationBlock : wm .CoordinationBlock ,
380+ StartTime : wm .StartTime ,
381+ EndTime : wm .EndTime ,
382+ Duration : wm .Duration ,
383+ ActivePhaseEndBlock : wm .ActivePhaseEndBlock ,
384+ EndBlock : wm .EndBlock ,
385+ WalletsCoordinated : wm .WalletsCoordinated ,
386+ WalletsSuccessful : wm .WalletsSuccessful ,
387+ WalletsFailed : wm .WalletsFailed ,
388+ TotalProceduresStarted : wm .TotalProceduresStarted ,
389+ TotalProceduresCompleted : wm .TotalProceduresCompleted ,
390+ TotalFaults : wm .TotalFaults ,
391+ Leaders : make (map [string ]uint64 , len (wm .Leaders )),
392+ ActionTypes : make (map [string ]uint64 , len (wm .ActionTypes )),
393+ FaultsByType : make (map [string ]uint64 , len (wm .FaultsByType )),
394+ FaultsByCulprit : make (map [string ]uint64 , len (wm .FaultsByCulprit )),
395+ WalletCoordinationDetails : make ([]walletCoordinationDetail , len (wm .WalletCoordinationDetails )),
396+ }
397+
398+ // Deep copy maps
399+ for k , v := range wm .Leaders {
400+ wmCopy .Leaders [k ] = v
401+ }
402+ for k , v := range wm .ActionTypes {
403+ wmCopy .ActionTypes [k ] = v
404+ }
405+ for k , v := range wm .FaultsByType {
406+ wmCopy .FaultsByType [k ] = v
407+ }
408+ for k , v := range wm .FaultsByCulprit {
409+ wmCopy .FaultsByCulprit [k ] = v
410+ }
411+
412+ // Deep copy slice
413+ copy (wmCopy .WalletCoordinationDetails , wm .WalletCoordinationDetails )
414+
415+ return wmCopy
416+ }
417+
396418// String returns a string representation of window metrics for logging.
397419func (wm * windowMetrics ) String () string {
398420 return fmt .Sprintf (
0 commit comments