-
-
Save WilfredAlmeida/9adea27abb5958178c4370c5656e89b7 to your computer and use it in GitHub Desktop.
// This is a custom fetch function that uses axios to make requests and retries on errors or 502 status codes | |
// Run this file by executing `npx tsx axiosFetchWithRetries.ts` | |
import { Connection } from "@solana/web3.js"; | |
import axios from "axios"; | |
const RETRY_ATTEMPTS = 3; | |
const agent = new https.Agent({ | |
maxSockets: 100, | |
}); | |
const axiosObject = axios.create({ | |
httpsAgent: agent, | |
}); | |
export async function axiosFetchWithRetries( | |
input: string | URL | globalThis.Request, | |
init?: RequestInit, | |
retryAttempts = 0 | |
): Promise<Response> { | |
let attempt = 0; | |
// Adding default headers | |
if (!init || !init.headers) { | |
init = { | |
headers: { | |
"Content-Type": "application/json", | |
}, | |
...init, | |
}; | |
} | |
while (attempt < retryAttempts) { | |
try { | |
let axiosHeaders = {}; | |
axiosHeaders = Array.from(new Headers(init.headers).entries()).reduce( | |
(acc, [key, value]) => { | |
acc[key] = value; | |
return acc; | |
}, | |
{} | |
); | |
const axiosConfig = { | |
data: init.body, | |
headers: axiosHeaders, | |
method: init.method, | |
baseURL: input.toString(), | |
validateStatus: (status) => true, | |
}; | |
const axiosResponse = await axiosObject.request(axiosConfig); | |
const { data, status, statusText, headers } = axiosResponse; | |
// Mapping headers from axios to fetch format | |
const headersArray: [string, string][] = Object.entries(headers).map( | |
([key, value]) => [key, value] | |
); | |
const fetchHeaders = new Headers(headersArray); | |
const response = new Response(JSON.stringify(data), { | |
status, | |
statusText, | |
headers: fetchHeaders, | |
}); | |
// Comment the above lines and uncomment the following one to switch from axios to fetch | |
// const response = await fetch(input, init); | |
// Traffic might get routed to backups or node restarts or if anything throws a 502, retry | |
if (response.status === 502) { | |
console.log("Retrying due to 502"); | |
attempt++; | |
// Backoff to avoid hammering the server | |
await new Promise<void>((resolve) => | |
setTimeout(resolve, 100 * attempt) | |
); | |
continue; | |
} | |
return Promise.resolve(response); | |
} catch (e) { | |
console.log(`Retrying due to error ${e}`, e); | |
attempt++; | |
continue; | |
} | |
} | |
return Promise.reject("Max retries reached"); | |
} | |
async function main() { | |
const connection = new Connection("https://api.devnet.solana.com", { | |
async fetch(input, init?) { | |
console.log( | |
"Custom fetch function", | |
input, | |
init.method, | |
init.body, | |
init.headers | |
); | |
return await axiosFetchWithRetries(input, init, RETRY_ATTEMPTS); | |
}, | |
}); | |
const blockhash = await connection.getLatestBlockhash(); | |
console.log("LATEST BLOCKHASH"); | |
console.log(blockhash); | |
} | |
main(); |
I don't receive the error like this which does not include the cause:
cause: ConnectTimeoutError: Connect Timeout Error
at onConnectTimeout (node:internal/deps/undici/undici:6869:28)
at node:internal/deps/undici/undici:6825:50
at Immediate._onImmediate (node:internal/deps/undici/undici:6857:13)
at process.processImmediate (node:internal/timers:478:21) {
code: 'UND_ERR_CONNECT_TIMEOUT'
}
i only receive only like below:
TypeError: fetch failed
at node:internal/deps/undici/undici:12606:11
at processTicksAndRejections (node:internal/process/task_queues:95:5)
at runNextTicks (node:internal/process/task_queues:64:3)
at processImmediate (node:internal/timers:454:9)
at ClientBrowser.callServer (/var/www/prod/soltrackers/server/node_modules/@solana/web3.js/src/connection.ts:1675:17)
the above error give me a this at ClientBrowser.callServer (/var/www/prod/soltrackers/server/node_modules/@solana/web3.js/src/connection.ts:1675:17)
and when i fave this error :TypeError: fetch failed
the my script execution stopped. I handle the proper all errors but i don't know from where it occurs. so can you suggest me more about that?
Thank you @WilfredAlmeida for giving me a solution. the one last question is that in the Documentation you metnion that we need to Configure the undici
import { setGlobalDispatcher, Agent } from "undici";
setGlobalDispatcher(
new Agent({
connections: 50,
})
);
but in above code you didn't mention so is that above script working without the undici package or i need to Configure it?
Glad it worked. The above script is setting agent and number of connection already
const agent = new https.Agent({
maxSockets: 100,
});
const axiosObject = axios.create({
httpsAgent: agent,
});
Thank you @WilfredAlmeida for giving me a solution and i like the way you explain each and every things in a simple words in documentation.
The Custom function which you write for the RPC connection. are you using it right now or while back?