diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index 38f942156a..b1f430ec0e 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -65,6 +65,8 @@ import { isNumber, sortBy, sum, uniqBy } from 'lodash'; export abstract class PortfolioCalculator { protected static readonly ENABLE_LOGGING = false; + private static readonly MAX_INITIALIZATION_ATTEMPTS = 3; + protected readonly logger = new Logger(PortfolioCalculator.name); protected accountBalanceItems: HistoricalDataItem[]; @@ -1124,7 +1126,7 @@ export abstract class PortfolioCalculator { } @LogPerformance - private async initialize() { + private async initialize(attempt = 1) { const startTimeTotal = performance.now(); let cachedPortfolioSnapshot: PortfolioSnapshot; @@ -1205,7 +1207,13 @@ export abstract class PortfolioCalculator { await job.finished(); } - await this.initialize(); + if (attempt >= PortfolioCalculator.MAX_INITIALIZATION_ATTEMPTS) { + throw new Error( + `Portfolio snapshot of user '${this.userId}' could not be computed after ${attempt} attempts` + ); + } + + await this.initialize(attempt + 1); } } } diff --git a/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts b/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts index cf94a9d2b0..2ade39a8ac 100644 --- a/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts +++ b/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts @@ -87,7 +87,7 @@ export class PortfolioSnapshotProcessor { : 0 ); - this.redisCacheService.set( + await this.redisCacheService.set( this.redisCacheService.getPortfolioSnapshotKey({ filters: job.data.filters, userId: job.data.userId diff --git a/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock.ts b/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock.ts index 7eb09d966f..6bfad95422 100644 --- a/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock.ts +++ b/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock.ts @@ -1,4 +1,8 @@ +import { PortfolioSnapshotValue } from '@ghostfolio/api/app/portfolio/interfaces/snapshot-value.interface'; +import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; + import type { Job, JobId, JobOptions } from 'bull'; +import ms from 'ms'; import { setTimeout } from 'timers/promises'; import { PortfolioSnapshotQueueJob } from './interfaces/portfolio-snapshot-queue-job.interface'; @@ -15,7 +19,15 @@ export const PortfolioSnapshotServiceMock = { finished: async () => { await setTimeout(100); - return Promise.resolve(); + // Mimic the processor which caches the computed portfolio snapshot + // under the job id + await RedisCacheServiceMock.set( + opts?.jobId as string, + JSON.stringify({ + expiration: Date.now() + ms('1 minute'), + portfolioSnapshot: {} + } as unknown as PortfolioSnapshotValue) + ); } };