From 7421c4795901090c0cf2fbf054c0a24a2ee64bff Mon Sep 17 00:00:00 2001 From: Liam Date: Sat, 19 Oct 2024 04:10:44 +0100 Subject: [PATCH] cleanup caching --- bun.lockb | Bin 364560 -> 364528 bytes .../src/common/{app-utils.ts => app.util.ts} | 0 projects/backend/src/common/cache.util.ts | 30 +++ .../backend/src/controller/app.controller.ts | 2 +- .../backend/src/service/image.service.tsx | 35 +--- projects/backend/src/service/score.service.ts | 194 ++++++++++-------- projects/website/package.json | 1 - .../(pages)/leaderboard/[...slug]/page.tsx | 6 +- .../src/app/(pages)/player/[...slug]/page.tsx | 6 +- 9 files changed, 153 insertions(+), 121 deletions(-) rename projects/backend/src/common/{app-utils.ts => app.util.ts} (100%) create mode 100644 projects/backend/src/common/cache.util.ts diff --git a/bun.lockb b/bun.lockb index 6aa74d02b541761a12a47b2eb14e097230a0eb35..3d135ce7cac8ee2218eaec5e765f1b723ad5f383 100755 GIT binary patch delta 58274 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 Y8}g$KF*|yjJ4?J_fJ^%m! delta 58546 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 + cache: SSRCache, + cacheKey: string, + fetchFn: () => Promise +): Promise { + if (cache == undefined) { + throw new InternalServerError(`Cache is not defined`); + } + + if (cache.has(cacheKey)) { + return cache.get(cacheKey); + } + + const data = await fetchFn(); + if (data) { + cache.set(cacheKey, data); + } + + return data; +} diff --git a/projects/backend/src/controller/app.controller.ts b/projects/backend/src/controller/app.controller.ts index 174253d..e905056 100644 --- a/projects/backend/src/controller/app.controller.ts +++ b/projects/backend/src/controller/app.controller.ts @@ -1,5 +1,5 @@ import { Controller, Get } from "elysia-decorators"; -import { getAppVersion } from "../common/app-utils"; +import { getAppVersion } from "../common/app.util"; import { AppService } from "../service/app.service"; @Controller() diff --git a/projects/backend/src/service/image.service.tsx b/projects/backend/src/service/image.service.tsx index b5484e0..faffa24 100644 --- a/projects/backend/src/service/image.service.tsx +++ b/projects/backend/src/service/image.service.tsx @@ -5,14 +5,17 @@ import { formatNumberWithCommas, formatPp } from "@ssr/common/utils/number-utils import { getDifficultyFromScoreSaberDifficulty } from "@ssr/common/utils/scoresaber-utils"; import { StarIcon } from "../../components/star-icon"; import { GlobeIcon } from "../../components/globe-icon"; -import NodeCache from "node-cache"; import ScoreSaberLeaderboardToken from "@ssr/common/types/token/scoresaber/score-saber-leaderboard-token"; import ScoreSaberPlayer, { getScoreSaberPlayerFromToken } from "@ssr/common/player/impl/scoresaber-player"; import { Jimp } from "jimp"; import { extractColors } from "extract-colors"; import { Config } from "@ssr/common/config"; +import { fetchWithCache } from "../common/cache.util"; +import { SSRCache } from "@ssr/common/cache"; -const cache = new NodeCache({ stdTTL: 60 * 60, checkperiod: 120 }); +const cache = new SSRCache({ + ttl: 1000 * 60 * 60, // 1 hour +}); const imageOptions = { width: 1200, height: 630 }; export class ImageService { @@ -26,7 +29,7 @@ export class ImageService { public static async getAverageImageColor(src: string): Promise<{ color: string } | undefined> { src = decodeURIComponent(src); - return await this.fetchWithCache<{ color: string }>(`average_color-${src}`, async () => { + return await fetchWithCache<{ color: string }>(cache, `average_color-${src}`, async () => { try { const image = await Jimp.read(src); // Load image using Jimp const { width, height, data } = image.bitmap; // Access image dimensions and pixel data @@ -54,28 +57,6 @@ export class ImageService { }); } - /** - * Fetches data with caching. - * - * @param cacheKey The key used for caching. - * @param fetchFn The function to fetch data if it's not in cache. - */ - private static async fetchWithCache( - cacheKey: string, - fetchFn: () => Promise - ): Promise { - if (cache.has(cacheKey)) { - return cache.get(cacheKey); - } - - const data = await fetchFn(); - if (data) { - cache.set(cacheKey, data); - } - - return data; - } - /** * The base of the OpenGraph image * @@ -120,7 +101,7 @@ export class ImageService { * @param id the player's id */ public static async generatePlayerImage(id: string) { - const player = await this.fetchWithCache(`player-${id}`, async () => { + const player = await fetchWithCache(cache, `player-${id}`, async () => { const token = await scoresaberService.lookupPlayer(id); return token ? await getScoreSaberPlayerFromToken(token, Config.apiUrl) : undefined; }); @@ -194,7 +175,7 @@ export class ImageService { * @param id the leaderboard's id */ public static async generateLeaderboardImage(id: string) { - const leaderboard = await this.fetchWithCache(`leaderboard-${id}`, () => + const leaderboard = await fetchWithCache(cache, `leaderboard-${id}`, () => scoresaberService.lookupLeaderboard(id) ); if (!leaderboard) { diff --git a/projects/backend/src/service/score.service.ts b/projects/backend/src/service/score.service.ts index 00d9dcd..e5d60e8 100644 --- a/projects/backend/src/service/score.service.ts +++ b/projects/backend/src/service/score.service.ts @@ -21,6 +21,16 @@ import PlayerScoresResponse from "@ssr/common/response/player-scores-response"; import { DiscordChannels, logToChannel } from "../bot/bot"; import { EmbedBuilder } from "discord.js"; import { Config } from "@ssr/common/config"; +import { SSRCache } from "@ssr/common/cache"; +import { fetchWithCache } from "../common/cache.util"; + +const playerScoresCache = new SSRCache({ + ttl: 1000 * 60, // 1 minute +}); + +const leaderboardScoresCache = new SSRCache({ + ttl: 1000 * 60, // 1 minute +}); export class ScoreService { /** @@ -116,58 +126,64 @@ export class ScoreService { page: number, sort: string, search?: string - ): Promise> { - const scores: PlayerScore[] | undefined = []; - let beatSaverMap: BeatSaverMap | undefined; - let metadata: Metadata = new Metadata(0, 0, 0, 0); // Default values + ): Promise | undefined> { + return fetchWithCache( + playerScoresCache, + `player-scores-${leaderboardName}-${id}-${page}-${sort}-${search}`, + async () => { + const scores: PlayerScore[] | undefined = []; + let beatSaverMap: BeatSaverMap | undefined; + let metadata: Metadata = new Metadata(0, 0, 0, 0); // Default values - switch (leaderboardName) { - case "scoresaber": { - const leaderboardScores = await scoresaberService.lookupPlayerScores({ - playerId: id, - page: page, - sort: sort as ScoreSort, - search: search, - }); - if (leaderboardScores == undefined) { - break; + switch (leaderboardName) { + case "scoresaber": { + const leaderboardScores = await scoresaberService.lookupPlayerScores({ + playerId: id, + page: page, + sort: sort as ScoreSort, + search: search, + }); + if (leaderboardScores == undefined) { + break; + } + + metadata = new Metadata( + Math.ceil(leaderboardScores.metadata.total / leaderboardScores.metadata.itemsPerPage), + leaderboardScores.metadata.total, + leaderboardScores.metadata.page, + leaderboardScores.metadata.itemsPerPage + ); + + for (const token of leaderboardScores.playerScores) { + const score = getScoreSaberScoreFromToken(token.score); + if (score == undefined) { + continue; + } + const tokenLeaderboard = getScoreSaberLeaderboardFromToken(token.leaderboard); + if (tokenLeaderboard == undefined) { + continue; + } + beatSaverMap = await BeatSaverService.getMap(tokenLeaderboard.songHash); + + scores.push({ + score: score, + leaderboard: tokenLeaderboard, + beatSaver: beatSaverMap, + }); + } + break; + } + default: { + throw new NotFoundError(`Leaderboard "${leaderboardName}" not found`); + } } - metadata = new Metadata( - Math.ceil(leaderboardScores.metadata.total / leaderboardScores.metadata.itemsPerPage), - leaderboardScores.metadata.total, - leaderboardScores.metadata.page, - leaderboardScores.metadata.itemsPerPage - ); - - for (const token of leaderboardScores.playerScores) { - const score = getScoreSaberScoreFromToken(token.score); - if (score == undefined) { - continue; - } - const tokenLeaderboard = getScoreSaberLeaderboardFromToken(token.leaderboard); - if (tokenLeaderboard == undefined) { - continue; - } - beatSaverMap = await BeatSaverService.getMap(tokenLeaderboard.songHash); - - scores.push({ - score: score, - leaderboard: tokenLeaderboard, - beatSaver: beatSaverMap, - }); - } - break; + return { + scores: scores, + metadata: metadata, + }; } - default: { - throw new NotFoundError(`Leaderboard "${leaderboardName}" not found`); - } - } - - return { - scores: scores, - metadata: metadata, - }; + ); } /** @@ -182,52 +198,54 @@ export class ScoreService { leaderboardName: Leaderboards, id: string, page: number - ): Promise> { - const scores: Score[] = []; - let leaderboard: Leaderboard | undefined; - let beatSaverMap: BeatSaverMap | undefined; - let metadata: Metadata = new Metadata(0, 0, 0, 0); // Default values + ): Promise | undefined> { + return fetchWithCache(leaderboardScoresCache, `leaderboard-scores-${leaderboardName}-${id}-${page}`, async () => { + const scores: Score[] = []; + let leaderboard: Leaderboard | undefined; + let beatSaverMap: BeatSaverMap | undefined; + let metadata: Metadata = new Metadata(0, 0, 0, 0); // Default values - switch (leaderboardName) { - case "scoresaber": { - const leaderboardResponse = await LeaderboardService.getLeaderboard(leaderboardName, id); - if (leaderboardResponse == undefined) { - throw new NotFoundError(`Leaderboard "${leaderboardName}" not found`); - } - leaderboard = leaderboardResponse.leaderboard; - beatSaverMap = leaderboardResponse.beatsaver; + switch (leaderboardName) { + case "scoresaber": { + const leaderboardResponse = await LeaderboardService.getLeaderboard(leaderboardName, id); + if (leaderboardResponse == undefined) { + throw new NotFoundError(`Leaderboard "${leaderboardName}" not found`); + } + leaderboard = leaderboardResponse.leaderboard; + beatSaverMap = leaderboardResponse.beatsaver; - const leaderboardScores = await scoresaberService.lookupLeaderboardScores(id, page); - if (leaderboardScores == undefined) { + const leaderboardScores = await scoresaberService.lookupLeaderboardScores(id, page); + if (leaderboardScores == undefined) { + break; + } + + for (const token of leaderboardScores.scores) { + const score = getScoreSaberScoreFromToken(token); + if (score == undefined) { + continue; + } + scores.push(score); + } + + metadata = new Metadata( + Math.ceil(leaderboardScores.metadata.total / leaderboardScores.metadata.itemsPerPage), + leaderboardScores.metadata.total, + leaderboardScores.metadata.page, + leaderboardScores.metadata.itemsPerPage + ); break; } - - for (const token of leaderboardScores.scores) { - const score = getScoreSaberScoreFromToken(token); - if (score == undefined) { - continue; - } - scores.push(score); + default: { + throw new NotFoundError(`Leaderboard "${leaderboardName}" not found`); } - - metadata = new Metadata( - Math.ceil(leaderboardScores.metadata.total / leaderboardScores.metadata.itemsPerPage), - leaderboardScores.metadata.total, - leaderboardScores.metadata.page, - leaderboardScores.metadata.itemsPerPage - ); - break; } - default: { - throw new NotFoundError(`Leaderboard "${leaderboardName}" not found`); - } - } - return { - scores: scores, - leaderboard: leaderboard, - beatSaver: beatSaverMap, - metadata: metadata, - }; + return { + scores: scores, + leaderboard: leaderboard, + beatSaver: beatSaverMap, + metadata: metadata, + }; + }); } } diff --git a/projects/website/package.json b/projects/website/package.json index bbdd914..1cc532a 100644 --- a/projects/website/package.json +++ b/projects/website/package.json @@ -38,7 +38,6 @@ "next": "15.0.0-rc.1", "next-build-id": "^3.0.0", "next-themes": "^0.3.0", - "node-cache": "^5.1.2", "react": "18.3.1", "react-chartjs-2": "^5.2.0", "react-countup": "^6.5.3", diff --git a/projects/website/src/app/(pages)/leaderboard/[...slug]/page.tsx b/projects/website/src/app/(pages)/leaderboard/[...slug]/page.tsx index 1463b3a..a30a0ef 100644 --- a/projects/website/src/app/(pages)/leaderboard/[...slug]/page.tsx +++ b/projects/website/src/app/(pages)/leaderboard/[...slug]/page.tsx @@ -3,7 +3,6 @@ import { redirect } from "next/navigation"; import { Colors } from "@/common/colors"; import { getAverageColor } from "@/common/image-utils"; import { LeaderboardData } from "@/components/leaderboard/leaderboard-data"; -import NodeCache from "node-cache"; import { Config } from "@ssr/common/config"; import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score"; import { LeaderboardResponse } from "@ssr/common/response/leaderboard-response"; @@ -11,6 +10,7 @@ import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leade import { fetchLeaderboard } from "@ssr/common/utils/leaderboard.util"; import { fetchLeaderboardScores } from "@ssr/common/utils/score-utils"; import LeaderboardScoresResponse from "@ssr/common/response/leaderboard-scores-response"; +import { SSRCache } from "@ssr/common/cache"; const UNKNOWN_LEADERBOARD = { title: "ScoreSaber Reloaded - Unknown Leaderboard", @@ -32,7 +32,9 @@ type LeaderboardData = { page: number; }; -const leaderboardCache = new NodeCache({ stdTTL: 60, checkperiod: 120 }); +const leaderboardCache = new SSRCache({ + ttl: 1000 * 60, // 1 minute +}); /** * Gets the leaderboard data and scores diff --git a/projects/website/src/app/(pages)/player/[...slug]/page.tsx b/projects/website/src/app/(pages)/player/[...slug]/page.tsx index 3162bc2..f5cc071 100644 --- a/projects/website/src/app/(pages)/player/[...slug]/page.tsx +++ b/projects/website/src/app/(pages)/player/[...slug]/page.tsx @@ -4,7 +4,6 @@ import { redirect } from "next/navigation"; import { Colors } from "@/common/colors"; import { getAverageColor } from "@/common/image-utils"; import { scoresaberService } from "@ssr/common/service/impl/scoresaber"; -import NodeCache from "node-cache"; import { getCookieValue } from "@ssr/common/utils/cookie-utils"; import { Config } from "@ssr/common/config"; import ScoreSaberPlayer, { getScoreSaberPlayerFromToken } from "@ssr/common/player/impl/scoresaber-player"; @@ -13,6 +12,7 @@ import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score"; import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard"; import { fetchPlayerScores } from "@ssr/common/utils/score-utils"; import PlayerScoresResponse from "@ssr/common/response/player-scores-response"; +import { SSRCache } from "@ssr/common/cache"; const UNKNOWN_PLAYER = { title: "ScoreSaber Reloaded - Unknown Player", @@ -36,7 +36,9 @@ type PlayerData = { search: string; }; -const playerCache = new NodeCache({ stdTTL: 60, checkperiod: 120 }); +const playerCache = new SSRCache({ + ttl: 1000 * 60, // 1 minute +}); /** * Gets the player data and scores