From 21bfd42a0f9814283613a0414b1def851cab3d50 Mon Sep 17 00:00:00 2001 From: Julian Gruber Date: Sun, 19 Jan 2025 10:12:21 +0100 Subject: [PATCH 1/3] Add exponential backoff to `getRoundStartEpoch()`. #311 --- api/lib/round-tracker.js | 39 +++++++++++++++++++++++++++++----- api/test/round-tracker.test.js | 15 +++++++++++-- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/api/lib/round-tracker.js b/api/lib/round-tracker.js index 1e530d96..9d00d45a 100644 --- a/api/lib/round-tracker.js +++ b/api/lib/round-tracker.js @@ -71,7 +71,7 @@ async function updateSparkRound (pgPool, contract, newRoundIndex, recordTelemetr const meridianContractAddress = await contract.getAddress() if (roundStartEpoch === undefined) { - roundStartEpoch = await getRoundStartEpoch(contract, meridianRoundIndex) + roundStartEpoch = await getRoundStartEpochWithBackoff(contract, meridianRoundIndex) } const pgClient = await pgPool.connect() @@ -329,17 +329,46 @@ async function defineTasksForRound (pgClient, sparkRoundNumber, taskCount) { ]) } +// Exponentially look at more blocks to handle the case when we have an outage +// and the rounds are not advanced frequently enough, while keeping the happy +// patch performant. +export async function getRoundStartEpochWithBackoff ( + contract, + roundIndex, + maxAttempts = 5 +) { + for (let attempt = 0; attempt < maxAttempts; attempt++) { + try { + return await getRoundStartEpoch( + contract, + roundIndex, + 50 * Math.pow(2, attempt) + ) + } catch (err) { + if (attempt < maxAttempts) { + console.warn('Failed to get round start epoch, retrying...', { + err, + attempt, + maxAttempts, + roundIndex + }) + } else { + throw err + } + } + } +} + /** * @param {MeridianContract} contract * @param {bigint} roundIndex * @returns {Promise} Filecoin Epoch (ETH block number) when the SPARK round started */ -export async function getRoundStartEpoch (contract, roundIndex) { +export async function getRoundStartEpoch (contract, roundIndex, blocks) { assert.strictEqual(typeof roundIndex, 'bigint', `roundIndex must be a bigint, received: ${typeof roundIndex}`) + assert.strictEqual(typeof blocks, 'number', `blocks must be a number, received: ${typeof blocks}`) - // Look at more blocks than should be necessary to handle the case when we have an outage and - // the rounds are not advanced frequently enough. - const recentRoundStartEvents = (await contract.queryFilter('RoundStart', -500)) + const recentRoundStartEvents = (await contract.queryFilter('RoundStart', -blocks)) .map(({ blockNumber, args }) => ({ blockNumber, roundIndex: args[0] })) const roundStart = recentRoundStartEvents.find(e => e.roundIndex === roundIndex) diff --git a/api/test/round-tracker.test.js b/api/test/round-tracker.test.js index 8f252856..c12e562d 100644 --- a/api/test/round-tracker.test.js +++ b/api/test/round-tracker.test.js @@ -6,6 +6,7 @@ import { TASKS_EXECUTED_PER_ROUND, ROUND_TASKS_TO_NODE_TASKS_RATIO, getRoundStartEpoch, + getRoundStartEpochWithBackoff, mapCurrentMeridianRoundToSparkRound, startRoundTracker, MAX_TASKS_PER_NODE_LIMIT @@ -562,11 +563,21 @@ describe('Round Tracker', () => { }) describe('getRoundStartEpoch', () => { - it('returns a block number', async function () { + it('returns a block number, safely query many blocks', async function () { this.timeout(TIMEOUT_WHEN_QUERYING_CHAIN) const contract = await createMeridianContract() const roundIndex = await contract.currentRoundIndex() - const startEpoch = await getRoundStartEpoch(contract, roundIndex) + const startEpoch = await getRoundStartEpoch(contract, roundIndex, 500) + assert.strictEqual(typeof startEpoch, 'number') + }) + }) + + describe('getRoundStartEpochWithBackoff', () => { + it('returns a block number, starting with query few blocks', async function () { + this.timeout(TIMEOUT_WHEN_QUERYING_CHAIN) + const contract = await createMeridianContract() + const roundIndex = await contract.currentRoundIndex() + const startEpoch = await getRoundStartEpochWithBackoff(contract, roundIndex) assert.strictEqual(typeof startEpoch, 'number') }) }) From a9a01d297184f331632c7f18c111ec6fe9527476 Mon Sep 17 00:00:00 2001 From: Julian Gruber Date: Sun, 19 Jan 2025 10:13:57 +0100 Subject: [PATCH 2/3] typo --- api/lib/round-tracker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/lib/round-tracker.js b/api/lib/round-tracker.js index 9d00d45a..2227aee3 100644 --- a/api/lib/round-tracker.js +++ b/api/lib/round-tracker.js @@ -331,7 +331,7 @@ async function defineTasksForRound (pgClient, sparkRoundNumber, taskCount) { // Exponentially look at more blocks to handle the case when we have an outage // and the rounds are not advanced frequently enough, while keeping the happy -// patch performant. +// path performant. export async function getRoundStartEpochWithBackoff ( contract, roundIndex, From 9ba7a76bde58858bf935d40228ea87d4650ff8bf Mon Sep 17 00:00:00 2001 From: Julian Gruber Date: Mon, 20 Jan 2025 09:08:52 +0100 Subject: [PATCH 3/3] Update api/lib/round-tracker.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Miroslav Bajtoš --- api/lib/round-tracker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/lib/round-tracker.js b/api/lib/round-tracker.js index 2227aee3..b624554d 100644 --- a/api/lib/round-tracker.js +++ b/api/lib/round-tracker.js @@ -342,7 +342,7 @@ export async function getRoundStartEpochWithBackoff ( return await getRoundStartEpoch( contract, roundIndex, - 50 * Math.pow(2, attempt) + 50 * (2 ** attempt) ) } catch (err) { if (attempt < maxAttempts) {