Skip to content

Fix concurrent RPC fallback skipping healthy endpoints#448

Merged
Hanssen0 merged 1 commit into
devfrom
copilot/fix-concurrent-client-requests
Jul 20, 2026
Merged

Fix concurrent RPC fallback skipping healthy endpoints#448
Hanssen0 merged 1 commit into
devfrom
copilot/fix-concurrent-client-requests

Conversation

Copilot AI commented Jul 20, 2026

Copy link
Copy Markdown

Concurrent requests sharing the same TransportFallback instance could route retries back to a failed endpoint instead of advancing to a healthy fallback, causing requests to reject without ever reaching a working RPC.

Root cause: TransportFallback.request() read this.i on every retry iteration. Two concurrent requests both failing on transport A would each increment this.i, causing one of them to wrap back to A on its retry instead of advancing to B.

Fix (packages/core/src/jsonRpc/transports/fallback.ts):

  • Snapshot this.i as startI at the start of each request() call
  • Use (startI + tried) % length to iterate transports — isolated per-call, unaffected by concurrent mutations
  • this.i is still incremented on failure so future requests skip known-bad transports
// Before: reads shared this.i on every iteration
return await this.transports[this.i % this.transports.length].request(data);

// After: each request iterates from its own snapshot
const startI = this.i;
return await this.transports[(startI + tried) % this.transports.length].request(data);

Tests (packages/core/src/jsonRpc/transports/fallback.test.ts):

  • Added fallback.test.ts covering single-transport success, sequential fallback, all-fail throw, concurrent requests both succeeding via fallback, and the "skip known-bad" advancement behaviour.

@netlify

netlify Bot commented Jul 20, 2026

Copy link
Copy Markdown

Deploy Preview for docsccc ready!

Name Link
🔨 Latest commit 7db4875
🔍 Latest deploy log https://app.netlify.com/projects/docsccc/deploys/6a5e6f9807c68e000935b76c
😎 Deploy Preview https://deploy-preview-448--docsccc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 68 (🔴 down 19 from production)
Accessibility: 95 (no change from production)
Best Practices: 92 (🔴 down 8 from production)
SEO: 75 (no change from production)
PWA: -
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Jul 20, 2026

Copy link
Copy Markdown

Deploy Preview for appccc ready!

Name Link
🔨 Latest commit 7db4875
🔍 Latest deploy log https://app.netlify.com/projects/appccc/deploys/6a5e6f980c75ac00085c8ab6
😎 Deploy Preview https://deploy-preview-448--appccc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 67 (🔴 down 16 from production)
Accessibility: 89 (🟢 up 1 from production)
Best Practices: 92 (🔴 down 8 from production)
SEO: 100 (no change from production)
PWA: -
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Jul 20, 2026

Copy link
Copy Markdown

Deploy Preview for liveccc ready!

Name Link
🔨 Latest commit 7db4875
🔍 Latest deploy log https://app.netlify.com/projects/liveccc/deploys/6a5e6f98da95c40008861a6b
😎 Deploy Preview https://deploy-preview-448--liveccc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 40 (🟢 up 5 from production)
Accessibility: 88 (no change from production)
Best Practices: 92 (🔴 down 8 from production)
SEO: 100 (no change from production)
PWA: -
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@changeset-bot

changeset-bot Bot commented Jul 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 7db4875

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 20 packages
Name Type
@ckb-ccc/core Patch
@ckb-ccc/did-ckb Patch
@ckb-ccc/eip6963 Patch
@ckb-ccc/joy-id Patch
@ckb-ccc/lumos-patches Patch
@ckb-ccc/nip07 Patch
@ckb-ccc/okx Patch
@ckb-ccc/rei Patch
@ckb-ccc/shell Patch
@ckb-ccc/spore Patch
@ckb-ccc/ssri Patch
@ckb-ccc/type-id Patch
@ckb-ccc/udt Patch
@ckb-ccc/uni-sat Patch
@ckb-ccc/utxo-global Patch
@ckb-ccc/xverse Patch
@ckb-ccc/ccc Patch
ckb-ccc Patch
@ckb-ccc/connector Patch
@ckb-ccc/connector-react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@netlify

netlify Bot commented Jul 20, 2026

Copy link
Copy Markdown

Deploy Preview for apiccc ready!

