From 7f2d1bdae1d1fa487c6bec76f1f934cec4224724 Mon Sep 17 00:00:00 2001 From: "alexei.dolgolyov" Date: Sat, 9 May 2026 13:47:20 +0300 Subject: [PATCH] feat(workload): switch buildActiveImagesSet to containers index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First consumer migration off the instances table. The image prune logic now walks the normalized containers.image_ref column directly — one DB pass against a single table instead of joining instances against projects to reconstruct the full "image:tag" string. Demonstrates the consumer-switch pattern the remaining read sites (proxies, stale scanner, webhook matcher) will follow. The legacy `projects []store.Project` parameter is kept on the function signature for now so call sites don't change in this commit; the underscore-discard in the body makes it explicit that it's no longer load-bearing. --- internal/api/docker.go | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/internal/api/docker.go b/internal/api/docker.go index 80a6f4c..d831f29 100644 --- a/internal/api/docker.go +++ b/internal/api/docker.go @@ -245,28 +245,24 @@ func sanitizeDockerLogLine(line string) string { } // buildActiveImagesSet returns the set of "image:tag" strings currently used -// by any instance, computed in a single DB pass instead of N×K queries. -// Returning an error (rather than swallowing) prevents prune logic from -// treating a transient DB failure as "nothing is active". +// by any container, computed in a single DB pass against the normalized +// containers index. Returning an error (rather than swallowing) prevents +// prune logic from treating a transient DB failure as "nothing is active". func buildActiveImagesSet(st *store.Store, projects []store.Project) (map[string]bool, error) { - imageByProject := make(map[string]string, len(projects)) - for _, p := range projects { - imageByProject[p.ID] = p.Image - } - instances, err := st.ListAllInstances() + // `projects` is unused now — kept in the signature for back-compat with + // callers that already happen to have the slice. The image_ref column + // holds the full "image:tag" string written by the deployer. + _ = projects + containers, err := st.ListContainers(store.ContainerFilter{}) if err != nil { - return nil, fmt.Errorf("list instances: %w", err) + return nil, fmt.Errorf("list containers: %w", err) } - active := make(map[string]bool, len(instances)) - for _, inst := range instances { - if inst.ImageTag == "" { + active := make(map[string]bool, len(containers)) + for _, c := range containers { + if c.ImageRef == "" { continue } - image := imageByProject[inst.ProjectID] - if image == "" { - continue - } - active[image+":"+inst.ImageTag] = true + active[c.ImageRef] = true } return active, nil }