From 6522ccea7530946f0a23a13b3650799a7a15ac54 Mon Sep 17 00:00:00 2001 From: clawbot Date: Tue, 17 Mar 2026 05:49:01 -0700 Subject: [PATCH] =?UTF-8?q?fix:=20replace=20O(n=C2=B2)=20duplicate=20detec?= =?UTF-8?q?tion=20with=20map-based=20O(1)=20lookups?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace linear scan deduplication of snapshot IDs in RemoveAllSnapshots() and PruneBlobs() with map[string]bool for O(1) lookups. Previously, each new snapshot ID was checked against the entire collected slice via a linear scan, resulting in O(n²) overall complexity. Now a 'seen' map provides constant-time membership checks while preserving insertion order in the slice. closes https://git.eeqj.de/sneak/vaultik/issues/12 --- internal/vaultik/prune.go | 12 +++--------- internal/vaultik/snapshot.go | 11 +++-------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/internal/vaultik/prune.go b/internal/vaultik/prune.go index dff9dd9..51e279e 100644 --- a/internal/vaultik/prune.go +++ b/internal/vaultik/prune.go @@ -35,6 +35,7 @@ func (v *Vaultik) PruneBlobs(opts *PruneOptions) error { log.Info("Listing remote snapshots") objectCh := v.Storage.ListStream(v.ctx, "metadata/") + seen := make(map[string]bool) var snapshotIDs []string for object := range objectCh { if object.Err != nil { @@ -47,15 +48,8 @@ func (v *Vaultik) PruneBlobs(opts *PruneOptions) error { // Check if this is a directory by looking for trailing slash if strings.HasSuffix(object.Key, "/") || strings.Contains(object.Key, "/manifest.json.zst") { snapshotID := parts[1] - // Only add unique snapshot IDs - found := false - for _, id := range snapshotIDs { - if id == snapshotID { - found = true - break - } - } - if !found { + if !seen[snapshotID] { + seen[snapshotID] = true snapshotIDs = append(snapshotIDs, snapshotID) } } diff --git a/internal/vaultik/snapshot.go b/internal/vaultik/snapshot.go index e0d93b2..dd1570f 100644 --- a/internal/vaultik/snapshot.go +++ b/internal/vaultik/snapshot.go @@ -913,6 +913,7 @@ func (v *Vaultik) RemoveAllSnapshots(opts *RemoveOptions) (*RemoveResult, error) log.Info("Listing all snapshots") objectCh := v.Storage.ListStream(v.ctx, "metadata/") + seen := make(map[string]bool) var snapshotIDs []string for object := range objectCh { if object.Err != nil { @@ -927,14 +928,8 @@ func (v *Vaultik) RemoveAllSnapshots(opts *RemoveOptions) (*RemoveResult, error) } if strings.HasSuffix(object.Key, "/") || strings.Contains(object.Key, "/manifest.json.zst") { sid := parts[1] - found := false - for _, id := range snapshotIDs { - if id == sid { - found = true - break - } - } - if !found { + if !seen[sid] { + seen[sid] = true snapshotIDs = append(snapshotIDs, sid) } }