Skip to content

feat(files): resume chunked sync uploads - #423

Open
veryCrunchy wants to merge 46 commits into
mainfrom
fix/resumable-sync-uploads
Open

feat(files): resume chunked sync uploads#423
veryCrunchy wants to merge 46 commits into
mainfrom
fix/resumable-sync-uploads

Conversation

@veryCrunchy

@veryCrunchy veryCrunchy commented Aug 26, 2026

Copy link
Copy Markdown
Member

Outcome

  • Runs Android and desktop folder-sync uploads through one shared, crash-safe Nextcloud chunking-v2 state machine and one shared exact-range request body.
  • Persists an exact local-generation checkpoint before creating server state and after every accepted chunk, so restart repeats at most one idempotent chunk PUT.
  • Reconciles resumed server collections by retaining only the exact contiguous chunk prefix and deleting stale, mismatched, or post-hole chunks before continuing.
  • Assembles into a UUID-owned hidden stage, then publishes separately with source and destination generation guards; cleanup is restricted to state owned by this client.
  • Uses read/write inactivity timeouts with no whole-call duration cap, preserves Android's approved-certificate trust, and keeps transfer work off the UI thread.
  • Streams chunk-collection XML instead of imposing an aggregate metadata byte ceiling. The parser retains bounded fields and enforces only Nextcloud's protocol record count.
  • Falls back to direct streaming when a file cannot use the chunking-v2 envelope, so the protocol's chunk rules do not become a supported-file-size limit.

The resume and reconciliation behavior follows the Nextcloud chunking-v2 documentation and the official desktop client's contiguous-prefix recovery model:

Verification

  • bash tools/check-repository.sh
  • :ui:desktopTest :androidApp:testDebugUnitTest
  • :androidApp:verifyReleaseLintGate
  • :androidApp:assembleDebug
  • :ui:createDistributable with direct desktop package updates enabled
  • Shared boundary, exact-generation, restart, stale-session, and server-prefix reconciliation tests
  • Android chunking-v2 header, guarded publication, and verbose streamed DAV metadata coverage
  • Desktop exact-range body and staged publication transport coverage

Compatibility and risk

Nextcloud's documented 5 MiB to 5 GiB chunk sizes and 10,000-chunk collection maximum are protocol constraints, not client file eligibility limits. Files outside a safe chunk plan remain transferable through the direct streaming path. Persisted snapshots without an upload checkpoint remain compatible through the checkpoint field's default value.

The visible destination is never used as the assembly target. An interrupted or rejected transfer can therefore clean up only its UUID-owned collection and stage without overwriting an unrelated local or remote generation.

Visual changes

Not applicable.

Closes #113

@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #423 / NC Native August 26, 2026 12:45 Destroyed
@obiente-cloud

obiente-cloud Bot commented Aug 26, 2026

Copy link
Copy Markdown

Obiente preview

NC Native · 3f130cebe51a · Ready

Open preview

View in Obiente

Obiente updates this comment as the preview changes.

Base automatically changed from fix/unbounded-user-files to main August 26, 2026 12:46
@veryCrunchy
veryCrunchy force-pushed the fix/resumable-sync-uploads branch from e54db03 to efeb92e Compare August 26, 2026 12:46
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #423 / NC Native August 26, 2026 12:56 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #423 / NC Native August 26, 2026 13:12 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #423 / NC Native August 26, 2026 13:13 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #423 / NC Native August 26, 2026 13:19 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #423 / NC Native August 26, 2026 13:20 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #423 / NC Native August 26, 2026 13:26 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #423 / NC Native August 26, 2026 13:30 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #423 / NC Native August 26, 2026 13:31 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #423 / NC Native August 26, 2026 13:34 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #423 / NC Native August 26, 2026 13:40 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #423 / NC Native August 26, 2026 13:41 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0d0521b39e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #423 / NC Native August 27, 2026 16:01 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #423 / NC Native August 27, 2026 16:07 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5f1f8e5ba1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #423 / NC Native August 27, 2026 16:41 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #423 / NC Native August 27, 2026 16:46 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 67c656bf22

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread androidApp/src/main/kotlin/dev/obiente/nextcloudnative/AndroidFileSyncEngine.kt Outdated
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #423 / NC Native August 27, 2026 17:13 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #423 / NC Native August 27, 2026 17:17 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 921a4a9112

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/src/desktopMain/kotlin/dev/obiente/nextcloudnative/app/DesktopFileSyncStore.kt Outdated
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #423 / NC Native August 27, 2026 17:36 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cc548371e0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/src/desktopMain/kotlin/dev/obiente/nextcloudnative/app/DesktopFileSyncStore.kt Outdated
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #423 / NC Native August 27, 2026 17:59 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: de0d5ae123

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #423 / NC Native August 27, 2026 18:24 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #423 / NC Native August 27, 2026 18:29 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a27b516149

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3f130cebe5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +125 to +130
FileSyncPendingUploadCleanup(
checkpoint.uploadId,
work.relativePath,
checkpoint.assembledStageEtag,
work.replacementBackupEtag(),
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve chunked publication state when abandoning work

When a chunked file-over-directory MOVE succeeds and the client exits before verification, deleting the local source before the next scan causes this conversion to abandon the checkpoint while retaining only the stage and backup ETags. The resulting cleanup defaults publicationInFlight to false and carries no content evidence, so the following run's discardOwnedUpload treats the visible file as staging, deletes it when its ETag matches assembledStageEtag, and restores the old directory. Unlike the newly added direct-replacement recovery, this chunked path still loses the publication evidence; retain enough checkpoint state to reconcile the published file instead of rolling it back.

AGENTS.md reference: AGENTS.md:L428-L430

Useful? React with 👍 / 👎.

Comment on lines +76 to +79
moveRemotePath(
stagingPath, destinationPath, verifiedStageEtag, sourceIsDirectory = false,
mutationRelativePaths = arrayOf(relativePath),
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Cancel direct replacement publication when sync pauses

When desktop sync is paused while this direct replacement's final MOVE is waiting for a response, moveRemotePath executes through DesktopHttpMutationExecutor without the available shouldContinue callback. The file-sync client has no whole-call timeout and a 30-minute inactivity timeout, so the paused run can remain blocked and the server mutation can continue; the latest cancellation fix covers chunked publication but leaves this direct-replacement path uncancelled.

AGENTS.md reference: AGENTS.md:L274-L276

Useful? React with 👍 / 👎.

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.

[SYNC-007] Resumable large uploads

1 participant