Skip to content

Persist attachment state per record during sync passes#2

Merged
austinbhale merged 1 commit into
mainfrom
fix/persist-attachment-state-per-record
Jun 10, 2026
Merged

Persist attachment state per record during sync passes#2
austinbhale merged 1 commit into
mainfrom
fix/persist-attachment-state-per-record

Conversation

@austinbhale

Copy link
Copy Markdown
Member

RunSyncPassAsync held the attachment context mutex for the entire pass and saved all state changes in one batch at the end. On a large queue, a single pass starved every other queue operation for its duration, plus any interruption discarded the progress of every completed transfer.

The context is now acquired only for table reads and writes, and each attachment is persisted as soon as its transfer completes, matching the JS & Dart helpers.

@austinbhale austinbhale requested a review from Copilot June 10, 2026 08:25
@austinbhale austinbhale merged commit b66032c into main Jun 10, 2026
3 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adjusts the attachment syncing pipeline to reduce lock contention by narrowing the scope of the attachment-context mutex and persisting progress incrementally, so large queues don’t block other queue operations and completed transfers aren’t lost on interruption.

Changes:

  • Refactors RunSyncPassAsync to acquire the attachment context only for table reads/writes, not for the entire sync pass.
  • Updates ProcessAttachmentsAsync to persist each attachment state change immediately after its transfer completes.
  • Updates DeleteAttachmentAsync to perform DB deletion via WithContextAsync internally instead of requiring a context parameter.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 133 to +143
Attachment? changed = attachment.State switch
{
AttachmentState.QueuedUpload => await UploadAttachmentAsync(attachment),
AttachmentState.QueuedDownload => await DownloadAttachmentAsync(attachment),
AttachmentState.QueuedDelete => await DeleteAttachmentAsync(attachment, context),
AttachmentState.QueuedDelete => await DeleteAttachmentAsync(attachment),
_ => null,
};

if (changed is not null)
{
updates.Add(changed);
await attachmentService.WithContextAsync(ctx => ctx.SaveAttachmentsAsync([changed]));
}

await context.DeleteAttachmentAsync(attachment.Id);
await attachmentService.WithContextAsync(ctx => ctx.DeleteAttachmentAsync(attachment.Id));
Comment on lines +118 to +120
var active = await attachmentService.WithContextAsync(ctx => ctx.GetActiveAttachmentsAsync());
await ProcessAttachmentsAsync(active);
await attachmentService.WithContextAsync(ctx => DeleteArchivedAttachmentsAsync(ctx));
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