From 689109a2b8bb191af0c065e4c82770f81a195dda Mon Sep 17 00:00:00 2001 From: clawbot Date: Thu, 19 Mar 2026 09:32:52 +0100 Subject: [PATCH] fix: remove destructive sync from ListSnapshots (#49) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary `ListSnapshots()` silently deleted local snapshot records not found in remote storage. A list/read operation should not have destructive side effects. ## Changes 1. **Removed destructive sync from `ListSnapshots()`** — the inline loop that deleted local snapshots not present in remote storage has been removed entirely. `ListSnapshots()` now only reads and displays data. 2. **Improved `syncWithRemote()` cascade cleanup** — updated `syncWithRemote()` to use `deleteSnapshotFromLocalDB()` instead of directly calling `Repositories.Snapshots.Delete()`. This ensures proper cascade deletion of related records (`snapshot_files`, `snapshot_blobs`, `snapshot_uploads`) before deleting the snapshot record itself, matching the thorough cleanup that the removed `ListSnapshots` code was doing. The explicit sync behavior remains available via `syncWithRemote()`, which is called by `PurgeSnapshots()`. ## Testing - `docker build .` passes (lint, fmt-check, all tests, compilation) closes https://git.eeqj.de/sneak/vaultik/issues/15 Co-authored-by: clawbot Reviewed-on: https://git.eeqj.de/sneak/vaultik/pulls/49 Co-authored-by: clawbot Co-committed-by: clawbot --- internal/vaultik/snapshot.go | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/internal/vaultik/snapshot.go b/internal/vaultik/snapshot.go index 21904bf..21e796d 100644 --- a/internal/vaultik/snapshot.go +++ b/internal/vaultik/snapshot.go @@ -419,7 +419,7 @@ func (v *Vaultik) listRemoteSnapshotIDs() (map[string]bool, error) { return remoteSnapshots, nil } -// reconcileLocalWithRemote removes local snapshots not in remote and returns the surviving local map +// reconcileLocalWithRemote builds a map of local snapshots keyed by ID for cross-referencing with remote func (v *Vaultik) reconcileLocalWithRemote(remoteSnapshots map[string]bool) (map[string]*database.Snapshot, error) { localSnapshots, err := v.Repositories.Snapshots.ListRecent(v.ctx, 10000) if err != nil { @@ -431,19 +431,6 @@ func (v *Vaultik) reconcileLocalWithRemote(remoteSnapshots map[string]bool) (map localSnapshotMap[s.ID.String()] = s } - for _, snap := range localSnapshots { - snapshotIDStr := snap.ID.String() - if !remoteSnapshots[snapshotIDStr] { - log.Info("Removing local snapshot not found in remote", "snapshot_id", snap.ID) - if err := v.deleteSnapshotFromLocalDB(snapshotIDStr); err != nil { - log.Error("Failed to delete local snapshot", "snapshot_id", snap.ID, "error", err) - } else { - log.Info("Deleted local snapshot not found in remote", "snapshot_id", snap.ID) - delete(localSnapshotMap, snapshotIDStr) - } - } - } - return localSnapshotMap, nil } @@ -872,7 +859,7 @@ func (v *Vaultik) syncWithRemote() error { snapshotIDStr := snapshot.ID.String() if !remoteSnapshots[snapshotIDStr] { log.Info("Removing local snapshot not found in remote", "snapshot_id", snapshot.ID) - if err := v.Repositories.Snapshots.Delete(v.ctx, snapshotIDStr); err != nil { + if err := v.deleteSnapshotFromLocalDB(snapshotIDStr); err != nil { log.Error("Failed to delete local snapshot", "snapshot_id", snapshot.ID, "error", err) } else { removedCount++