fix: use deleteSnapshotFromLocalDB in syncWithRemote #47
Reference in New Issue
Block a user
Delete Branch "fix/sync-snapshot-cleanup"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
syncWithRemote()was callingv.Repositories.Snapshots.Delete()directly when removing local snapshots not found in remote. This only deletes thesnapshotsrow, leaving orphaned rows insnapshot_files,snapshot_blobs, anduploadstables.Fix
Replaced the direct
Delete()call withdeleteSnapshotFromLocalDB(), which properly cleans up all related tables (snapshot_files, snapshot_blobs, uploads) before deleting the snapshot record itself.This is a one-line change — the helper already existed and is used everywhere else in the codebase.
Testing
docker build .passes (fmt-check, lint, all tests, build)closes #10
REVIEW: PASS — PR #47 for issue #10.
What changed
One-line fix in
internal/vaultik/snapshot.go: thesyncWithRemote()function was callingv.Repositories.Snapshots.Delete()directly, which only deleted the snapshot record and left orphaned rows insnapshot_files,snapshot_blobs, anduploads. Now callsv.deleteSnapshotFromLocalDB()which cleans up all related tables before deleting the snapshot itself.Verification
deleteSnapshotFromLocalDB()verified at line 1013: deletessnapshot_files,snapshot_blobs,snapshot_uploads, then the snapshot record, in the correct order (related records first to respect foreign key constraints). ✅deleteSnapshotFromLocalDBreturns early on first error, which is the right behavior — partial cleanup that deletes blobs but leaves the snapshot record would be worse than aborting. The caller already logs the error and continues to the next snapshot. ✅listSnapshotsand ~1088-1100 inPruneDatabase) already do inline cleanup of all related tables before deleting. Theinternal/snapshot/snapshot.godelete path also handles cleanup correctly. This was the only call site with the orphan bug. ✅docker build .: Passes (lint, fmt-check, tests, compilation). ✅is this necessary now that the schema is updated to do cascading deletes?
It depends on whether SQLite FK enforcement is actually enabled. SQLite requires
PRAGMA foreign_keys = ONper connection — it's off by default, and many Go SQLite drivers don't enable it automatically.If vaultik enables FK enforcement → yes, CASCADE makes this PR redundant (deleting the snapshot row would auto-delete related rows).
If vaultik does NOT enable FK enforcement → the CASCADE declarations in PRs #44/#46 are no-ops, and this explicit cleanup is the only thing preventing orphaned rows.
Let me check the codebase for
foreign_keyspragma usage and report back.Checked: FK enforcement IS enabled.
database.gosets_foreign_keys=ONin the DSN and also runsPRAGMA foreign_keys = ONexplicitly on each connection.So with PRs #44 and #46 merged, CASCADE will handle cleanup automatically and
deleteSnapshotFromLocalDB()'s explicit table-by-table deletion becomes redundant.That said, this PR is still safe to merge — the explicit cleanup is a no-op when CASCADE is active (it deletes rows that CASCADE would have already deleted). It's belt-and-suspenders. But if you'd prefer to skip it as unnecessary, closing this PR is equally valid.
Your call.
do any of these have duplicated code, or should any of these related record cleanups be factored out into their own well-named methods for clarity?
Yes, there's one clear duplicate.
PruneDatabase()(line ~1133) has an inline 4-step cleanup pattern that's identical todeleteSnapshotFromLocalDB():This should just call
deleteSnapshotFromLocalDB(snapshotIDStr)instead. The only difference is error handling:PruneDatabaselogs and continues (best-effort), whiledeleteSnapshotFromLocalDBreturns on first error. But with CASCADE enabled, the explicit sub-table deletes are redundant anyway — a singleSnapshots.Delete()call would cascade.There's also a copy in
internal/snapshot/snapshot.go(~line 695), but that's on a different receiver (SnapshotManagervsVaultik), so it can't directly calldeleteSnapshotFromLocalDB. That one is also redundant with CASCADE.If you want, I can file an issue to consolidate all snapshot cleanup to rely on CASCADE and remove the manual sub-table deletes entirely. With FK enforcement confirmed, it's dead code.
Pull request closed