diff --git a/packages/node-core/CHANGELOG.md b/packages/node-core/CHANGELOG.md index 24667e710d..753e733386 100644 --- a/packages/node-core/CHANGELOG.md +++ b/packages/node-core/CHANGELOG.md @@ -7,10 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - Schema Migration support for Enums, Relations, Subscription (#2251) + ### Fixed - Fixed non-atomic schema migration execution (#2244) - Testing Suites should run with unfinalizedBlocks `false` (#2258) +### Changed +- Improve error handling when fetching blocks (#2256) + ## [7.2.1] - 2024-02-07 ### Added - Update `ParentProject` to use `untilBlock` as and alias for `block` (#2235) diff --git a/packages/node-core/src/api.service.ts b/packages/node-core/src/api.service.ts index 208acbbd5d..5d938ccce6 100644 --- a/packages/node-core/src/api.service.ts +++ b/packages/node-core/src/api.service.ts @@ -43,18 +43,30 @@ export abstract class ApiService = any[] private timeouts: Record = {}; async fetchBlocks(heights: number[], numAttempts = MAX_RECONNECT_ATTEMPTS): Promise { + return this.retryFetch(async () => { + // Get the latest fetch function from the provider + const apiInstance = this.connectionPoolService.api; + return apiInstance.fetchBlocks(heights); + }, numAttempts); + } + + protected async retryFetch(fn: () => Promise, numAttempts = MAX_RECONNECT_ATTEMPTS): Promise { let reconnectAttempts = 0; + let lastError: Error | null = null; while (reconnectAttempts < numAttempts) { try { - // Get the latest fetch function from the provider - const apiInstance = this.connectionPoolService.api; - return await apiInstance.fetchBlocks(heights); + return await fn(); } catch (e: any) { - logger.error(e, `Failed to fetch blocks ${heights[0]}...${heights[heights.length - 1]}`); - + lastError = e; reconnectAttempts++; } } + if (lastError !== null) { + logger.error( + `Maximum number of retries (${numAttempts}) reached. See the following error for the underlying reason.` + ); + throw lastError; + } throw new Error(`Maximum number of retries (${numAttempts}) reached.`); } diff --git a/packages/node-core/src/indexer/blockDispatcher/block-dispatcher.ts b/packages/node-core/src/indexer/blockDispatcher/block-dispatcher.ts index a2d870d43c..8ebe3dc331 100644 --- a/packages/node-core/src/indexer/blockDispatcher/block-dispatcher.ts +++ b/packages/node-core/src/indexer/blockDispatcher/block-dispatcher.ts @@ -206,7 +206,7 @@ export abstract class BlockDispatcher // Do nothing, fetching the block was flushed, this could be caused by forked blocks or dynamic datasources return; } - logger.warn(e, 'Failed to enqueue fetched block to process'); + logger.error(e, 'Failed to enqueue fetched block to process'); process.exit(1); }); diff --git a/packages/node-core/src/indexer/worker/worker.service.ts b/packages/node-core/src/indexer/worker/worker.service.ts index e315c5df1d..6c2aff4c43 100644 --- a/packages/node-core/src/indexer/worker/worker.service.ts +++ b/packages/node-core/src/indexer/worker/worker.service.ts @@ -69,6 +69,7 @@ export abstract class BaseWorkerService< return; } logger.error(e, `Failed to fetch block ${height}`); + throw e; } } diff --git a/packages/node/src/indexer/api.service.ts b/packages/node/src/indexer/api.service.ts index 524e38e0d8..16b647a59d 100644 --- a/packages/node/src/indexer/api.service.ts +++ b/packages/node/src/indexer/api.service.ts @@ -263,27 +263,16 @@ export class ApiService return `api.rpc.${ext?.section ?? '*'}.${ext?.method ?? '*'}`; } + // Overrides the super function because of the specVer async fetchBlocks( heights: number[], overallSpecVer?: number, numAttempts = MAX_RECONNECT_ATTEMPTS, ): Promise { - let reconnectAttempts = 0; - while (reconnectAttempts < numAttempts) { - try { - const apiInstance = this.connectionPoolService.api; - return await apiInstance.fetchBlocks(heights, overallSpecVer); - } catch (e: any) { - logger.error( - e, - `Failed to fetch blocks ${heights[0]}...${ - heights[heights.length - 1] - }`, - ); - - reconnectAttempts++; - } - } - throw new Error(`Maximum number of retries (${numAttempts}) reached.`); + return this.retryFetch(async () => { + // Get the latest fetch function from the provider + const apiInstance = this.connectionPoolService.api; + return apiInstance.fetchBlocks(heights, overallSpecVer); + }, numAttempts); } }