From b27dc0dc151a37a50b2917a2862c60e0d2341f90 Mon Sep 17 00:00:00 2001 From: auricom <27022259+auricom@users.noreply.github.com> Date: Mon, 22 Dec 2025 09:25:05 +0100 Subject: [PATCH 1/2] fix: ensure time_since_last_block accuracy with polling fallback --- pkg/exporters/drift/drift.go | 7 +++++++ pkg/metrics/metrics.go | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/pkg/exporters/drift/drift.go b/pkg/exporters/drift/drift.go index 58799da..9384352 100644 --- a/pkg/exporters/drift/drift.go +++ b/pkg/exporters/drift/drift.go @@ -61,6 +61,9 @@ func (e exporter) ExportMetrics(ctx context.Context, m *metrics.Metrics) error { m.RecordEndpointAvailability(e.chainID, e.referenceNode, true) m.RecordReferenceBlockHeight(e.chainID, e.referenceNode, refHeight) + // Also update time since last block metric when we see new blocks via polling + // This ensures the metric stays accurate even if WebSocket subscription fails + m.UpdateLastBlockTime(e.chainID, time.Now()) e.logger.Info().Uint64("height", refHeight).Str("endpoint", e.referenceNode).Msg("recorded reference node height") // get each full node height and calculate drift @@ -77,6 +80,10 @@ func (e exporter) ExportMetrics(ctx context.Context, m *metrics.Metrics) error { m.RecordCurrentBlockHeight(e.chainID, fullNode, currentHeight) m.RecordBlockHeightDrift(e.chainID, fullNode, refHeight, currentHeight) + // Also update time since last block metric when we see new blocks via polling + // This ensures the metric stays accurate even if WebSocket subscription fails + m.UpdateLastBlockTime(e.chainID, time.Now()) + drift := int64(refHeight) - int64(currentHeight) e.logger.Info(). Uint64("ref_height", refHeight). diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index 6c7c885..cce4ce7 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -568,6 +568,18 @@ func (m *Metrics) RecordBlockTime(chainID string, arrivalTime time.Time) { m.TimeSinceLastBlock.WithLabelValues(chainID).Set(0) } +// UpdateLastBlockTime updates the last block arrival time and resets time since last block metric +// without recording block time histogram. This is used by pollers that can't measure inter-block time. +func (m *Metrics) UpdateLastBlockTime(chainID string, arrivalTime time.Time) { + m.mu.Lock() + defer m.mu.Unlock() + + // update last seen arrival time + m.lastBlockArrivalTime[chainID] = arrivalTime + // reset time since last block to 0 + m.TimeSinceLastBlock.WithLabelValues(chainID).Set(0) +} + // UpdateTimeSinceLastBlock updates the time_since_last_block metric for all chains // should be called periodically to keep the metric current. func (m *Metrics) UpdateTimeSinceLastBlock() { From 2566db18f1b70f8b932d642525a2e57194297cbf Mon Sep 17 00:00:00 2001 From: auricom <27022259+auricom@users.noreply.github.com> Date: Mon, 22 Dec 2025 11:09:22 +0100 Subject: [PATCH 2/2] gemini assist --- pkg/metrics/metrics.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index cce4ce7..4de801e 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -562,10 +562,7 @@ func (m *Metrics) RecordBlockTime(chainID string, arrivalTime time.Time) { } } - // update last seen arrival time - m.lastBlockArrivalTime[chainID] = arrivalTime - // reset time since last block to 0 - m.TimeSinceLastBlock.WithLabelValues(chainID).Set(0) + m.updateLastBlockTimeUnsafe(chainID, arrivalTime) } // UpdateLastBlockTime updates the last block arrival time and resets time since last block metric @@ -573,7 +570,13 @@ func (m *Metrics) RecordBlockTime(chainID string, arrivalTime time.Time) { func (m *Metrics) UpdateLastBlockTime(chainID string, arrivalTime time.Time) { m.mu.Lock() defer m.mu.Unlock() + m.updateLastBlockTimeUnsafe(chainID, arrivalTime) +} +// updateLastBlockTimeUnsafe is an unexported helper that updates the last block arrival time +// and resets the time since last block gauge. +// This function is not thread-safe and should be called with a lock held. +func (m *Metrics) updateLastBlockTimeUnsafe(chainID string, arrivalTime time.Time) { // update last seen arrival time m.lastBlockArrivalTime[chainID] = arrivalTime // reset time since last block to 0