Name Link
🔨 Latest commit 7db4875
🔍 Latest deploy log https://app.netlify.com/projects/apiccc/deploys/6a5e6f98343e8c0008e83c46
😎 Deploy Preview https://deploy-preview-448--apiccc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 94 (🟢 up 5 from production)
Accessibility: 100 (no change from production)
Best Practices: 100 (no change from production)
SEO: 95 (no change from production)
PWA: -
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

Copilot AI changed the title [WIP] Fix concurrent client requests skipping healthy RPC fallback Fix concurrent RPC fallback skipping healthy endpoints Jul 20, 2026
Copilot AI requested a review from Hanssen0 July 20, 2026 16:59
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: db80f29e-c6db-4fed-bae2-5185d4ca5e62

📥 Commits

Reviewing files that changed from the base of the PR and between 1b1ea8a and 7db4875.

📒 Files selected for processing (3)
  • .changeset/fiery-swans-end.md
  • packages/core/src/jsonRpc/transports/fallback.test.ts
  • packages/core/src/jsonRpc/transports/fallback.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/core/src/jsonRpc/transports/fallback.test.ts
  • packages/core/src/jsonRpc/transports/fallback.ts

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved RPC fallback handling for concurrent requests.
    • Requests now retry available transports reliably when one fails.
    • Errors are reported correctly when all transports are unavailable.
    • Empty transport configurations now produce a clear error instead of failing unexpectedly.

Walkthrough

TransportFallback now bounds retries from a request-local starting index and selects transports modularly. Tests cover healthy requests, sequential fallback, complete failure, concurrent fallback, and subsequent starting-index behavior.

Changes

Transport fallback correction

Layer / File(s) Summary
Bounded retry flow and behavioral validation
packages/core/src/jsonRpc/transports/fallback.ts, packages/core/src/jsonRpc/transports/fallback.test.ts, .changeset/fiery-swans-end.md
request attempts each transport at most once, records the final failure, updates the index only after success, and tests validate sequential, concurrent, and subsequent-request behavior. A patch changeset records the fix.

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

Suggested reviewers: hanssen0

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the bug, root cause, fix, and tests, but the required contributing-guidelines checkbox is left unchecked. Check the required contributing-guidelines box and keep the PR description template fields completed.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main fix: concurrent RPC fallback requests skipping healthy endpoints.
Linked Issues check ✅ Passed The changes address #445 by snapshotting the fallback start index per request, preserving shared failure advancement, and adding tests for concurrent success.
Out of Scope Changes check ✅ Passed The added test and changeset files support the fallback bug fix and stay within the issue scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


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.

🧹 Nitpick comments (1)
packages/core/src/jsonRpc/transports/fallback.ts (1)

18-18: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider wrapping this.i with modulo for bounded growth.

this.i advances by 1 on each failure but is never wrapped with % this.transports.length. While the modular arithmetic at line 15 keeps selection correct, this.i can grow well beyond the valid index range, making the // Current transport index comment on line 4 slightly misleading. Wrapping keeps the value bounded and self-consistent.

♻️ Optional refactor
-        this.i += 1;
+        this.i = (this.i + 1) % this.transports.length;
🤖 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 `@packages/core/src/jsonRpc/transports/fallback.ts` at line 18, Update the
failure-advance logic around this.i so it wraps with modulo
this.transports.length after incrementing. Keep transport selection behavior
unchanged while ensuring this.i remains bounded to the valid transport index
range and stays consistent with its “Current transport index” role.
🤖 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.

Nitpick comments:
In `@packages/core/src/jsonRpc/transports/fallback.ts`:
- Line 18: Update the failure-advance logic around this.i so it wraps with
modulo this.transports.length after incrementing. Keep transport selection
behavior unchanged while ensuring this.i remains bounded to the valid transport
index range and stays consistent with its “Current transport index” role.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d90d3fa6-b71b-4a0b-bbe5-f0786f66eab2

📥 Commits

Reviewing files that changed from the base of the PR and between 6d5d629 and fa0509f.

📒 Files selected for processing (2)
  • packages/core/src/jsonRpc/transports/fallback.test.ts
  • packages/core/src/jsonRpc/transports/fallback.ts

@Hanssen0

Copy link
Copy Markdown
Member

@copilot assign to this.i only when we find a working endpoint. Also make sure it's always bounded in transports.length. Would recording errors & throwing at the end better? Since that eliminates the last iteration check & unreachable code.

