Skip to content

fix(sync): defer category pulls while local edits are pending - #555

Open
Autsunset wants to merge 1 commit into
TNT-Likely:mainfrom
Autsunset:fix/defer-pending-category-replay
Open

Autsunset wants to merge 1 commit into
TNT-Likely:mainfrom
Autsunset:fix/defer-pending-category-replay

Conversation

@Autsunset

Copy link
Copy Markdown

What & Why

Protect unpushed local category changes from incremental pull and historical
replay, including server reconciliation events returned with
updated_by_device_id: null.

The race is:

  1. The phone uploads category icon A.
  2. While the subsequent pull request is in flight, the user changes it to B.
  3. Pull returns the previously committed A. Applying it overwrites B even though
    B still has an unpushed local_changes record.
  4. A later push serializes the overwritten row rather than the user's B.

This is companion hardening for TNT-Likely/BeeCount-Cloud#96. It does not
replace that PR's historical icon backfill and category reconciliation.

Changes

  • Preflight applicable category events inside the page transaction, before any
    writes or custom-icon cleanup. Defer the entire page if an affected category
    has an unpushed local change.
  • Include children affected by a parent-category delete. Preserve local
    deletions as well as creations and edits.
  • Treat deferral as temporary, not a persisted sync error. Keep the page's
    retry cursor; a replay starting behind the saved cursor also persists its
    earlier retry point so the next ordinary pull cannot skip the deferred page.
  • Commit category creation, editing, and icon changes atomically with their
    outbox records, closing the gap between updating the row and recording the
    pending change.
  • Preserve existing own-device skips and no-payload skips. Already-pushed
    changes and pending changes to unrelated entities do not block reconciliation.

No database migration, server API change, or UI change is required. Pending
changes remain available to the existing push path; after push acknowledges
them, the same pull page can be retried.

Related Issue

Related: TNT-Likely/BeeCount-Cloud#96.

Self-tested

Toolchain: Flutter 3.27.3, Dart 3.6.1, matching the repository's CI.

  • flutter test --no-pub: 1021 passed, 1 skipped.
  • Added 17 regression/control tests. Against the original production
    code, 13 failed / 4 passed; with this fix, 17 passed.
  • Scoped flutter analyze: no new diagnostics. The baseline's existing
    1 warning and 3 info diagnostics are unchanged; --no-fatal-warnings
    and --no-fatal-infos were used without suppressing errors.
  • dart format on changed implementation/test files and git diff --check.
  • Real-device verification: not performed.
  • Localization updates: not applicable; no user-visible copy changes.

The tests cover an edit made while HTTP is in flight, retry after push, local
and remote deletion, parent-delete cascades, page-level atomicity, pagination,
replay cursor recovery, outbox-write rollback, and existing skip behavior.
The fake auth now exposes its device ID in user metadata like the real provider,
and the existing parent-link test acknowledges its seeded local changes before
testing a remote update.

Contributor License Terms

Preflight incoming category changes and cascading child deletes inside the page transaction. Defer without recording a sync error or advancing past the retry cursor, including during historical replay.

Commit category creations and edits atomically with their outbox records. Cover HTTP-time edits, deletion, pagination, replay recovery, atomic rollback, and existing own-device skip behavior.

Refs TNT-Likely/BeeCount-Cloud#96
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