Skip to content

fix(android): preserve queued upload scheduling - #439

Open
veryCrunchy wants to merge 47 commits into
fix/account-background-isolationfrom
fix/durable-upload-scheduling-recovery-stack
Open

fix(android): preserve queued upload scheduling#439
veryCrunchy wants to merge 47 commits into
fix/account-background-isolationfrom
fix/durable-upload-scheduling-recovery-stack

Conversation

@veryCrunchy

Copy link
Copy Markdown
Member

Outcome

  • keep the durable Deck attachment journal after WorkManager scheduling completes ambiguously
  • preserve the queued source capability so status restoration can schedule the same job again after process restart
  • propagate cancellation after persistence without converting it into a rejected upload
  • release a newly selected source only when the queue can prove that no active job owns it

Failure path

The upload job was persisted before WorkManager scheduling, but any exception from awaiting the scheduling operation deleted that journal and released its source. WorkManager can already have accepted or started the work before its completion signal fails, leaving a server-visible upload without a durable outcome record and allowing the same attachment to be submitted again.

Validation

On the dedicated Linux build host, from exact parent 8d1989297bb99efd8e5bb40bc7e1761c98299ad2:

  • :androidApp:testDebugUnitTest --tests dev.obiente.nextcloudnative.AndroidDurableMultipartUploadPolicyTest
  • :androidApp:assembleDebug
  • bash tools/check-kotlin-architecture.sh
  • bash tools/check-repository.sh

The deterministic tests cover scheduler acceptance followed by a lost completion signal, restart-visible queued state, cancellation after persistence, and persistence failure before scheduling.

Evidence boundaries

This validates the transaction policy and Android debug build on Linux. It does not include an emulator lifecycle run, a live Nextcloud attachment upload, or an injected failure inside Android's real WorkManager database.

This PR is intentionally based on #438 because that ready account-isolation change owns the adjacent queued-account credential lookup.

Advances #52

@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #439 / NC Native September 1, 2026 21:15 Destroyed
@obiente-cloud

obiente-cloud Bot commented Sep 1, 2026

Copy link
Copy Markdown

Obiente preview

NC Native · 1caca52102f9 · Ready

Open preview

View in Obiente

Obiente updates this comment as the preview changes.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review 🔄 Running since 2026-09-04T21:39:40.762081Z 84c8005 New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #439 / NC Native September 1, 2026 21: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: 454d73b427

ℹ️ 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".

@veryCrunchy
veryCrunchy force-pushed the fix/durable-upload-scheduling-recovery-stack branch from e2a8728 to d4e541c Compare September 1, 2026 21:54
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #439 / NC Native September 1, 2026 21:54 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #439 / NC Native September 1, 2026 22:31 Destroyed
@veryCrunchy
veryCrunchy force-pushed the fix/durable-upload-scheduling-recovery-stack branch from 4482b93 to c2f0cc7 Compare September 3, 2026 20:43
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #439 / NC Native September 3, 2026 20:43 Destroyed
@veryCrunchy
veryCrunchy force-pushed the fix/durable-upload-scheduling-recovery-stack branch from c2f0cc7 to 84677b8 Compare September 3, 2026 20:54
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #439 / NC Native September 3, 2026 20:54 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: 84677b8a69

ℹ️ 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".

@veryCrunchy
veryCrunchy force-pushed the fix/durable-upload-scheduling-recovery-stack branch from 84677b8 to abcbb66 Compare September 3, 2026 21:18
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #439 / NC Native September 3, 2026 21:19 Destroyed
@veryCrunchy
veryCrunchy force-pushed the fix/durable-upload-scheduling-recovery-stack branch from abcbb66 to 7b2e575 Compare September 3, 2026 21:43
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #439 / NC Native September 3, 2026 21:43 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: 7b2e575cd6

ℹ️ 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".

@veryCrunchy
veryCrunchy force-pushed the fix/durable-upload-scheduling-recovery-stack branch from 7b2e575 to a86e5ec Compare September 4, 2026 00:39
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #439 / NC Native September 4, 2026 00:39 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: a86e5ec1fa

ℹ️ 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".

@veryCrunchy
veryCrunchy force-pushed the fix/durable-upload-scheduling-recovery-stack branch from a86e5ec to 3ed84f8 Compare September 4, 2026 01:10
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #439 / NC Native September 4, 2026 01:10 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: 81e33dc68b

ℹ️ 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".

} catch (cancelled: CancellationException) {
throw cancelled
} catch (_: Exception) {
return@synchronized retainCapabilityCleanup(file.selectionId)

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 Quarantine cleanup when the capability snapshot is oversized

When persisted capability preferences contain more than MAX_RECOVERABLE_CAPABILITIES and any terminal upload still has cleanup pending, the bounded recovery scan marks the oversized store as quarantined, but that job's subsequent picker.release() calls the unbounded loadCapabilitySnapshot(), whose size check throws into this catch. Returning retainCapabilityCleanup makes reconcileQueuedUploads() fail, so the startup monitor reloads the same oversized store every 60 seconds indefinitely instead of honoring the quarantine; classify this overflow as quarantined in the terminal release path so the job marker can be completed without polling.

AGENTS.md reference: AGENTS.md:L325-L329

Useful? React with 👍 / 👎.

@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: 261a84cd0d

ℹ️ 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 +103 to +104
!durableUploadCapabilityPermissionOwnedByAnother(
capabilities = existing,

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 Block selections that duplicate malformed URI capabilities

When a malformed capability retains a valid URI and an app-owned grant, selecting that same document again is allowed because this duplicate check examines only snapshot.capabilities, not snapshot.malformedCapabilities. The new record then observes the existing grant as pre-existing; cleanup of the malformed record retains it because of the new peer, while cleanup of the new record retains it because of the malformed peer, leaving terminal cleanup pending and statuses undismissable indefinitely. Reject only malformed peers with the same cleanup URI, or resolve them before acquiring the new capability.

AGENTS.md reference: AGENTS.md:L457-L459

Useful? React with 👍 / 👎.

@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: bcae385d19

ℹ️ 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 +150 to +152
if (immediatePending) {
immediatePending = false
recover()

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 Preserve failed-job backoff during immediate recovery

When a worker has just failed and its 60-second follow-up is pending, any immediate signal reaches this branch and invokes the global reconciler, which re-enqueues the same now-unowned queued job along with unrelated work. Fresh evidence beyond the earlier backoff comments is that opening or refreshing a card calls durableMultipartUploadStatuses, whose unconditional queued-status signal interrupts this delay; another enqueue-recovery signal has the same effect. Persistent worker failures can therefore churn WorkManager and storage at the UI refresh cadence, so immediate recovery should exclude jobs whose failure deadline has not elapsed.

AGENTS.md reference: AGENTS.md:L25-L26

Useful? React with 👍 / 👎.

@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: 1caca52102

ℹ️ 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".

)

private fun storedCapabilitySelectionIds(maximumRows: Int? = null): List<String> {
val selectionIds = preferences.all.keys.asSequence()

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 Bound capability storage before calling getAll

When a legacy or corrupted capability preference file contains many or very large upload_ entries, preferences.all materializes the complete key/value map before the sequence applies take(limit + 1), so startup recovery can still exhaust memory or stall before detecting and quarantining the oversized store. Fresh evidence beyond the earlier bounded-recovery comment is that the final scanner limits its returned IDs but this newly introduced storage read remains unbounded; enumerate capabilities through bounded storage or enforce a bound before loading all preference values.

AGENTS.md reference: AGENTS.md:L325-L326

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.

1 participant