Browse Source

Fix portfolio snapshot cache write and initialization retry limit

pull/7517/head
Thomas Kaul 4 weeks ago
parent
commit
ec0829f9b4
  1. 10
      apps/api/src/app/app.module.ts
  2. 7
      apps/api/src/app/portfolio/calculator/portfolio-calculator.ts
  3. 7
      apps/api/src/app/portfolio/errors/portfolio-snapshot-computation.error.ts
  4. 26
      apps/api/src/filters/portfolio-snapshot-computation-exception.filter.ts
  5. 14
      apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock.ts

10
apps/api/src/app/app.module.ts

@ -1,4 +1,5 @@
import { EventsModule } from '@ghostfolio/api/events/events.module';
import { PortfolioSnapshotComputationExceptionFilter } from '@ghostfolio/api/filters/portfolio-snapshot-computation-exception.filter';
import { getRedisConnectionOptions } from '@ghostfolio/api/helper/redis.helper';
import { BullBoardAuthMiddleware } from '@ghostfolio/api/middlewares/bull-board-auth.middleware';
import { HtmlTemplateMiddleware } from '@ghostfolio/api/middlewares/html-template.middleware';
@ -24,6 +25,7 @@ import { ThrottlerStorageRedisService } from '@nest-lab/throttler-storage-redis'
import { BullModule } from '@nestjs/bull';
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { APP_FILTER } from '@nestjs/core';
import { EventEmitterModule } from '@nestjs/event-emitter';
import { ScheduleModule } from '@nestjs/schedule';
import { ServeStaticModule } from '@nestjs/serve-static';
@ -184,7 +186,13 @@ import { UserModule } from './user/user.module';
UserModule,
WatchlistModule
],
providers: [I18nService]
providers: [
I18nService,
{
provide: APP_FILTER,
useClass: PortfolioSnapshotComputationExceptionFilter
}
]
})
export class AppModule implements NestModule {
public configure(consumer: MiddlewareConsumer) {

7
apps/api/src/app/portfolio/calculator/portfolio-calculator.ts

@ -1,4 +1,5 @@
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { PortfolioSnapshotComputationError } from '@ghostfolio/api/app/portfolio/errors/portfolio-snapshot-computation.error';
import { PortfolioCalculatorPosition } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-calculator-position.interface';
import { PortfolioOrder } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-order.interface';
import { PortfolioSnapshotValue } from '@ghostfolio/api/app/portfolio/interfaces/snapshot-value.interface';
@ -1190,9 +1191,9 @@ export abstract class PortfolioCalculator {
});
}
} else {
if (attempt >= PortfolioCalculator.MAX_INITIALIZATION_ATTEMPTS) {
throw new Error(
`Portfolio snapshot of user '${this.userId}' could not be computed after ${attempt} attempts`
if (attempt > PortfolioCalculator.MAX_INITIALIZATION_ATTEMPTS) {
throw new PortfolioSnapshotComputationError(
`Portfolio snapshot of user '${this.userId}' could not be computed after ${PortfolioCalculator.MAX_INITIALIZATION_ATTEMPTS} attempts`
);
}

7
apps/api/src/app/portfolio/errors/portfolio-snapshot-computation.error.ts

@ -0,0 +1,7 @@
export class PortfolioSnapshotComputationError extends Error {
public constructor(message: string) {
super(message);
this.name = 'PortfolioSnapshotComputationError';
}
}

26
apps/api/src/filters/portfolio-snapshot-computation-exception.filter.ts

@ -0,0 +1,26 @@
import { PortfolioSnapshotComputationError } from '@ghostfolio/api/app/portfolio/errors/portfolio-snapshot-computation.error';
import { ArgumentsHost, Catch, ExceptionFilter, Logger } from '@nestjs/common';
import { Response } from 'express';
import { getReasonPhrase, StatusCodes } from 'http-status-codes';
@Catch(PortfolioSnapshotComputationError)
export class PortfolioSnapshotComputationExceptionFilter implements ExceptionFilter {
private readonly logger = new Logger(
PortfolioSnapshotComputationExceptionFilter.name
);
public catch(
exception: PortfolioSnapshotComputationError,
host: ArgumentsHost
) {
this.logger.error(exception.message);
const response = host.switchToHttp().getResponse<Response>();
response.status(StatusCodes.SERVICE_UNAVAILABLE).json({
message: getReasonPhrase(StatusCodes.SERVICE_UNAVAILABLE),
statusCode: StatusCodes.SERVICE_UNAVAILABLE
});
}
}

14
apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock.ts

@ -8,13 +8,13 @@ import { setTimeout } from 'timers/promises';
import { PortfolioSnapshotQueueJob } from './interfaces/portfolio-snapshot-queue-job.interface';
export const PortfolioSnapshotServiceMock = {
addJobToQueue({
addJobToQueue: ({
opts
}: {
data: PortfolioSnapshotQueueJob;
name: string;
opts?: JobOptions;
}): Promise<Job> {
}): Promise<Job> => {
const mockJob: Partial<Job> = {
finished: async () => {
await setTimeout(100);
@ -31,17 +31,17 @@ export const PortfolioSnapshotServiceMock = {
}
};
this.jobsStore.set(opts?.jobId, mockJob);
PortfolioSnapshotServiceMock.jobsStore.set(opts?.jobId, mockJob);
return Promise.resolve(mockJob as Job);
},
getJob(jobId: JobId): Promise<Job> {
const job = this.jobsStore.get(jobId);
getJob: (jobId: JobId): Promise<Job> => {
const job = PortfolioSnapshotServiceMock.jobsStore.get(jobId);
return Promise.resolve(job as Job);
},
jobsStore: new Map<JobId, Partial<Job>>(),
reset() {
this.jobsStore.clear();
reset: () => {
PortfolioSnapshotServiceMock.jobsStore.clear();
}
};

Loading…
Cancel
Save