Skip to content

Commit 8ae76df

Browse files
feat(backend): caches user data with 5mins of redis TTL
1 parent ddf9056 commit 8ae76df

2 files changed

Lines changed: 57 additions & 1 deletion

File tree

backend/src/api/controllers/user.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import MonkeyError, {
44
isFirebaseError,
55
} from "../../utils/error";
66
import { MonkeyResponse } from "../../utils/monkey-response";
7+
import { getCached, setCached, invalidateUserCache } from "../../utils/cache";
78
import * as DiscordUtils from "../../utils/discord";
89
import {
910
buildAgentLog,
@@ -914,6 +915,10 @@ export async function getProfile(
914915
): Promise<GetProfileResponse> {
915916
const { uidOrName } = req.params;
916917

918+
const cacheKey = `user:profile:${uidOrName}`;
919+
const cached = await getCached<GetProfileResponse>(cacheKey);
920+
if (cached !== null) return cached;
921+
917922
const user = req.query.isUid
918923
? await UserDAL.getUser(uidOrName, "get user profile")
919924
: await UserDAL.getUserByName(uidOrName, "get user profile");
@@ -987,7 +992,11 @@ export async function getProfile(
987992
};
988993

989994
if (banned) {
990-
return new MonkeyResponse("Profile retrived: banned user", baseProfile);
995+
await setCached(
996+
cacheKey,
997+
new MonkeyResponse("Profile retrieved: banned user", baseProfile),
998+
);
999+
return new MonkeyResponse("Profile retrieved: banned user", baseProfile);
9911000
}
9921001

9931002
const allTimeLbs = await getAllTimeLbs(user.uid);
@@ -1005,6 +1014,10 @@ export async function getProfile(
10051014
} else {
10061015
delete profileData.testActivity;
10071016
}
1017+
await setCached(
1018+
cacheKey,
1019+
new MonkeyResponse("Profile retrieved", profileData),
1020+
);
10081021
return new MonkeyResponse("Profile retrieved", profileData);
10091022
}
10101023

@@ -1050,6 +1063,7 @@ export async function updateProfile(
10501063
};
10511064

10521065
await UserDAL.updateProfile(uid, profileDetailsUpdates, user.inventory);
1066+
await invalidateUserCache(uid);
10531067

10541068
return new MonkeyResponse("Profile updated", profileDetailsUpdates);
10551069
}

backend/src/utils/cache.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { getConnection } from "../init/redis";
2+
3+
const CACHE_PREFIX = "cache:";
4+
const TTL = 300; // == 5 minutes
5+
6+
export async function getCached<T>(key: string): Promise<T | null> {
7+
const redis = getConnection();
8+
if (!redis) return null;
9+
10+
try {
11+
const data = await redis.get(`${CACHE_PREFIX}${key}`);
12+
if (data === null || data === undefined || data === "") return null;
13+
return JSON.parse(data) as T;
14+
} catch {
15+
return null;
16+
}
17+
}
18+
19+
export async function setCached<T>(key: string, data: T): Promise<void> {
20+
const redis = getConnection();
21+
if (!redis) return;
22+
23+
try {
24+
await redis.setex(`${CACHE_PREFIX}${key}`, TTL, JSON.stringify(data));
25+
} catch (err) {
26+
console.error("Cache set failed:", err);
27+
}
28+
}
29+
30+
export async function invalidateUserCache(userId: string): Promise<void> {
31+
const redis = getConnection();
32+
if (!redis) return;
33+
34+
try {
35+
const keys = await redis.keys(`${CACHE_PREFIX}user:profile:${userId}*`);
36+
if (keys.length > 0) {
37+
await redis.del(keys);
38+
}
39+
} catch (err) {
40+
console.error("Cache invalidation failed:", err);
41+
}
42+
}

0 commit comments

Comments
 (0)