diff --git a/packages/neuron-wallet/src/block-sync-renderer/sync/full-synchronizer.ts b/packages/neuron-wallet/src/block-sync-renderer/sync/full-synchronizer.ts index 64cfe86eeb..ffb6016c7b 100644 --- a/packages/neuron-wallet/src/block-sync-renderer/sync/full-synchronizer.ts +++ b/packages/neuron-wallet/src/block-sync-renderer/sync/full-synchronizer.ts @@ -30,12 +30,16 @@ export default class FullSynchronizer extends Synchronizer { await this.processNextBlockNumber() while (this.pollingIndexer) { - const indexerTipBlock = await this.indexer.tip() - await this.synchronize(indexerTipBlock) + try { + const indexerTipBlock = await this.indexer.tip() + await this.synchronize(indexerTipBlock) + } catch (error) { + logger.error(`Full synchronization iteration failed; retrying in 5 seconds: ${error.message}`) + } await CommonUtils.sleep(5000) } } catch (error) { - logger.error(`Error connecting to Indexer: ${error.message}`) + logger.error(`Failed to initialize full synchronization: ${error.message}`) } } diff --git a/packages/neuron-wallet/tests/block-sync-renderer/full-synchronizer.test.ts b/packages/neuron-wallet/tests/block-sync-renderer/full-synchronizer.test.ts index bceea87ad7..e8d45159d4 100644 --- a/packages/neuron-wallet/tests/block-sync-renderer/full-synchronizer.test.ts +++ b/packages/neuron-wallet/tests/block-sync-renderer/full-synchronizer.test.ts @@ -338,11 +338,14 @@ describe('unit tests for IndexerConnector', () => { await connectIndexer(indexerConnector) await flushPromises() }) - it('throws error', async () => { + it('logs the queue worker error', () => { expect(stubbedLoggerErrorFn).toHaveBeenCalledWith( 'Connector: \tError in processing next block number queue: Error: exception' ) }) + it('starts polling after the queue worker error', () => { + expect(stubbedTipFn).toHaveBeenCalledTimes(1) + }) }) }) describe('#blockTipsSubject', () => { @@ -399,6 +402,46 @@ describe('unit tests for IndexerConnector', () => { }) }) }) + describe('when a polling iteration fails', () => { + const sqliteBusyError = new Error('SQLITE_BUSY: database is locked') + let tipObserver: any + + beforeEach(async () => { + tipObserver = jest.fn() + indexerConnector.blockTipsSubject.subscribe(tip => { + tipObserver(tip) + }) + stubbedTipFn.mockResolvedValue(fakeTip1) + stubbedUpsertTxHashesFn.mockRejectedValueOnce(sqliteBusyError).mockResolvedValue([]) + stubbedNextUnprocessedTxsGroupedByBlockNumberFn.mockResolvedValue([]) + stubbedNextUnprocessedBlock.mockResolvedValue(undefined) + + await connectIndexer(indexerConnector) + }) + + it('logs the error without emitting a block tip', () => { + expect(stubbedLoggerErrorFn).toHaveBeenCalledWith( + 'Full synchronization iteration failed; retrying in 5 seconds: SQLITE_BUSY: database is locked' + ) + expect(tipObserver).toHaveBeenCalledTimes(0) + }) + + describe('after the polling interval', () => { + beforeEach(async () => { + jest.advanceTimersByTime(5000) + await flushPromises() + }) + + it('continues polling and emits the recovered block tip', () => { + expect(stubbedTipFn).toHaveBeenCalledTimes(2) + expect(tipObserver).toHaveBeenCalledTimes(1) + expect(tipObserver).toHaveBeenCalledWith({ + cacheTipNumber: parseInt(fakeTip1.blockNumber), + indexerTipNumber: parseInt(fakeTip1.blockNumber), + }) + }) + }) + }) }) }) })