Skip to content

[BUG] Concurrent Client Requests Can Skip Healthy RPC Fallback #445

Description

@phroi

Hello folks at CKB DevRel,

I noticed that concurrent CCC client requests can change which RPC endpoint each other retries. A request may retry a failed endpoint and reject without trying a healthy fallback.

Minimal Reproduction

import { ccc } from "@ckb-ccc/core";

globalThis.fetch = async (url, { body }) => {
  const { id } = JSON.parse(body);
  const endpoint = url === "https://a.invalid/" ? "A" : "B";
  console.log(`${id + 1} -> ${endpoint}`);

  if (endpoint === "A") {
    throw new Error("A unavailable");
  }
  return Response.json({ jsonrpc: "2.0", id, result: "0x1" });
};

const client = new ccc.ClientPublicTestnet({
  url: "https://a.invalid/",
  fallbacks: ["https://b.invalid/"],
  timeout: 10,
});
const results = await Promise.allSettled([
  client.getTip(),
  client.getTip(),
]);

console.log(results.map(({ status }) => status));

And to run it:

node repro.mjs

Behavior

Endpoint A is unavailable and endpoint B is healthy. Both requests should try A, then B, and succeed. Instead:

1 -> A
2 -> A
1 -> B
2 -> A
[ 'fulfilled', 'rejected' ]

Request 1 correctly retries B and succeeds. Request 2 retries failed endpoint A instead. It uses both of its attempts on A and rejects without ever trying healthy endpoint B.

This happens because concurrent requests advance the same endpoint index. One request can therefore change which endpoint another request retries.

Environment

  • @ckb-ccc/core@1.17.0
  • Node.js 24.18.0
  • Linux

Impact

The bug turns requests that could succeed through the healthy fallback into errors. It does not corrupt a successful response; the request never reaches the healthy endpoint because its retry is routed back to the failed endpoint.

Keep up the Great Work
Phroi %53

Metadata

Metadata

Labels

bugSomething isn't workinggood first issueGood for newcomers

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions