Skip to content
This repository was archived by the owner on Jan 7, 2026. It is now read-only.

Commit cb3c50b

Browse files
juliangruberbajtos
andauthored
Add exponential backoff to getRoundStartEpoch(). #311 (#493)
* Add exponential backoff to `getRoundStartEpoch()`. #311 * typo * Update api/lib/round-tracker.js Co-authored-by: Miroslav Bajtoš <oss@bajtos.net> --------- Co-authored-by: Miroslav Bajtoš <oss@bajtos.net>
1 parent 2cb7fd2 commit cb3c50b

File tree

2 files changed

+47
-7
lines changed

2 files changed

+47
-7
lines changed

api/lib/round-tracker.js

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ async function updateSparkRound (pgPool, contract, newRoundIndex, recordTelemetr
7171
const meridianContractAddress = await contract.getAddress()
7272

7373
if (roundStartEpoch === undefined) {
74-
roundStartEpoch = await getRoundStartEpoch(contract, meridianRoundIndex)
74+
roundStartEpoch = await getRoundStartEpochWithBackoff(contract, meridianRoundIndex)
7575
}
7676

7777
const pgClient = await pgPool.connect()
@@ -329,17 +329,46 @@ async function defineTasksForRound (pgClient, sparkRoundNumber, taskCount) {
329329
])
330330
}
331331

332+
// Exponentially look at more blocks to handle the case when we have an outage
333+
// and the rounds are not advanced frequently enough, while keeping the happy
334+
// path performant.
335+
export async function getRoundStartEpochWithBackoff (
336+
contract,
337+
roundIndex,
338+
maxAttempts = 5
339+
) {
340+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
341+
try {
342+
return await getRoundStartEpoch(
343+
contract,
344+
roundIndex,
345+
50 * (2 ** attempt)
346+
)
347+
} catch (err) {
348+
if (attempt < maxAttempts) {
349+
console.warn('Failed to get round start epoch, retrying...', {
350+
err,
351+
attempt,
352+
maxAttempts,
353+
roundIndex
354+
})
355+
} else {
356+
throw err
357+
}
358+
}
359+
}
360+
}
361+
332362
/**
333363
* @param {MeridianContract} contract
334364
* @param {bigint} roundIndex
335365
* @returns {Promise<number>} Filecoin Epoch (ETH block number) when the SPARK round started
336366
*/
337-
export async function getRoundStartEpoch (contract, roundIndex) {
367+
export async function getRoundStartEpoch (contract, roundIndex, blocks) {
338368
assert.strictEqual(typeof roundIndex, 'bigint', `roundIndex must be a bigint, received: ${typeof roundIndex}`)
369+
assert.strictEqual(typeof blocks, 'number', `blocks must be a number, received: ${typeof blocks}`)
339370

340-
// Look at more blocks than should be necessary to handle the case when we have an outage and
341-
// the rounds are not advanced frequently enough.
342-
const recentRoundStartEvents = (await contract.queryFilter('RoundStart', -500))
371+
const recentRoundStartEvents = (await contract.queryFilter('RoundStart', -blocks))
343372
.map(({ blockNumber, args }) => ({ blockNumber, roundIndex: args[0] }))
344373

345374
const roundStart = recentRoundStartEvents.find(e => e.roundIndex === roundIndex)

api/test/round-tracker.test.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
TASKS_EXECUTED_PER_ROUND,
77
ROUND_TASKS_TO_NODE_TASKS_RATIO,
88
getRoundStartEpoch,
9+
getRoundStartEpochWithBackoff,
910
mapCurrentMeridianRoundToSparkRound,
1011
startRoundTracker,
1112
MAX_TASKS_PER_NODE_LIMIT
@@ -562,11 +563,21 @@ describe('Round Tracker', () => {
562563
})
563564

564565
describe('getRoundStartEpoch', () => {
565-
it('returns a block number', async function () {
566+
it('returns a block number, safely query many blocks', async function () {
566567
this.timeout(TIMEOUT_WHEN_QUERYING_CHAIN)
567568
const contract = await createMeridianContract()
568569
const roundIndex = await contract.currentRoundIndex()
569-
const startEpoch = await getRoundStartEpoch(contract, roundIndex)
570+
const startEpoch = await getRoundStartEpoch(contract, roundIndex, 500)
571+
assert.strictEqual(typeof startEpoch, 'number')
572+
})
573+
})
574+
575+
describe('getRoundStartEpochWithBackoff', () => {
576+
it('returns a block number, starting with query few blocks', async function () {
577+
this.timeout(TIMEOUT_WHEN_QUERYING_CHAIN)
578+
const contract = await createMeridianContract()
579+
const roundIndex = await contract.currentRoundIndex()
580+
const startEpoch = await getRoundStartEpochWithBackoff(contract, roundIndex)
570581
assert.strictEqual(typeof startEpoch, 'number')
571582
})
572583
})

0 commit comments

Comments
 (0)