From 037d074e8c48e0faf3db0ea3d7598a07677c8bbb Mon Sep 17 00:00:00 2001 From: Gabriel Martinez Rodriguez Date: Tue, 25 Feb 2025 16:15:07 +0100 Subject: [PATCH 1/6] fix(randomness): reduce sleep time when too early getting drand number --- apps/randomness/src/DrandService.ts | 30 ++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/apps/randomness/src/DrandService.ts b/apps/randomness/src/DrandService.ts index 108d1cd226..2cf255ac7a 100644 --- a/apps/randomness/src/DrandService.ts +++ b/apps/randomness/src/DrandService.ts @@ -150,21 +150,29 @@ export class DrandService { await Promise.all( drandGaps.map(async (round) => { - let drandBeacon = await this.getDrandBeacon(round) - if (drandBeacon.isErr()) { - if (drandBeacon.error !== DrandError.TooEarly) { - console.error("Failed to get drand beacon", drandBeacon.error) - return - } - await sleep(1000) - drandBeacon = await this.getDrandBeacon(round) - if (drandBeacon.isErr()) { - console.error("Failed to get drand beacon", drandBeacon.error) + const maxRetries = 10 + const retryIntervalMs = 100 + let retryCount = 0 + + let drandBeacon: Result | undefined + while (retryCount < maxRetries) { + await sleep(retryIntervalMs) + drandBeacon = await this.getDrandBeacon(round) + + if (drandBeacon.isOk() || drandBeacon.error !== DrandError.TooEarly) { + break + } + + retryCount++ + } + + if (!drandBeacon || drandBeacon.isErr()) { + console.error("Failed to get drand beacon", drandBeacon?.error) return } - } + const postDrandTransactionResult = this.transactionFactory.createPostDrandTransaction({ round: round, From e2d28b7d3d75cbf634ef606d23abb247a76e859c Mon Sep 17 00:00:00 2001 From: Gabriel Martinez Rodriguez Date: Thu, 6 Mar 2025 14:44:58 +0100 Subject: [PATCH 2/6] chore(randomness): move sleep after get the first drand --- apps/randomness/src/DrandService.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/randomness/src/DrandService.ts b/apps/randomness/src/DrandService.ts index 2cf255ac7a..229eec8a5a 100644 --- a/apps/randomness/src/DrandService.ts +++ b/apps/randomness/src/DrandService.ts @@ -158,13 +158,12 @@ export class DrandService { let drandBeacon: Result | undefined while (retryCount < maxRetries) { - await sleep(retryIntervalMs) drandBeacon = await this.getDrandBeacon(round) if (drandBeacon.isOk() || drandBeacon.error !== DrandError.TooEarly) { break } - + await sleep(retryIntervalMs) retryCount++ } From 040835efaaa283109991d1cbe7bb78b6faadfa4d Mon Sep 17 00:00:00 2001 From: Gabriel Martinez Rodriguez Date: Thu, 6 Mar 2025 14:45:43 +0100 Subject: [PATCH 3/6] chore: format --- apps/randomness/src/DrandService.ts | 33 +++++++++++++---------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/apps/randomness/src/DrandService.ts b/apps/randomness/src/DrandService.ts index 229eec8a5a..f66a9b33ed 100644 --- a/apps/randomness/src/DrandService.ts +++ b/apps/randomness/src/DrandService.ts @@ -150,28 +150,25 @@ export class DrandService { await Promise.all( drandGaps.map(async (round) => { + const maxRetries = 10 + const retryIntervalMs = 100 + let retryCount = 0 + let drandBeacon: Result | undefined + while (retryCount < maxRetries) { + drandBeacon = await this.getDrandBeacon(round) - const maxRetries = 10 - const retryIntervalMs = 100 - let retryCount = 0 - - let drandBeacon: Result | undefined - while (retryCount < maxRetries) { - drandBeacon = await this.getDrandBeacon(round) - - if (drandBeacon.isOk() || drandBeacon.error !== DrandError.TooEarly) { - break - } - await sleep(retryIntervalMs) - retryCount++ + if (drandBeacon.isOk() || drandBeacon.error !== DrandError.TooEarly) { + break } + await sleep(retryIntervalMs) + retryCount++ + } - if (!drandBeacon || drandBeacon.isErr()) { - console.error("Failed to get drand beacon", drandBeacon?.error) - return - } - + if (!drandBeacon || drandBeacon.isErr()) { + console.error("Failed to get drand beacon", drandBeacon?.error) + return + } const postDrandTransactionResult = this.transactionFactory.createPostDrandTransaction({ round: round, From c845ff97e30623731900951f964ce2eb9b846948 Mon Sep 17 00:00:00 2001 From: Gabriel Martinez Rodriguez Date: Thu, 6 Mar 2025 15:14:43 +0100 Subject: [PATCH 4/6] chore(randomness): increased timeout --- apps/randomness/src/DrandService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/randomness/src/DrandService.ts b/apps/randomness/src/DrandService.ts index f66a9b33ed..7074534f8a 100644 --- a/apps/randomness/src/DrandService.ts +++ b/apps/randomness/src/DrandService.ts @@ -74,7 +74,7 @@ export class DrandService { } const url = `${env.EVM_DRAND_URL}/rounds/${round}` - const response = await ResultAsync.fromPromise(fetchWithRetry(url, {}, 2, 1000), unknownToError) + const response = await ResultAsync.fromPromise(fetchWithRetry(url, {}, 2, 2000), unknownToError) if (response.isErr()) { return err(DrandError.NetworkError) From 50f228b7df040a8590571844a23ff0c314f9e8ef Mon Sep 17 00:00:00 2001 From: Gabriel Martinez Rodriguez Date: Fri, 14 Mar 2025 12:23:19 +0100 Subject: [PATCH 5/6] chore(randomness): remove retries on _handleNewDrandBeacons --- apps/randomness/src/DrandService.ts | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/apps/randomness/src/DrandService.ts b/apps/randomness/src/DrandService.ts index 7074534f8a..8311d168ee 100644 --- a/apps/randomness/src/DrandService.ts +++ b/apps/randomness/src/DrandService.ts @@ -150,22 +150,9 @@ export class DrandService { await Promise.all( drandGaps.map(async (round) => { - const maxRetries = 10 - const retryIntervalMs = 100 - let retryCount = 0 - - let drandBeacon: Result | undefined - while (retryCount < maxRetries) { - drandBeacon = await this.getDrandBeacon(round) - - if (drandBeacon.isOk() || drandBeacon.error !== DrandError.TooEarly) { - break - } - await sleep(retryIntervalMs) - retryCount++ - } + const drandBeacon = await this.getDrandBeacon(round) - if (!drandBeacon || drandBeacon.isErr()) { + if (drandBeacon.isErr()) { console.error("Failed to get drand beacon", drandBeacon?.error) return } From 4ffb47a903ad213f1e19113f87f386e4ba4c87b2 Mon Sep 17 00:00:00 2001 From: Gabriel Martinez Rodriguez Date: Fri, 14 Mar 2025 16:00:32 +0100 Subject: [PATCH 6/6] feat(common): modified fetch --- apps/randomness/src/DrandService.ts | 2 +- support/common/lib/utils/fetch.ts | 55 +++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/apps/randomness/src/DrandService.ts b/apps/randomness/src/DrandService.ts index 8311d168ee..223ee30fef 100644 --- a/apps/randomness/src/DrandService.ts +++ b/apps/randomness/src/DrandService.ts @@ -74,7 +74,7 @@ export class DrandService { } const url = `${env.EVM_DRAND_URL}/rounds/${round}` - const response = await ResultAsync.fromPromise(fetchWithRetry(url, {}, 2, 2000), unknownToError) + const response = await ResultAsync.fromPromise(fetchWithRetry(url, {}, 3, 1000, 3000), unknownToError) if (response.isErr()) { return err(DrandError.NetworkError) diff --git a/support/common/lib/utils/fetch.ts b/support/common/lib/utils/fetch.ts index fe15cb515e..4858bfffc2 100644 --- a/support/common/lib/utils/fetch.ts +++ b/support/common/lib/utils/fetch.ts @@ -1,3 +1,6 @@ +import { promiseWithResolvers } from "./promises" +import { sleep } from "./sleep" + /** * Performs an HTTP request using fetch with retry capability and a timeout for each attempt. * @@ -6,8 +9,10 @@ * See https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#fetch_options. * @param maxRetries The maximum number of retry attempts in case of failure or timeout. * (Default: 3) - * @param timeout The maximum time (in milliseconds) to wait for each attempt before timing out. + * @param retryAfter The time (in milliseconds) to wait before retrying the request. * (Default: 5000) + * @param absoluteTimeout The maximum time (in milliseconds) to wait for all retry attempts before timing out. + * (Default: 15000) * * @returns A promise that resolves with the response of the request (type Response). * Throws an error if all retry attempts fail or if a timeout occurs. @@ -16,19 +21,41 @@ export async function fetchWithRetry( url: string, options: RequestInit = {}, maxRetries = 3, - timeout = 5000, + retryAfter = 5000, + absoluteTimeout = 15000, ): Promise { - for (let attempt = 1; attempt <= maxRetries; attempt++) { - try { - const signal = AbortSignal.timeout(timeout) - const response = await fetch(url, { ...options, signal }) - return response - } catch (error) { - if (attempt === maxRetries) { - throw error - } - } - } + return new Promise((outerResolve, outerReject) => { + ;(async () => { + let isResolved = false + const startTime = Date.now() + + for (let i = 0; i < maxRetries; i++) { + const { resolve: localResolve, promise: localPromise } = promiseWithResolvers() + const elapsed = Date.now() - startTime + const signal = AbortSignal.timeout(absoluteTimeout - elapsed) - throw new Error("Failed to complete the request.") + fetch(url, { ...options, signal }) + .then((response) => { + if (response.ok) { + isResolved = true + outerResolve(response) + } else if (i === maxRetries - 1) { + outerResolve(response) + } else { + localResolve(response) + } + }) + .catch((err) => { + if (i === maxRetries - 1) { + outerReject(err) + } else { + localResolve(err) + } + }) + + await Promise.race([localPromise, sleep(retryAfter)]) + if (isResolved) break + } + })() + }) }