From 6df2940d2f759e74c4b315a24e69fdd7d1ab4eef Mon Sep 17 00:00:00 2001 From: Josephine Pfeiffer Date: Sun, 21 Sep 2025 15:48:38 +0200 Subject: [PATCH] Fix max absence calculation continuously growing The LongestAbsence function was incorrectly updating the max absence to current time when it exceeded the stored value, causing it to grow indefinitely. This fix ensures max absence only tracks completed absence periods between heartbeats. --- database.go | 47 ++++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/database.go b/database.go index d3f89bd..aad1ebe 100644 --- a/database.go +++ b/database.go @@ -121,24 +121,16 @@ func LastSeen() string { return time.Unix(lastBeat.Timestamp, 0).Format(timeFormat) } -// LongestAbsence will return HeartbeatStats.LongestMissingBeat unless the current missing beat is longer +// LongestAbsence returns the longest recorded absence between heartbeats func LongestAbsence() string { lastBeat := GetMostRecentBeat() - // This will happen when GetLastBeat returned a nil, and heartbeatDevices is empty if lastBeat == nil { heartbeatStats.LastBeatFormatted = "Never" return "Never" } - diff := time.Now().Unix() - lastBeat.Timestamp - // If current absence is bigger than record absence, return current absence - if diff > heartbeatStats.LongestMissingBeat { - heartbeatStats.LongestMissingBeat = diff - return FormattedTime(diff) - } else { - return FormattedTime(heartbeatStats.LongestMissingBeat) - } + return FormattedTime(heartbeatStats.LongestMissingBeat) } func UpdateNumDevices() { @@ -189,27 +181,40 @@ func UpdateDevice(beat HeartbeatBeat) { } if n == -1 { // couldn't find an existing device with the name - device = HeartbeatDevice{beat.DeviceName, beat, 0, 0} + device = HeartbeatDevice{beat.DeviceName, beat, 1, 0} + // Don't calculate absence for new devices - append and return + *heartbeatDevices = append(*heartbeatDevices, device) + PostMessage("New device added", fmt.Sprintf("A new device called `%s` was added on at ", beat.DeviceName, beat.Timestamp, beat.Timestamp), EmbedColorGreen, WebhookLevelSimple) + UpdateNumDevices() + return + } + + // Validate timestamp to handle clock skew or duplicate beats + if beat.Timestamp <= device.LastBeat.Timestamp { + // Update device but don't calculate absence for non-advancing timestamps + device.LastBeat = beat + device.TotalBeats += 1 + (*heartbeatDevices)[n] = device + UpdateTotalBeats() + return } - diff := time.Now().Unix() - device.LastBeat.Timestamp + // Calculate the absence period that just ended (from old beat to new beat) + diff := beat.Timestamp - device.LastBeat.Timestamp if diff > device.LongestMissingBeat { device.LongestMissingBeat = diff } - // We want to update the longest absence here (heartbeatStats.LongestMissingBeat) in case - // device.LongestMissingBeat > heartbeatStats.LongestMissingBeat *and* other devices haven't pinged recently - LongestAbsence() + // Update global longest absence if this device's absence is longer + if diff > heartbeatStats.LongestMissingBeat { + heartbeatStats.LongestMissingBeat = diff + } device.LastBeat = beat device.TotalBeats += 1 UpdateTotalBeats() - if n == -1 { // if device doesn't exist, append it, else replace it - *heartbeatDevices = append(*heartbeatDevices, device) - PostMessage("New device added", fmt.Sprintf("A new device called `%s` was added on at ", beat.DeviceName, beat.Timestamp, beat.Timestamp), EmbedColorGreen, WebhookLevelSimple) - } else { - (*heartbeatDevices)[n] = device - } + // Update existing device in slice + (*heartbeatDevices)[n] = device } // UpdateLastBeat will save the last beat and insert a new HeartbeatBeat into beats