Stop emitting no-op docker.updateStatus change rows

dockerUpdateStatusChanged compared the whole DockerUpdateStatusMeta with
reflect.DeepEqual, so the LastChecked stamp refreshed by every periodic
update check emitted a resource_changes row per container per cycle and
flooded unified_resources.db (discussion #1577, same class as #1496).
Only availability, digests, and error text count as change now.
This commit is contained in:
rcourtman
2026-07-14 09:38:13 +01:00
parent d746731c75
commit 58d533a524
2 changed files with 53 additions and 2 deletions
+12 -2
View File
@@ -255,14 +255,24 @@ func dockerCommandChanged(before, after Resource) bool {
}
func dockerUpdateStatusChanged(before, after Resource) bool {
var beforeStatus, afterStatus any
var beforeStatus, afterStatus *DockerUpdateStatusMeta
if before.Docker != nil {
beforeStatus = before.Docker.UpdateStatus
}
if after.Docker != nil {
afterStatus = after.Docker.UpdateStatus
}
return !reflect.DeepEqual(beforeStatus, afterStatus)
if beforeStatus == nil || afterStatus == nil {
return beforeStatus != afterStatus
}
// LastChecked is restamped by every periodic update check, so comparing the
// whole struct emitted a no-op docker.updateStatus change row per container
// per check cycle (the resource_changes flooding behind issue #1577). Only
// semantic fields count as change.
return beforeStatus.UpdateAvailable != afterStatus.UpdateAvailable ||
beforeStatus.CurrentDigest != afterStatus.CurrentDigest ||
beforeStatus.LatestDigest != afterStatus.LatestDigest ||
beforeStatus.Error != afterStatus.Error
}
func dockerRestartChanged(before, after Resource) bool {
@@ -276,6 +276,47 @@ func TestBuildResourceChange_ClassifiesDockerRestartChange(t *testing.T) {
}
}
func TestBuildResourceChange_IgnoresUpdateStatusLastCheckedOnly(t *testing.T) {
before := Resource{
ID: "container:1",
Type: ResourceTypeAppContainer,
Name: "container-1",
Status: StatusOnline,
Docker: &DockerData{
Image: "example/app:1",
UpdateStatus: &DockerUpdateStatusMeta{
UpdateAvailable: false,
CurrentDigest: "sha256:aaa",
LatestDigest: "sha256:aaa",
LastChecked: time.Date(2026, 7, 14, 6, 0, 0, 0, time.UTC),
},
},
}
after := before
refreshed := *before.Docker.UpdateStatus
refreshed.LastChecked = refreshed.LastChecked.Add(2 * time.Hour)
afterDocker := *before.Docker
afterDocker.UpdateStatus = &refreshed
after.Docker = &afterDocker
if change := buildResourceChange(before, true, after, true, time.Now().UTC(), nil, SourcePulseDiff, ""); change != nil {
t.Fatalf("expected no change for LastChecked-only refresh, got %+v", change)
}
available := refreshed
available.UpdateAvailable = true
available.LatestDigest = "sha256:bbb"
afterDocker.UpdateStatus = &available
change := buildResourceChange(before, true, after, true, time.Now().UTC(), nil, SourcePulseDiff, "")
if change == nil {
t.Fatal("expected update-status change when digest/availability changed, got nil")
}
if !sameStringSet(mustChangedFields(t, change), []string{"docker.updateStatus"}) {
t.Fatalf("changedFields = %+v, want docker.updateStatus", mustChangedFields(t, change))
}
}
func TestBuildResourceChange_ClassifiesKubernetesRestartChange(t *testing.T) {
before := Resource{
ID: "pod:1",