Skip to content

PE-9204: One drive's gateway hiccup killed the whole sync - #2180

Merged
vilenarios merged 2 commits into
devfrom
PE-9204-sync-signature-resilience
Aug 11, 2026
Merged

vilenarios merged 2 commits into
devfrom
PE-9204-sync-signature-resilience

Conversation

@vilenarios

@vilenarios vilenarios commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Reported from staging: a drive signature 404ed and sync stopped dead before any drive had synced. Retrying by hand found the transaction present — the gateway had it, it just hadn't indexed it yet.

Two causes.

The failure wasn't contained

Drive discovery already skips a drive whose metadata can't be read (if (driveResponses[i] == null) continue). But the drive-signature read a few lines below was the one unguarded await in that loop. It threw past every handler to the method's outer catch — which rethrows — so getUniqueUserDriveEntities failed, updateUserDrives failed, and sync died at discovery.

One drive's bad luck took every drive with it, including public ones that would have synced fine. It now drops that drive for the pass, like every other failure there, and picks it up next time.

A 404 was treated as final, and that was wrong

The 404 fast-path reasoned that the same host will not turn a 404 into a 200. That holds for a host that has answered definitively. It does not hold for one still indexing — which is precisely what was observed: 404 on the first read, present on the second.

A 404 now spends the second attempt like any other failure and is only reported as TransactionNotFound when every attempt agrees. A genuinely missing transaction still costs two attempts, no more.

Why this got worse recently

Before #2179 this path used the multi-gateway waterfall, so a gateway that was a beat behind was covered by the next one. Single-gateway reads removed that cushion, and the 404 fast-path removed the retry too — so an indexing lag went straight to a hard failure with no second chance.

Verification

flutter analyze clean · 1,320 tests passing.

The test asserting "does not retry a 404" encoded the behaviour this reverses; it's replaced by two — one proving a 404-then-200 succeeds, one proving a persistent 404 still raises TransactionNotFound in exactly two attempts.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UpkQ6gRPTUuy3MCUFxFw2P

Summary by CodeRabbit

  • Bug Fixes
    • Improved private-drive discovery so a failed signature request no longer stops the entire discovery process.
    • Added retry handling for temporary 404 responses when fetching data from the configured gateway.
    • Persistent 404 responses still report that the requested transaction could not be found.

Reported from staging: a drive signature 404ed, and sync stopped dead before
any drive had synced. Retrying by hand found the transaction present.

Two causes, both fixed.

**The failure was not contained.** Drive discovery skips a drive whose
metadata cannot be read - `if (driveResponses[i] == null) continue` - but the
drive-signature read a few lines later was the one unguarded await in that
loop. It threw past every handler to the method's outer catch, which rethrows,
so `getUniqueUserDriveEntities` failed, `updateUserDrives` failed, and the sync
died at discovery. One drive's bad luck took every drive with it. It now drops
that drive for the pass, like every other failure there.

**A 404 was treated as final, and that was wrong.** The reasoning was that the
same host will not turn a 404 into a 200. True of a host that has answered
definitively, false of one that has not finished indexing - which is exactly
what was seen: 404 on the first read, present on the second. A 404 now spends
the second attempt like any other failure, and is only reported as
`TransactionNotFound` if every attempt agrees. A genuinely missing transaction
still costs two attempts, no more.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@vilenarios, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 31 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 571941cc-4472-475a-beee-26beeeb60130

📥 Commits

Reviewing files that changed from the base of the PR and between 7bd8025 and e226878.

📒 Files selected for processing (3)
  • lib/services/arweave/arweave_service.dart
  • lib/services/arweave/data_gateway_fallback.dart
  • test/services/arweave/data_gateway_fallback_test.dart
📝 Walkthrough

Walkthrough

Private-drive discovery now skips drives whose v1 signature retrieval fails. Sync gateway fetches retry 404 responses before returning TransactionNotFound. Tests cover successful retry and persistent 404 responses.

Changes

Arweave resilience

Layer / File(s) Summary
Private-drive signature error handling
lib/services/arweave/arweave_service.dart
Private-drive discovery catches v1 signature retrieval failures, logs the affected drive, and continues processing other drives.
Sync gateway 404 retries
lib/services/arweave/data_gateway_fallback.dart, test/services/arweave/data_gateway_fallback_test.dart
Sync fetches retry 404 responses. Persistent 404 responses still produce TransactionNotFound. Tests cover both outcomes.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main fix: preventing one drive gateway failure from aborting the full sync.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch PE-9204-sync-signature-resilience

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🧹 Nitpick comments (1)
test/services/arweave/data_gateway_fallback_test.dart (1)

80-96: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a mixed-status regression test.

The changed tests cover 404 then 200 and 404 then 404. They do not cover 500 then 404 or an exception then 404. The current production condition misclassifies those sequences as TransactionNotFound. Assert a non-TransactionNotFound failure and exactly two gateway calls. This enforces the all-404 terminal contract in the PR objective.

