Feature/improve portfolio snapshot caching (#3717)

* Improve portfolio snapshot caching

* Update changelog
pull/3719/head
Thomas Kaul 5 months ago committed by GitHub
parent c6f804f68c
commit 71a568264d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Reworked the portfolio calculator
- Improved the caching of the portfolio snapshot in the portfolio calculator by returning cached data and recalculating in the background when it expires
- Exposed the log levels as an environment variable (`LOG_LEVELS`)
- Exposed the maximum of chart data items as an environment variable (`MAX_CHART_ITEMS`)
- Changed the data format of the environment variable `CACHE_QUOTES_TTL` from seconds to milliseconds

@ -1,6 +1,7 @@
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { PortfolioOrder } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-order.interface';
import { PortfolioSnapshotValue } from '@ghostfolio/api/app/portfolio/interfaces/snapshot-value.interface';
import { TransactionPointSymbol } from '@ghostfolio/api/app/portfolio/interfaces/transaction-point-symbol.interface';
import { TransactionPoint } from '@ghostfolio/api/app/portfolio/interfaces/transaction-point.interface';
import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service';
@ -32,6 +33,7 @@ import { Logger } from '@nestjs/common';
import { Big } from 'big.js';
import { plainToClass } from 'class-transformer';
import {
addMilliseconds,
differenceInDays,
eachDayOfInterval,
endOfDay,
@ -863,6 +865,29 @@ export abstract class PortfolioCalculator {
return chartDateMap;
}
private async computeAndCacheSnapshot() {
const snapshot = await this.computeSnapshot();
const expiration = addMilliseconds(
new Date(),
this.configurationService.get('CACHE_QUOTES_TTL')
);
this.redisCacheService.set(
this.redisCacheService.getPortfolioSnapshotKey({
filters: this.filters,
userId: this.userId
}),
JSON.stringify(<PortfolioSnapshotValue>(<unknown>{
expiration: expiration.getTime(),
portfolioSnapshot: snapshot
})),
0
);
return snapshot;
}
@LogPerformance
private computeTransactionPoints() {
this.transactionPoints = [];
@ -1006,19 +1031,33 @@ export abstract class PortfolioCalculator {
private async initialize() {
const startTimeTotal = performance.now();
const cachedSnapshot = await this.redisCacheService.get(
let cachedPortfolioSnapshot: PortfolioSnapshot;
let isCachedPortfolioSnapshotExpired = false;
try {
const cachedPortfolioSnapshotValue = await this.redisCacheService.get(
this.redisCacheService.getPortfolioSnapshotKey({
filters: this.filters,
userId: this.userId
})
);
if (cachedSnapshot) {
this.snapshot = plainToClass(
const { expiration, portfolioSnapshot }: PortfolioSnapshotValue =
JSON.parse(cachedPortfolioSnapshotValue);
cachedPortfolioSnapshot = plainToClass(
PortfolioSnapshot,
JSON.parse(cachedSnapshot)
portfolioSnapshot
);
if (isAfter(new Date(), new Date(expiration))) {
isCachedPortfolioSnapshotExpired = true;
}
} catch {}
if (cachedPortfolioSnapshot) {
this.snapshot = cachedPortfolioSnapshot;
Logger.debug(
`Fetched portfolio snapshot from cache in ${(
(performance.now() - startTimeTotal) /
@ -1026,17 +1065,14 @@ export abstract class PortfolioCalculator {
).toFixed(3)} seconds`,
'PortfolioCalculator'
);
} else {
this.snapshot = await this.computeSnapshot();
this.redisCacheService.set(
this.redisCacheService.getPortfolioSnapshotKey({
filters: this.filters,
userId: this.userId
}),
JSON.stringify(this.snapshot),
this.configurationService.get('CACHE_QUOTES_TTL')
);
if (isCachedPortfolioSnapshotExpired) {
// Compute in the background
this.computeAndCacheSnapshot();
}
} else {
// Wait for computation
this.snapshot = await this.computeAndCacheSnapshot();
}
}
}

@ -0,0 +1,4 @@
export interface PortfolioSnapshotValue {
expiration: number;
portfolioSnapshot: string;
}
Loading…
Cancel
Save