From 48fdff3efbc18ef2ae190b38932a69df0ca8ce32 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Tue, 6 Jan 2026 11:00:36 +0000 Subject: [PATCH] fix: Preserve ackState for old acknowledged alerts during restore When LoadActiveAlerts skipped acknowledged alerts older than 1 hour, it was also not populating ackState. This meant that when the same alert (e.g., backup-age) was recreated on the next poll cycle, preserveAlertState couldn't find any acknowledgement record and the alert would retrigger notifications. Now ackState is populated even for skipped old acknowledged alerts, so if they reappear, the acknowledgement will be restored. Related to #1043 --- internal/alerts/alerts.go | 15 +++++++++++++-- internal/alerts/alerts_test.go | 14 +++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/internal/alerts/alerts.go b/internal/alerts/alerts.go index 37ddb50b2..201ed0649 100644 --- a/internal/alerts/alerts.go +++ b/internal/alerts/alerts.go @@ -8726,9 +8726,20 @@ func (m *Manager) LoadActiveAlerts() error { continue } - // Skip acknowledged alerts older than 1 hour + // Skip acknowledged alerts older than 1 hour from activeAlerts, + // but still preserve the ackState so if the same alert reappears + // (e.g., backup-age alerts) it won't retrigger notifications. if alert.Acknowledged && alert.AckTime != nil && now.Sub(*alert.AckTime) > time.Hour { - log.Debug().Str("alertID", alert.ID).Msg("Skipping old acknowledged alert") + log.Debug().Str("alertID", alert.ID).Msg("Skipping old acknowledged alert from activeAlerts but preserving ackState") + ackTime := alert.StartTime + if alert.AckTime != nil { + ackTime = *alert.AckTime + } + m.ackState[alert.ID] = ackRecord{ + acknowledged: true, + user: alert.AckUser, + time: ackTime, + } continue } diff --git a/internal/alerts/alerts_test.go b/internal/alerts/alerts_test.go index 00b18add1..ca262867a 100644 --- a/internal/alerts/alerts_test.go +++ b/internal/alerts/alerts_test.go @@ -15343,10 +15343,22 @@ func TestLoadActiveAlerts(t *testing.T) { m.mu.RLock() _, exists := m.activeAlerts["old-ack-alert"] + ackRecord, ackExists := m.ackState["old-ack-alert"] m.mu.RUnlock() if exists { - t.Error("old acknowledged alert (>1h) should be skipped during load") + t.Error("old acknowledged alert (>1h) should be skipped from activeAlerts") + } + + // But ackState should be preserved so the alert doesn't retrigger if it reappears + if !ackExists { + t.Error("ackState should be preserved for old acknowledged alerts to prevent retriggering") + } + if ackExists && !ackRecord.acknowledged { + t.Error("ackState.acknowledged should be true") + } + if ackExists && ackRecord.user != "testuser" { + t.Errorf("ackState.user should be 'testuser', got %q", ackRecord.user) } })