Skip to content

Commit 519e56b

Browse files
network retries
1 parent 56cfce6 commit 519e56b

File tree

3 files changed

+58
-2
lines changed

3 files changed

+58
-2
lines changed

sdk/src/network-client.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { get, post, parseJSON, logAndThrow } from "./utils";
1+
import { get, post, parseJSON, logAndThrow, retryWithBackoff } from "./utils";
22
import { Account } from "./account";
33
import { BlockJSON } from "./models/blockJSON";
44
import { TransactionJSON } from "./models/transaction/transactionJSON";
@@ -101,7 +101,7 @@ class AleoNetworkClient {
101101
*/
102102
async fetchData<Type>(url = "/"): Promise<Type> {
103103
try {
104-
return parseJSON(await this.fetchRaw(url));
104+
return retryWithBackoff(() => this.fetchRaw(url).then(parseJSON));
105105
} catch (error) {
106106
throw new Error(`Error fetching data: ${error}`);
107107
}

sdk/src/utils.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,36 @@ export async function post(url: URL | string, options: RequestInit) {
3939

4040
return response;
4141
}
42+
43+
export async function retryWithBackoff<T>(
44+
fn: () => Promise<T>,
45+
maxAttempts = 5,
46+
baseDelay = 100
47+
): Promise<T> {
48+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
49+
try {
50+
return await fn();
51+
} catch (err: any) {
52+
const error = err as Error & { code?: string };
53+
const isLast = attempt === maxAttempts;
54+
55+
// retry only for certain errors
56+
const retryable =
57+
// err?. 404s but that will probably not be 404 soon ?
58+
error?.message?.includes('404') ||
59+
// retry for timeouts as well
60+
error?.message?.includes('5');
61+
62+
if (!retryable || isLast) throw error;
63+
64+
const jitter = Math.floor(Math.random() * baseDelay);
65+
const delay = baseDelay * 2 ** (attempt - 1) + jitter;
66+
console.warn(`Retry ${attempt}/${maxAttempts} failed. Retrying in ${delay}ms...`);
67+
68+
await new Promise(res => setTimeout(res, delay));
69+
}
70+
}
71+
72+
throw new Error("retryWithBackoff: unreachable");
73+
}
74+

sdk/tests/network-client.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,29 @@ describe("NodeConnection", () => {
214214
});
215215
});
216216

217+
describe("retryWithBackoff", () => {
218+
it("should retry failed network requests and eventually give up", async () => {
219+
const client = new AleoNetworkClient("http://localhost:1234");
220+
221+
let attemptCount = 0;
222+
223+
client.fetchRaw = async () => {
224+
attemptCount++;
225+
console.warn(`fake fetchRaw attempt ${attemptCount}`);
226+
throw new Error("503 Service Unavailable");
227+
};
228+
229+
try {
230+
await client.fetchData("/block/latest");
231+
throw new Error("Expected fetchData to fail");
232+
} catch (err: any) {
233+
expect(err.message).to.include("503");
234+
expect(attemptCount).to.be.greaterThan(1);
235+
}
236+
});
237+
});
238+
239+
217240
describe("waitForTransactionConfirmation", () => {
218241
const mainnetAcceptedTx =
219242
"at1dl9lze8wscct0dee8x9tjnfmpjvj33hh23jcnp5f0ywjn5552yrsperzl9";

0 commit comments

Comments
 (0)