move beatsaver map caching to redis
All checks were successful
deploy / deploy (push) Successful in 1m35s

This commit is contained in:
Lee 2023-11-08 23:17:32 +00:00
parent af707a8c79
commit e6b169f3fc
9 changed files with 154 additions and 37 deletions

4
.env Normal file

@ -0,0 +1,4 @@
SENTRY_AUTH_TOKEN=hi
# Redis
REDIS_URL=redis://:bigtitsyes7@10.0.0.203:30004

@ -1 +1,4 @@
SENTRY_AUTH_TOKEN=hi
SENTRY_AUTH_TOKEN=hi
# Redis
REDIS_URL=redis://localhost:6379/0

@ -39,6 +39,7 @@
"react-chartjs-2": "^5.2.0",
"react-dom": "^18",
"react-toastify": "^9.1.3",
"redis": "^4.6.10",
"sharp": "^0.32.6",
"tailwind-merge": "^2.0.0",
"tailwindcss-animate": "^1.0.7",

73
pnpm-lock.yaml generated

@ -89,6 +89,9 @@ dependencies:
react-toastify:
specifier: ^9.1.3
version: 9.1.3(react-dom@18.2.0)(react@18.2.0)
redis:
specifier: ^4.6.10
version: 4.6.10
sharp:
specifier: ^0.32.6
version: 0.32.6
@ -1062,6 +1065,55 @@ packages:
'@babel/runtime': 7.23.2
dev: false
/@redis/bloom@1.2.0(@redis/client@1.5.11):
resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
'@redis/client': 1.5.11
dev: false
/@redis/client@1.5.11:
resolution: {integrity: sha512-cV7yHcOAtNQ5x/yQl7Yw1xf53kO0FNDTdDU6bFIMbW6ljB7U7ns0YRM+QIkpoqTAt6zK5k9Fq0QWlUbLcq9AvA==}
engines: {node: '>=14'}
dependencies:
cluster-key-slot: 1.1.2
generic-pool: 3.9.0
yallist: 4.0.0
dev: false
/@redis/graph@1.1.0(@redis/client@1.5.11):
resolution: {integrity: sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
'@redis/client': 1.5.11
dev: false
/@redis/json@1.0.6(@redis/client@1.5.11):
resolution: {integrity: sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
'@redis/client': 1.5.11
dev: false
/@redis/search@1.1.5(@redis/client@1.5.11):
resolution: {integrity: sha512-hPP8w7GfGsbtYEJdn4n7nXa6xt6hVZnnDktKW4ArMaFQ/m/aR7eFvsLQmG/mn1Upq99btPJk+F27IQ2dYpCoUg==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
'@redis/client': 1.5.11
dev: false
/@redis/time-series@1.0.5(@redis/client@1.5.11):
resolution: {integrity: sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
'@redis/client': 1.5.11
dev: false
/@rollup/plugin-commonjs@24.0.0(rollup@2.78.0):
resolution: {integrity: sha512-0w0wyykzdyRRPHOb0cQt14mIBLujfAv6GgP6g8nvg/iBxEm112t3YPPq+Buqe2+imvElTka+bjNlJ/gB56TD8g==}
engines: {node: '>=14.0.0'}
@ -1719,6 +1771,11 @@ packages:
engines: {node: '>=6'}
dev: false
/cluster-key-slot@1.1.2:
resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
engines: {node: '>=0.10.0'}
dev: false
/color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
@ -2524,6 +2581,11 @@ packages:
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
dev: true
/generic-pool@3.9.0:
resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==}
engines: {node: '>= 4'}
dev: false
/get-intrinsic@1.2.2:
resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==}
dependencies:
@ -3766,6 +3828,17 @@ packages:
dependencies:
picomatch: 2.3.1
/redis@4.6.10:
resolution: {integrity: sha512-mmbyhuKgDiJ5TWUhiKhBssz+mjsuSI/lSZNPI9QvZOYzWvYGejtb+W3RlDDf8LD6Bdl5/mZeG8O1feUGhXTxEg==}
dependencies:
'@redis/bloom': 1.2.0(@redis/client@1.5.11)
'@redis/client': 1.5.11
'@redis/graph': 1.1.0(@redis/client@1.5.11)
'@redis/json': 1.0.6(@redis/client@1.5.11)
'@redis/search': 1.1.5(@redis/client@1.5.11)
'@redis/time-series': 1.0.5(@redis/client@1.5.11)
dev: false
/reflect.getprototypeof@1.0.4:
resolution: {integrity: sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==}
engines: {node: '>= 0.4'}

@ -1,7 +1,8 @@
import { Redis } from "@/db/redis";
import { BeatsaverMap } from "@/schemas/beatsaver/BeatsaverMap";
import { BeatsaverAPI } from "@/utils/beatsaver/api";
const mapCache = new Map<string, BeatsaverMap>();
await Redis.connectRedis();
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
@ -10,24 +11,41 @@ export async function GET(request: Request) {
return new Response("mapHashes parameter is required", { status: 400 });
}
const idOnly = searchParams.get("idonly") === "true";
let totalInCache = 0;
const maps: Record<string, BeatsaverMap | { id: string }> = {};
for (const mapHash of mapHashes) {
if (mapCache.has(mapHash)) {
maps[mapHash] = mapCache.get(mapHash)!;
const cachedMap = await (
await Redis.client
).get(`beatsaver:map:${mapHash}`);
if (cachedMap) {
maps[mapHash] = JSON.parse(cachedMap);
totalInCache++;
} else {
const map = await BeatsaverAPI.fetchMapByHash(mapHash);
if (map) {
maps[mapHash] = map;
mapCache.set(mapHash, map);
}
if (map && idOnly) {
maps[mapHash] = { id: map.id };
if (!map) {
continue;
}
maps[mapHash] = map;
await (
await Redis.client
).set("beatsaver:map:" + mapHash, JSON.stringify(map), {
EX: 60 * 60 * 24 * 7, // 7 days
});
}
if (idOnly) {
maps[mapHash] = { id: (maps[mapHash] as BeatsaverMap).id };
}
}
return new Response(JSON.stringify(maps), {
headers: { "content-type": "application/json;charset=UTF-8" },
});
return new Response(
JSON.stringify({
maps,
totalInCache,
}),
{
headers: { "content-type": "application/json;charset=UTF-8" },
},
);
}

@ -16,14 +16,14 @@ export default function BeatSaverLogo({
version="1.1"
className={className}
>
<g fill="none" stroke="#000000" stroke-width="10">
<path d="M 100,7 189,47 100,87 12,47 Z" stroke-linejoin="round"></path>
<g fill="none" stroke="#000000" strokeWidth="10">
<path d="M 100,7 189,47 100,87 12,47 Z" strokeLinejoin="round"></path>
<path
d="M 189,47 189,155 100,196 12,155 12,47"
stroke-linejoin="round"
strokeLinejoin="round"
></path>
<path d="M 100,87 100,196" stroke-linejoin="round"></path>
<path d="M 26,77 85,106 53,130 Z" stroke-linejoin="round"></path>
<path d="M 100,87 100,196" strokeLinejoin="round"></path>
<path d="M 26,77 85,106 53,130 Z" strokeLinejoin="round"></path>
</g>
</svg>
);

31
src/db/redis.ts Normal file

@ -0,0 +1,31 @@
import { createClient } from "redis";
let redisClient = connectRedis();
async function connectRedis() {
console.log("Connecting to redis");
const client = createClient({
url: process.env.REDIS_URL,
});
await client.connect();
client.on("connect", () => {
console.log("Connected to redis");
});
client.on("error", (error) => {
console.error("There was an error connecting to redis: " + error);
setTimeout(() => {
redisClient = connectRedis();
}, 5_000); // 5 seconds
});
return client;
}
// todo: add disconnect handler
export const Redis = {
client: redisClient,
connectRedis,
};

@ -176,7 +176,7 @@ async function fetchScoresWithBeatsaverData(
});
const mapJson = await mapResponse.json();
for (const score of scores) {
const mapData = mapJson[score.leaderboard.songHash];
const mapData = mapJson.maps[score.leaderboard.songHash];
if (mapData) {
scoresWithBeatsaverData[score.leaderboard.songHash] = {
score: score.score,

@ -1,11 +1,7 @@
{
"compilerOptions": {
"target": "ES2016",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"target": "ES2022",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@ -24,18 +20,9 @@
}
],
"paths": {
"@/*": [
"./src/*"
]
"@/*": ["./src/*"]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}