Browse Source

Task/extend portfolio snapshot cache key by performance calculation type (#7913)

* Extend portfolio snapshot cache key by performance calculation type

* Update changelog
pull/7916/head
Thomas Kaul 1 week ago
committed by GitHub
parent
commit
8529aea25e
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 6
      apps/api/src/app/auth/web-auth.service.ts
  3. 8
      apps/api/src/app/portfolio/calculator/portfolio-calculator.ts
  4. 5
      apps/api/src/app/redis-cache/redis-cache.service.mock.ts
  5. 11
      apps/api/src/app/redis-cache/redis-cache.service.ts
  6. 3
      apps/api/src/events/portfolio-changed.listener.ts
  7. 1
      apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts

1
CHANGELOG.md

@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- Extended the cache key of the portfolio snapshot by the performance calculation type
- Hardened the validation of the device id in the biometric authentication - Hardened the validation of the device id in the biometric authentication
### Fixed ### Fixed

6
apps/api/src/app/auth/web-auth.service.ts

@ -283,7 +283,11 @@ export class WebAuthService {
}); });
const portfolioSnapshotKey = const portfolioSnapshotKey =
this.redisCacheService.getPortfolioSnapshotKey({ filters, userId }); this.redisCacheService.getPortfolioSnapshotKey({
filters,
userId,
calculationType: userSettings.performanceCalculationType
});
if (await this.isPortfolioSnapshotExpired(portfolioSnapshotKey)) { if (await this.isPortfolioSnapshotExpired(portfolioSnapshotKey)) {
await this.portfolioSnapshotService.addJobToQueue({ await this.portfolioSnapshotService.addJobToQueue({

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

@ -1208,8 +1208,12 @@ export abstract class PortfolioCalculator {
let cachedPortfolioSnapshot: PortfolioSnapshot | undefined; let cachedPortfolioSnapshot: PortfolioSnapshot | undefined;
let isCachedPortfolioSnapshotExpired = false; let isCachedPortfolioSnapshotExpired = false;
const calculationType = this.getPerformanceCalculationType();
const portfolioSnapshotKey = this.redisCacheService.getPortfolioSnapshotKey( const portfolioSnapshotKey = this.redisCacheService.getPortfolioSnapshotKey(
{ {
calculationType,
filters: this.filters, filters: this.filters,
userId: this.userId userId: this.userId
} }
@ -1248,7 +1252,7 @@ export abstract class PortfolioCalculator {
// Compute in the background // Compute in the background
this.portfolioSnapshotService.addJobToQueue({ this.portfolioSnapshotService.addJobToQueue({
data: { data: {
calculationType: this.getPerformanceCalculationType(), calculationType,
filters: this.filters, filters: this.filters,
userCurrency: this.currency, userCurrency: this.currency,
userId: this.userId userId: this.userId
@ -1271,7 +1275,7 @@ export abstract class PortfolioCalculator {
// Wait for computation // Wait for computation
await this.portfolioSnapshotService.addJobToQueue({ await this.portfolioSnapshotService.addJobToQueue({
data: { data: {
calculationType: this.getPerformanceCalculationType(), calculationType,
filters: this.filters, filters: this.filters,
userCurrency: this.currency, userCurrency: this.currency,
userId: this.userId userId: this.userId

5
apps/api/src/app/redis-cache/redis-cache.service.mock.ts

@ -1,4 +1,5 @@
import { Filter } from '@ghostfolio/common/interfaces'; import { Filter } from '@ghostfolio/common/interfaces';
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type';
export const RedisCacheServiceMock = { export const RedisCacheServiceMock = {
cache: new Map<string, string>(), cache: new Map<string, string>(),
@ -8,15 +9,17 @@ export const RedisCacheServiceMock = {
return Promise.resolve(value); return Promise.resolve(value);
}, },
getPortfolioSnapshotKey: ({ getPortfolioSnapshotKey: ({
calculationType,
filters, filters,
userId userId
}: { }: {
calculationType: PerformanceCalculationType;
filters?: Filter[]; filters?: Filter[];
userId: string; userId: string;
}): string => { }): string => {
const filtersHash = filters?.length; const filtersHash = filters?.length;
return `portfolio-snapshot-${userId}${filtersHash > 0 ? `-${filtersHash}` : ''}`; return `portfolio-snapshot-${userId}-${calculationType}${filtersHash > 0 ? `-${filtersHash}` : ''}`;
}, },
reset: () => { reset: () => {
RedisCacheServiceMock.cache.clear(); RedisCacheServiceMock.cache.clear();

11
apps/api/src/app/redis-cache/redis-cache.service.ts

@ -1,6 +1,7 @@
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { getAssetProfileIdentifier } from '@ghostfolio/common/helper'; import { getAssetProfileIdentifier } from '@ghostfolio/common/helper';
import { AssetProfileIdentifier, Filter } from '@ghostfolio/common/interfaces'; import { AssetProfileIdentifier, Filter } from '@ghostfolio/common/interfaces';
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type';
import { CACHE_MANAGER, Cache } from '@nestjs/cache-manager'; import { CACHE_MANAGER, Cache } from '@nestjs/cache-manager';
import { Inject, Injectable, Logger } from '@nestjs/common'; import { Inject, Injectable, Logger } from '@nestjs/common';
@ -52,13 +53,15 @@ export class RedisCacheService {
} }
public getPortfolioSnapshotKey({ public getPortfolioSnapshotKey({
calculationType,
filters, filters,
userId userId
}: { }: {
calculationType: PerformanceCalculationType;
filters?: Filter[]; filters?: Filter[];
userId: string; userId: string;
}) { }) {
let portfolioSnapshotKey = `portfolio-snapshot-${userId}`; let portfolioSnapshotKey = `${this.getPortfolioSnapshotKeyPrefix({ userId })}-${calculationType}`;
if (filters?.length > 0) { if (filters?.length > 0) {
const filtersHash = createHash('sha256') const filtersHash = createHash('sha256')
@ -122,7 +125,7 @@ export class RedisCacheService {
userId: string; userId: string;
}) { }) {
const keys = await this.getKeys( const keys = await this.getKeys(
`${this.getPortfolioSnapshotKey({ userId })}` this.getPortfolioSnapshotKeyPrefix({ userId })
); );
return this.cache.mdel(keys); return this.cache.mdel(keys);
@ -139,4 +142,8 @@ export class RedisCacheService {
ttl ?? this.configurationService.get('CACHE_TTL') ttl ?? this.configurationService.get('CACHE_TTL')
); );
} }
private getPortfolioSnapshotKeyPrefix({ userId }: { userId: string }) {
return `portfolio-snapshot-${userId}`;
}
} }

3
apps/api/src/events/portfolio-changed.listener.ts

@ -80,7 +80,8 @@ export class PortfolioChangedListener {
...PORTFOLIO_SNAPSHOT_PROCESS_JOB_OPTIONS, ...PORTFOLIO_SNAPSHOT_PROCESS_JOB_OPTIONS,
jobId: this.redisCacheService.getPortfolioSnapshotKey({ jobId: this.redisCacheService.getPortfolioSnapshotKey({
filters, filters,
userId userId,
calculationType: userSettings.performanceCalculationType
}), }),
priority: PORTFOLIO_SNAPSHOT_COMPUTATION_QUEUE_PRIORITY_LOW priority: PORTFOLIO_SNAPSHOT_COMPUTATION_QUEUE_PRIORITY_LOW
} }

1
apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts

@ -87,6 +87,7 @@ export class PortfolioSnapshotProcessor {
await this.redisCacheService.set( await this.redisCacheService.set(
this.redisCacheService.getPortfolioSnapshotKey({ this.redisCacheService.getPortfolioSnapshotKey({
calculationType: job.data.calculationType,
filters: job.data.filters, filters: job.data.filters,
userId: job.data.userId userId: job.data.userId
}), }),

Loading…
Cancel
Save