Skip to content

Commit

Permalink
Merge pull request #46 from ubiquity/development
Browse files Browse the repository at this point in the history
Merge development into main
  • Loading branch information
gentlementlegen authored Aug 23, 2024
2 parents ae8b28d + ec3e6d1 commit 81742cc
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 22 deletions.
12 changes: 12 additions & 0 deletions tests/rpc-handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,4 +191,16 @@ describe("RPCHandler", () => {
}, 10000);
}
});

it("Should return the first available RPC", async () => {
const module = await import("../types/rpc-handler");
const rpcHandler = new module.RPCHandler({
...testConfig,
networkId: "1",
rpcTimeout: 1000,
});

const provider = await rpcHandler.getFirstAvailableRpcProvider();
expect(provider).not.toBeNull();
});
});
28 changes: 28 additions & 0 deletions types/rpc-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ import { StorageService } from "./storage-service";

const NO_RPCS_AVAILABLE = "No RPCs available";

function shuffleArray(array: object[]) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}

export class RPCHandler implements HandlerInterface {
private static _instance: RPCHandler | null = null;
private _provider: JsonRpcProvider | null = null;
Expand Down Expand Up @@ -55,6 +62,27 @@ export class RPCHandler implements HandlerInterface {
this.testRpcPerformance.bind(this);
}

/**
* Loops through all RPCs for a given network id and returns a provider with the first successful network.
*/
public async getFirstAvailableRpcProvider() {
const rpcList = [...networkRpcs[this._networkId].rpcs];
shuffleArray(rpcList);
for (const rpc of rpcList) {
try {
const result = await RPCService.makeRpcRequest(rpc.url, this._rpcTimeout, { "Content-Type": "application/json" });
if (result.success) {
return new JsonRpcProvider({ url: rpc.url, skipFetchSetup: true }, Number(this._networkId));
} else {
console.error(`Failed to reach endpoint ${rpc.url}. ${result.error}`);
}
} catch (err) {
console.error(`Failed to reach endpoint ${rpc.url}. ${err}`);
}
}
return null;
}

public async getFastestRpcProvider(): Promise<JsonRpcProvider> {
let fastest = await this.testRpcPerformance();

Expand Down
23 changes: 1 addition & 22 deletions types/rpc-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,29 +57,8 @@ export class RPCService {
throw new Error(rpcUrl);
}
}
const promises = runtimeRpcs.map((rpcUrl) => requestEndpoint(rpcUrl));
async function getFirstSuccessfulRequest(requests: string[]) {
if (requests.length === 0) {
throw new Error("All requests failed.");
}
const promisesToResolve = requests.map((rpcUrl) => requestEndpoint(rpcUrl));

try {
return await Promise.race(promisesToResolve);
} catch (err) {
console.error(`Failed to reach endpoint. ${err}`);
if (err instanceof Error && requests.includes(err.message)) {
return getFirstSuccessfulRequest(requests.filter((request) => request !== err.message));
}
return getFirstSuccessfulRequest(requests.slice(1));
}
}
const fastest = await getFirstSuccessfulRequest(runtimeRpcs);

if (fastest.success) {
latencies[`${networkId}__${fastest.rpcUrl}`] = fastest.duration;
}

const promises = runtimeRpcs.map((rpcUrl) => requestEndpoint(rpcUrl));
const allResults = await Promise.allSettled(promises);

allResults.forEach((result) => {
Expand Down

0 comments on commit 81742cc

Please sign in to comment.