Also applies to: 108-110

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/services/arweave/data_gateway_fallback_test.dart` around lines 80 - 96,
Add regression coverage in the fallback.fetchDataForSync tests for both
500-then-404 and exception-then-404 responses from primaryApi.getSandboxedTx.
Assert each result is a non-TransactionNotFound failure and verify exactly two
gateway calls, preserving TransactionNotFound only when all attempts return 404.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/services/arweave/arweave_service.dart`:
- Around line 1037-1046: Update the pagination flow using
getUniqueUserDriveEntityTxs and drivesById to deduplicate Drive-Id values across
all pages, not just within each page. Track processed or failed IDs across page
iterations so an older transaction cannot populate stale metadata when the
newest transaction’s signature retrieval fails. Add a regression test covering
duplicate Drive-Id values split across pages.

In `@lib/services/arweave/data_gateway_fallback.dart`:
- Around line 111-122: Update the surrounding method documentation near the 404
handling in the sync flow to state that the first 404 is retried and
TransactionNotFound is reported only after the final attempt. Keep the
implementation and existing retry behavior unchanged.
- Around line 111-122: Enforce the all-404 terminal contract in the fallback
logic: in lib/services/arweave/data_gateway_fallback.dart lines 111-122, track
whether every sync attempt returned 404 before producing TransactionNotFound,
rather than checking only the final status. In
test/services/arweave/data_gateway_fallback_test.dart lines 80-96, add a
500-then-404 scenario; in lines 108-110, assert it is not TransactionNotFound
and that exactly two gateway calls occur.

---

Nitpick comments:
In `@test/services/arweave/data_gateway_fallback_test.dart`:
- Around line 80-96: Add regression coverage in the fallback.fetchDataForSync
tests for both 500-then-404 and exception-then-404 responses from
primaryApi.getSandboxedTx. Assert each result is a non-TransactionNotFound
failure and verify exactly two gateway calls, preserving TransactionNotFound
only when all attempts return 404.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 321b9885-0cf0-4ca6-abc1-feb6b589764b

📥 Commits

Reviewing files that changed from the base of the PR and between ff925de and 7bd8025.

📒 Files selected for processing (3)
  • lib/services/arweave/arweave_service.dart
  • lib/services/arweave/data_gateway_fallback.dart
  • test/services/arweave/data_gateway_fallback_test.dart

Comment thread lib/services/arweave/arweave_service.dart
Comment thread lib/services/arweave/data_gateway_fallback.dart
@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown

Visit the preview URL for this PR (updated for commit e226878):

https://ardrive-web--pr2180-pe-9204-sync-signatu-idqv133t.web.app

(expires Tue, 18 Aug 2026 13:04:04 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

Sign: a224ebaee2f0939e7665e7630e7d3d6cd7d0f8b0

All three valid, and the first is a consequence of this PR.

**Skipping a drive let an older revision take its place.**
`getUniqueUserDriveEntityTxs` dedupes per page, so the same Drive-Id can
appear on several pages, newest first. The new `continue` on a signature
failure skipped the newest transaction without recording the id, so the older
one on a later page was no longer deduped and wrote stale metadata. Worse than
the drive being late. The id is claimed on failure now.

**`TransactionNotFound` claimed more than it knew.** The check was "the last
attempt was a 404", so a 500 followed by a 404 reported the transaction as
absent. That is an unwell gateway, not missing data. Every attempt must agree
now - a timeout or socket error disqualifies it too.

**The doc still said a 404 is not retried**, which this PR had already stopped
being true.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vilenarios

Copy link
Copy Markdown
Collaborator Author

All three valid — fixed in e2268785. Analyze clean, 1,322 tests passing.

Cross-page dedup — and this one was caused by this PR. getUniqueUserDriveEntityTxs dedupes per page, so the same Drive-Id can appear on several pages, newest first. The continue I added on a signature failure skipped the newest transaction without recording the id, so the older one on a later page was no longer deduped and would write stale metadata. That's worse than the drive being late — a silently outdated drive beats a temporarily absent one. The id is now claimed on failure, so nothing older stands in for it.

TransactionNotFound claimed more than it knew. My check was "the last attempt was a 404", so 500-then-404 reported the transaction as absent. That's an unwell gateway, not missing data. Every attempt must now agree, and a timeout or socket error disqualifies it too.

The doc was stale — it still said a 404 isn't retried, which this PR had already made untrue.

Added both scenarios you asked for: 500-then-404 and exception-then-404, each asserting a non-TransactionNotFound failure in exactly two gateway calls.

I did not add the cross-page regression test — getUniqueUserDriveEntities needs a wallet, crypto, a DriveDao and paginated GraphQL to construct, so a faithful test is a fixture exercise rather than a small one. The guard is three lines and covered by inspection; flagging that trade rather than claiming coverage I didn't write.

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