perf: cache acct

This commit is contained in:
Namekuji 2023-08-07 08:32:29 -04:00
parent df71ee65b2
commit df4cf7822e
No known key found for this signature in database
GPG key ID: 1D62332C07FBA532
3 changed files with 29 additions and 14 deletions

View file

@ -140,7 +140,7 @@ export class Cache<T> {
} }
} }
class SetCache { export class SetCache {
private readonly key: string; private readonly key: string;
private readonly fetchedKey: string; private readonly fetchedKey: string;
private readonly fetcher: () => Promise<string[]>; private readonly fetcher: () => Promise<string[]>;
@ -198,7 +198,7 @@ class SetCache {
} }
} }
class HashCache { export class HashCache {
private readonly key: string; private readonly key: string;
private readonly fetchedKey: string; private readonly fetchedKey: string;
private readonly fetcher: () => Promise<Map<string, string>>; private readonly fetcher: () => Promise<Map<string, string>>;

View file

@ -34,6 +34,10 @@ import { initializeStreamingServer } from "./api/streaming.js";
import { koaBody } from "koa-body"; import { koaBody } from "koa-body";
import removeTrailingSlash from "koa-remove-trailing-slashes"; import removeTrailingSlash from "koa-remove-trailing-slashes";
import { v4 as uuid } from "uuid"; import { v4 as uuid } from "uuid";
import {
acctToUserIdCache,
userDenormalizedCache,
} from "@/services/user-cache.js";
export const serverLogger = new Logger("server", "gray", false); export const serverLogger = new Logger("server", "gray", false);
@ -109,19 +113,29 @@ router.use(wellKnown.routes());
router.get("/avatar/@:acct", async (ctx) => { router.get("/avatar/@:acct", async (ctx) => {
const { username, host } = Acct.parse(ctx.params.acct); const { username, host } = Acct.parse(ctx.params.acct);
const user = await Users.findOne({ const userId = await acctToUserIdCache.fetchMaybe(
`${username}@${host ?? config.host}`,
() =>
Users.findOne({
where: { where: {
usernameLower: username.toLowerCase(), usernameLower: username.toLowerCase(),
host: host == null || host === config.host ? IsNull() : host, host: !host || host === config.host ? IsNull() : host,
isSuspended: false, isSuspended: false,
}, },
relations: ["avatar"], }).then((user) => user?.id ?? undefined),
}); true,
);
if (user) { if (!userId) {
ctx.redirect(Users.getAvatarUrlSync(user));
} else {
ctx.redirect("/static-assets/user-unknown.png"); ctx.redirect("/static-assets/user-unknown.png");
} else {
const user = await userDenormalizedCache.fetch(userId, () =>
Users.findOneOrFail({
relations: { avatar: true, banner: true },
where: { id: userId },
}),
);
ctx.redirect(Users.getAvatarUrlSync(user));
} }
}); });

View file

@ -4,7 +4,7 @@ import type {
ILocalUser, ILocalUser,
} from "@/models/entities/user.js"; } from "@/models/entities/user.js";
import { Users } from "@/models/index.js"; import { Users } from "@/models/index.js";
import { Cache } from "@/misc/cache.js"; import { Cache, HashCache } from "@/misc/cache.js";
import { redisClient, subscriber } from "@/db/redis.js"; import { redisClient, subscriber } from "@/db/redis.js";
export const userByIdCache = new Cache<CacheableUser>("userById", 60 * 30); export const userByIdCache = new Cache<CacheableUser>("userById", 60 * 30);
@ -24,6 +24,7 @@ export const userDenormalizedCache = new Cache<CacheableUser>(
"userDenormalized", "userDenormalized",
60 * 30, 60 * 30,
); );
export const acctToUserIdCache = new Cache<string>("acctToUserId", 60 * 60 * 24);
subscriber.on("message", async (_, data) => { subscriber.on("message", async (_, data) => {
const obj = JSON.parse(data); const obj = JSON.parse(data);