Copilot AI commented Jul 20, 2026

Copy link
Copy Markdown
Author

@copilot assign to this.i only when we find a working endpoint. Also make sure it's always bounded in transports.length. Would recording errors & throwing at the end better? Since that eliminates ...

Done in the latest commit. this.i is now set to (startI + tried) % this.transports.length only when a transport succeeds. Errors are collected into an array and the last one is thrown after the loop, removing both the mid-loop check and the unreachable throw.

@Hanssen0

Copy link
Copy Markdown
Member

OMG

@Hanssen0
Hanssen0 marked this pull request as ready for review July 20, 2026 18:29
Copilot AI review requested due to automatic review settings July 20, 2026 18:29
@Hanssen0

Copy link
Copy Markdown
Member

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

🤖 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 `@packages/core/src/jsonRpc/transports/fallback.ts`:
- Around line 10-26: Validate that the fallback transport list is non-empty
before entering the retry loop in the request method containing startI and
lastErr. If no transports are configured, throw an actionable Error instead of
allowing the final throw lastErr to throw undefined; preserve the existing retry
and error propagation behavior for non-empty configurations.
🪄 Autofix (Beta)

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ee3aece3-785a-4b34-b0ad-3757476234eb

📥 Commits

Reviewing files that changed from the base of the PR and between fa0509f and 20aba2e.

📒 Files selected for processing (1)
  • packages/core/src/jsonRpc/transports/fallback.ts

Comment thread packages/core/src/jsonRpc/transports/fallback.ts

Copilot AI 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.

Pull request overview

This PR fixes a concurrency bug in TransportFallback where concurrent requests sharing the same instance could retry a failed transport again (wrapping the shared index) instead of advancing to a healthy fallback endpoint, leading to avoidable request failures.

Changes:

  • Refactors TransportFallback.request() to snapshot the starting index per call and iterate deterministically over transports.
  • Adds a Vitest suite covering success, sequential fallback, all-fail behavior, concurrent fallback behavior, and “skip known-bad” behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
packages/core/src/jsonRpc/transports/fallback.ts Updates fallback iteration logic to be per-request (concurrency-safe).
packages/core/src/jsonRpc/transports/fallback.test.ts Adds regression tests for sequential and concurrent fallback behavior.

Comment thread packages/core/src/jsonRpc/transports/fallback.ts
@Hanssen0

Copy link
Copy Markdown
Member

@coderabbitai rate limit

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Your plan includes PR reviews subject to rate limits. Reviews are available now.

@Hanssen0
Hanssen0 requested a review from Copilot July 20, 2026 18:48

Copilot AI 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.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

packages/core/src/jsonRpc/transports/fallback.ts:25

  • this.i is no longer advanced on transport failure. That means requests that start after a failure (but before a later success in another in-flight request) will still snapshot the old index and hit the known-bad transport first, which also contradicts the PR description that this.i is incremented on failure to help future requests skip failing endpoints. Advancing the shared index in the catch block is safe now that retry selection is based on the per-call startI snapshot.
        this.i = i;
        return res;
      } catch (err) {
        lastErr = err;
      }

packages/core/src/jsonRpc/transports/fallback.ts:13

  • The new explicit error path for transports.length === 0 (throwing "TransportFallback requires at least one transport") isn’t covered by tests yet. Adding a small unit test would prevent regressions back to a less-informative runtime error (e.g., trying to call .request on undefined).
    const startI = this.i;
    let lastErr: unknown = new Error(
      "TransportFallback requires at least one transport",
    );

@Hanssen0
Hanssen0 changed the base branch from master to dev July 20, 2026 18:54
@Hanssen0
Hanssen0 force-pushed the copilot/fix-concurrent-client-requests branch from 1b1ea8a to 90f533b Compare July 20, 2026 18:56
@Hanssen0
Hanssen0 force-pushed the copilot/fix-concurrent-client-requests branch from 90f533b to 7db4875 Compare July 20, 2026 18:57
@Hanssen0
Hanssen0 merged commit 8c148af into dev Jul 20, 2026
19 checks passed
@Hanssen0
Hanssen0 deleted the copilot/fix-concurrent-client-requests branch July 20, 2026 19:03
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.

[BUG] Concurrent Client Requests Can Skip Healthy RPC Fallback

3 participants