Skip to content

Implement remote attachment blob cleanup queue for sync - #77

Open
patrickunterwegs wants to merge 1 commit into
mainfrom
claude/attachment-remote-cleanup
Open

Implement remote attachment blob cleanup queue for sync#77
patrickunterwegs wants to merge 1 commit into
mainfrom
claude/attachment-remote-cleanup

Conversation

@patrickunterwegs

Copy link
Copy Markdown
Member

Summary

This PR implements a persistent queue mechanism to track and clean up orphaned attachment blobs from CalDAV servers. When attachments are removed, entries are deleted, or entries are moved between calendars, their remote blobs are now queued for deletion and cleaned up during the next sync cycle.

Key Changes

  • New PendingRemoteFileDeletion domain model: Represents a remote attachment blob queued for deletion with its calendar context and remote URL.

  • Database schema additions:

    • New PendingRemoteDeletionDto table to persist the deletion queue independently of entry lifecycle (survives entry cascading deletes)
    • Migration file 13.sqm and SQL queries in pending_remote_deletion_dto.sq for queue management
  • Repository enhancements (IcalEntryRepositoryImpl):

    • Inject FileManager dependency for local file cleanup
    • New methods: enqueueRemoteFileDeletions(), getPendingRemoteFileDeletions(), deletePendingRemoteFileDeletion()
    • Enhanced moveIcalEntries() to queue source collection blobs for deletion before re-creating entries in target calendar
    • Enhanced deleteTrashed() to queue remote blobs and delete local files before hard-deleting entries
  • Sync engine integration (SyncCoordinator):

    • New drainPendingRemoteFileDeletions() method called after each sync cycle
    • Best-effort deletion: clears queue on 2xx or 404 responses, retries on other errors
    • Integrated into both sync-token and multiget sync paths
  • WebDAV remote data source:

    • New deleteFile() method to perform HTTP DELETE on attachment blob URLs
    • Implementation in RemoteDataSourceIcalEntry.kt via deleteFileMultiplatform()
  • UI integration (DetailsViewModel):

    • When user removes an attachment, queue its remote blob for deletion if it exists on server
    • Local file is deleted immediately; remote cleanup happens during next sync

Implementation Details

  • The queue is deliberately separate from IcalEntryDto to survive entry cascading deletes
  • Moved entries' original attachments are queued for deletion only if they have local copies (re-uploaded to target); remote-only attachments keep pointing to source blobs
  • Deletion is best-effort with automatic retry on next sync; transient failures don't block other operations
  • Uses INSERT OR IGNORE to handle duplicate URLs gracefully

https://claude.ai/code/session_011XuDzYDVnD3s5Zzm7aJ3K3

The app could upload attachment blobs but never delete them: removing an
attachment, deleting an entry, or moving one to another calendar all left the
uploaded file behind in its CalDAV collection (and local files behind on the
device), so storage grew without bound.

Adds a proper cleanup lifecycle:
- New WebDAV deleteFile(url) primitive.
- New PendingRemoteDeletionDto table (schema migration 13) that queues orphaned
  blob URLs independently of entries, so the ON DELETE CASCADE from an entry
  can't lose them before they're deleted.
- SyncCoordinator drains the queue per calendar after pushing local changes,
  best-effort with retry (2xx or 404 clears the entry, anything else retries).

Enqueue points:
- Attachment removal (details screen) queues the removed blob.
- Move queues the source-collection blobs - captured before re-creating the
  entries, since the copy's attachment rows reuse the same UNIQUE uid and
  replace the originals'. Only blobs that get re-uploaded into the target are
  queued; a remote-only attachment the copy still points at is left alone.
- Trashbin hard-delete (deleteTrashed) queues each attachment's blob and
  deletes its local file before the rows cascade away.

IcalEntryRepositoryImpl now depends on FileManager to reclaim local files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011XuDzYDVnD3s5Zzm7aJ3K3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants