Skip to content

fix(sync): honor database restore verification cooldowns - #607

Closed
appflowy wants to merge 1 commit into
mainfrom
fix/database-restore-busy-retry
Closed

appflowy wants to merge 1 commit into
mainfrom
fix/database-restore-busy-retry

Conversation

@appflowy

@appflowy appflowy commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

After restore-state verification returned a transient error, later open, sync, focus, and reconnect checks could bypass the retry timer and immediately request verification again. The tracker now shares one retry deadline per database, honors Retry-After, and uses a 30-second fallback for busy responses. Callers remain fenced until fresh verification succeeds; permission denials remain terminal and queued edits are preserved.

Validation: 71 focused tracker/hook tests pass, including 32 concurrent and staggered callers, restore hints during a busy request, retained local edits, and permanent denial handling. Four regression cases failed before the fix. Full TypeScript checking, production ESLint, and git diff --check pass.

Related server work: https://github.com/AppFlowy-IO/AppFlowy-Cloud-Premium/pull/1336. This Web fix is independent and can be reviewed separately.

Summary by Sourcery

Prevent database restore checks from bypassing transient verification cooldowns while preserving terminal denial handling and unsent local changes.

Bug Fixes:

  • Honor a shared per-database retry deadline after transient restore-verification failures so open, sync, focus, and reconnect checks remain fenced until verification can be retried.
  • Preserve permanent permission denials as terminal errors while retaining local edits and preventing restore cleanup during deferred verification.

Enhancements:

  • Centralize restore-verification error classification and support server Retry-After hints with safe fallback delays and bounded retry scheduling.

Tests:

  • Add focused coverage for concurrent and staggered callers, busy verification hints, oversized retry delays, permission denials, and preservation of local edits.

@sourcery-ai

sourcery-ai Bot commented Oct 6, 2026

Copy link
Copy Markdown

Reviewer's Guide

The PR prevents restore-state verification stampedes by sharing a per-database retry deadline across all sync callers, honoring Retry-After with safe busy-response fallbacks, and keeping databases fenced until fresh verification succeeds. Permanent permission failures remain terminal, while hints and local edits are preserved during cooldowns; focused tracker and hook regressions cover concurrent lifecycle paths and recovery behavior.

Sequence diagram for shared database restore verification cooldown

sequenceDiagram
    participant Caller as Sync callers
    participant Tracker as DatabaseRestoreTracker
    participant Server as Restore state service
    participant Hook as Restore sync hook

    Caller->>Tracker: check(databaseId)
    Tracker->>Server: readState(databaseId)
    Server-->>Tracker: transient error with Retry-After
    Tracker->>Tracker: store deferred retryAtMs
    Tracker-->>Caller: DatabaseRestoreVerificationDeferredError
    Caller->>Hook: schedule retry
    Hook->>Tracker: check(databaseId)
    Tracker-->>Hook: reject until retryAtMs
    Note over Caller,Tracker: open, sync, focus, and reconnect callers share one deadline
    Tracker->>Server: readState(databaseId)
    Server-->>Tracker: verified restore state
    Tracker-->>Caller: fresh verification succeeds
Loading

Flow diagram for restore verification error handling

flowchart TD
    A[Restore verification fails] --> B{Permanent denial?}
    B -->|Yes| C[Keep database fenced]
    C --> D[Stop retrying]
    B -->|No| E{Retryable busy or transient error?}
    E -->|No| F[Propagate verification error]
    E -->|Yes| G[Use Retry-After or fallback delay]
    G --> H[Store per-database retry deadline]
    H --> I[Reject callers during cooldown]
    I --> J[Retry fresh verification]
    J --> K{Verification succeeds?}
    K -->|Yes| L[Release fence]
    K -->|No| G
Loading

File-Level Changes

Change Details Files
Centralize transient restore-verification retry state per database and preserve terminal permission failures.
  • Classify permanent and retryable verification errors in one shared helper.
  • Store a bounded shared retry deadline and return the same deferred error to callers until it expires.
  • Honor server Retry-After values with safe fallback and overflow handling; retry 429/busy responses after 30 seconds when no hint is provided.
  • Keep restore hints fenced until a fresh post-deadline authority read succeeds.
src/components/ws/sync/databaseRestoreState.ts
Make sync lifecycle callers use the tracker’s shared verification cooldown without disrupting local data or queued edits.
  • Reuse centralized permanent-error classification and schedule retries from the shared deadline.
  • Cap browser timer delays while preserving long retry deadlines.
  • Continue fencing open, send, focus, and reconnect paths during cooldown and admit work only after successful verification.
src/components/ws/sync/useDatabaseHistoryRestoreSync.ts
Add regression coverage for cooldown sharing, error classification, hint handling, and edit preservation.
  • Test concurrent and staggered callers, Retry-After propagation, busy-response fallbacks, and extreme finite delays.
  • Verify permission denials remain terminal and are retried independently rather than cached as transient failures.
  • Verify hints do not bypass failed verification and local edits, documents, and outbox state remain intact during deferred retries.
  • Cover hook behavior across opens, sends, focus, online, and delayed recovery events.
src/components/ws/sync/__tests__/databaseRestoreState.test.ts
src/components/ws/sync/__tests__/useDatabaseHistoryRestoreSync.test.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@appflowy appflowy closed this Oct 7, 2026
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