Skip to content

Persist paste pull cursor for discarded oversized remote pastes - #4703

Merged
guiyanakuang merged 1 commit into
mainfrom
fix/issue-4702-durable-paste-pull-cursor
Jul 31, 2026
Merged

Persist paste pull cursor for discarded oversized remote pastes#4703
guiyanakuang merged 1 commit into
mainfrom
fix/issue-4702-durable-paste-pull-cursor

Conversation

@guiyanakuang

Copy link
Copy Markdown
Member

Closes #4702

Problem

#4699 discards oversized remote non-file pastes without storing them, but the pull cursor only advances in memory. After a restart, PastePullService.init() rebuilds each device's cursor from the max createTime of stored pastes — which never includes a discarded paste. If the newest paste from a peer was discarded, every restart re-pulls it, re-discards it, and re-notifies, repeatedly transferring tens of MB until the peer produces a newer stored paste.

Changes

  • New PastePullCursorEntity table (appInstanceId PK, maxCreateTime) with SQLDelight migration 3.sqm. The migration uses CREATE TABLE IF NOT EXISTS so it stays idempotent under DesktopDriverFactory's migration replay after a fresh create.
  • Discard path (PasteReleaseService.discardOversizedRemoteNonFilePaste) upserts the cursor with the discarded paste's createTime; the SQL upsert takes MAX(existing, excluded) so the cursor never moves backwards.
  • PastePullService.init() restores each device's cursor as the max of the stored-paste max createTime and the durable cursor.
  • Device removal cleanup: SyncDeviceManager.removeDevice (reached by both local unpair and remote NOTIFY_REMOVE) now deletes the device's cursor row alongside the other local state. PasteDao is injected directly rather than going through PastePullService to avoid a constructor dependency cycle via SyncManager.

Safety notes

  • The durable cursor is written synchronously in the discard path; in the pull batch flow items are processed in ascending createTime order and persisted inline, so the cursor can never cover an older item that has not yet been stored.
  • The pull query uses strict createTime > :createTime, so a cursor equal to the discarded paste's createTime is sufficient to suppress re-pulls.

Tests

  • PasteDaoTest: cursor upsert is monotonic; delete only removes the given device's row.
  • PasteReleaseServiceRemoteDiscardTest: discarding upserts the durable cursor exactly once.
  • PastePullServiceTest (new): init() merges stored max and durable cursor per device; in-memory updates never write the durable cursor.
  • SyncDeviceManagerTest: removeDevice deletes the cursor row.

Note for mobile: shared/commonMain changes include the new table + 3.sqm; the mobile repos must pick up the migration file when syncing shared code.

@guiyanakuang
guiyanakuang merged commit dc98f4d into main Jul 31, 2026
5 checks passed
@guiyanakuang
guiyanakuang deleted the fix/issue-4702-durable-paste-pull-cursor branch July 31, 2026 11:21
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.

Discarded oversized remote paste is re-pulled and re-discarded after every restart

1 participant