Skip to content

[Fix] Keep uploading when a write races the write checkpoint - #1055

Draft
bean1352 wants to merge 2 commits into
mainfrom
fix/upload-loop-write-checkpoint-race
Draft

[Fix] Keep uploading when a write races the write checkpoint#1055
bean1352 wants to merge 2 commits into
mainfrom
fix/upload-loop-write-checkpoint-race

Conversation

@bean1352

@bean1352 bean1352 commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

What's wrong

When the CRUD queue drains, the upload loop requests a write checkpoint. If a local write lands inside that window, updateLocalTarget returns false because it found new CRUD, but false is also its "nothing left to upload" answer. The loop can't tell those apart, so it breaks out while the row is still in ps_crud.

The sync core then refuses every checkpoint with "Could not apply checkpoint due to local data". Usually the racing write's own notification is latched while the loop is busy and wakes it again right after it exits, so this shows up as a short stall.

The fix

Ask the adapter whether CRUD is still pending before breaking, and go round again if it is. This covers both cases where updateLocalTarget reports a race, including the sequence updated branch.

hasCrud() is only reached after nextCrudItem() returned null, so a true result means new data really did arrive during the round trip. If a connector doesn't complete its batch, the existing "previously encountered CRUD item" guard still catches that on the next pass, so this can't spin.

AI disclosure

I used Opus 5 to help research the original bug report, narrow down where the fault could be, and apply the fix. I reviewed and supervised the work.

@changeset-bot

changeset-bot Bot commented Jul 29, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 44914ed

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 7 packages
Name Type
@powersync/shared-internals Patch
@powersync/adapter-sql-js Patch
@powersync/capacitor Patch
@powersync/node Patch
@powersync/react-native Patch
@powersync/web Patch
@powersync/diagnostics-app Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@simolus3

Copy link
Copy Markdown
Contributor

The loop can't tell those apart, so it breaks out while the row is still in ps_crud.

Shouldn't the new write also have triggered a crud trigger? If we change crudUploadLoop to acquire the wait notification promise before the rest of the upload, does that work?

  private async crudUploadLoop(signal: AbortSignal, options: ResolvedSyncOptions): Promise<void> {
    while (!signal.aborted) {
      const crudChangeNotification = this.crudUploadNotifier.waitForNotification(signal);
      await Promise.all([
        // Start the initial CRUD upload on connect. Then, keep polling until we're done.
        this._uploadAllCrud(signal, options),
        this.delayRetry(signal, options.crudUploadThrottleMs)
      ]);

      await crudChangeNotification;
    }
  }

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