From f4d8f82239d07356f8b400cd6c99a65107aa300e Mon Sep 17 00:00:00 2001 From: Namekuji Date: Sun, 6 Aug 2023 00:40:55 -0400 Subject: [PATCH] perf: add blockee cache --- packages/backend/src/db/scylla.ts | 16 +++++++++++++++ packages/backend/src/misc/cache.ts | 20 +++++++++++++++++++ .../server/api/endpoints/blocking/create.ts | 9 +++------ .../server/api/endpoints/blocking/delete.ts | 9 +++------ .../server/api/endpoints/notes/timeline.ts | 2 ++ .../backend/src/services/blocking/create.ts | 4 ++++ .../backend/src/services/blocking/delete.ts | 5 ++++- 7 files changed, 52 insertions(+), 13 deletions(-) diff --git a/packages/backend/src/db/scylla.ts b/packages/backend/src/db/scylla.ts index 1b39530a9e..a658e54035 100644 --- a/packages/backend/src/db/scylla.ts +++ b/packages/backend/src/db/scylla.ts @@ -9,6 +9,7 @@ import { ChannelFollowingsCache, InstanceMutingsCache, LocalFollowingsCache, + UserBlockedCache, UserMutingsCache, userWordMuteCache, } from "@/misc/cache.js"; @@ -466,3 +467,18 @@ export async function filterMutedNote( return notes.filter((note) => !getWordHardMute(note, user, mutedWords)); } + +export async function filterBlockedUser( + notes: ScyllaNote[], + user: { id: User["id"] }, +): Promise { + const cache = await UserBlockedCache.init(user.id); + const blockerIds = await cache.getAll(); + + return notes.filter( + (note) => + !blockerIds.includes(note.userId) && + !(note.replyUserId && blockerIds.includes(note.replyUserId)) && + !(note.renoteUserId && blockerIds.includes(note.renoteUserId)), + ); +} diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts index 9a7ff064f7..b8cf15d8d3 100644 --- a/packages/backend/src/misc/cache.ts +++ b/packages/backend/src/misc/cache.ts @@ -2,6 +2,7 @@ import { redisClient } from "@/db/redis.js"; import { encode, decode } from "msgpackr"; import { ChainableCommander } from "ioredis"; import { + Blockings, ChannelFollowings, Followings, MutedNotes, @@ -380,4 +381,23 @@ export class InstanceMutingsCache extends SetCache { } } +export class UserBlockedCache extends SetCache { + private constructor(userId: string) { + const fetcher = () => + Blockings.find({ + select: ["blockerId"], + where: { blockeeId: userId }, + }).then((blocks) => blocks.map(({ blockerId }) => blockerId)); + + super("blocked", userId, fetcher); + } + + public static async init(userId: string): Promise { + const cache = new UserBlockedCache(userId); + await cache.fetch(); + + return cache; + } +} + export const userWordMuteCache = new Cache("mutedWord", 60 * 30); diff --git a/packages/backend/src/server/api/endpoints/blocking/create.ts b/packages/backend/src/server/api/endpoints/blocking/create.ts index f00a2923d5..c4cf3e4e16 100644 --- a/packages/backend/src/server/api/endpoints/blocking/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking/create.ts @@ -4,6 +4,7 @@ import { ApiError } from "../../error.js"; import { getUser } from "../../common/getters.js"; import { Blockings, NoteWatchings, Users } from "@/models/index.js"; import { HOUR } from "@/const.js"; +import { UserBlockedCache } from "@/misc/cache.js"; export const meta = { tags: ["account"], @@ -69,12 +70,8 @@ export default define(meta, paramDef, async (ps, user) => { }); // Check if already blocking - const exist = await Blockings.exist({ - where: { - blockerId: blocker.id, - blockeeId: blockee.id, - }, - }); + const cache = await UserBlockedCache.init(blockee.id); + const exist = await cache.has(blocker.id); if (exist) { throw new ApiError(meta.errors.alreadyBlocking); diff --git a/packages/backend/src/server/api/endpoints/blocking/delete.ts b/packages/backend/src/server/api/endpoints/blocking/delete.ts index 037d5af22c..9e69367305 100644 --- a/packages/backend/src/server/api/endpoints/blocking/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking/delete.ts @@ -4,6 +4,7 @@ import { ApiError } from "../../error.js"; import { getUser } from "../../common/getters.js"; import { Blockings, Users } from "@/models/index.js"; import { HOUR } from "@/const.js"; +import { UserBlockedCache } from "@/misc/cache.js"; export const meta = { tags: ["account"], @@ -69,12 +70,8 @@ export default define(meta, paramDef, async (ps, user) => { }); // Check not blocking - const exist = await Blockings.exist({ - where: { - blockerId: blocker.id, - blockeeId: blockee.id, - }, - }); + const cache = await UserBlockedCache.init(blockee.id); + const exist = await cache.has(blocker.id); if (!exist) { throw new ApiError(meta.errors.notBlocking); diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index f4e492fcd3..ecdcaa3e5c 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -20,6 +20,7 @@ import { execTimelineQuery, filterMutedUser, filterMutedNote, + filterBlockedUser, } from "@/db/scylla.js"; import { ChannelFollowingsCache, LocalFollowingsCache } from "@/misc/cache.js"; @@ -90,6 +91,7 @@ export default define(meta, paramDef, async (ps, user) => { filtered = await filterVisibility(filtered, user, followingUserIds); filtered = await filterMutedUser(filtered, user); filtered = await filterMutedNote(filtered, user); + filtered = await filterBlockedUser(filtered, user); return filtered; }; diff --git a/packages/backend/src/services/blocking/create.ts b/packages/backend/src/services/blocking/create.ts index 60bd6e9431..86467b9195 100644 --- a/packages/backend/src/services/blocking/create.ts +++ b/packages/backend/src/services/blocking/create.ts @@ -20,6 +20,7 @@ import { genId } from "@/misc/gen-id.js"; import { IdentifiableError } from "@/misc/identifiable-error.js"; import { getActiveWebhooks } from "@/misc/webhook-cache.js"; import { webhookDeliver } from "@/queue/index.js"; +import { UserBlockedCache } from "@/misc/cache"; export default async function (blocker: User, blockee: User) { await Promise.all([ @@ -41,6 +42,9 @@ export default async function (blocker: User, blockee: User) { await Blockings.insert(blocking); + const cache = await UserBlockedCache.init(blockee.id); + await cache.add(blocker.id); + if (Users.isLocalUser(blocker) && Users.isRemoteUser(blockee)) { const content = renderActivity(renderBlock(blocking)); deliver(blocker, content, blockee.inbox); diff --git a/packages/backend/src/services/blocking/delete.ts b/packages/backend/src/services/blocking/delete.ts index 67f1e76f0e..99666312cd 100644 --- a/packages/backend/src/services/blocking/delete.ts +++ b/packages/backend/src/services/blocking/delete.ts @@ -4,8 +4,8 @@ import renderUndo from "@/remote/activitypub/renderer/undo.js"; import { deliver } from "@/queue/index.js"; import Logger from "../logger.js"; import type { CacheableUser } from "@/models/entities/user.js"; -import { User } from "@/models/entities/user.js"; import { Blockings, Users } from "@/models/index.js"; +import { UserBlockedCache } from "@/misc/cache.js"; const logger = new Logger("blocking/delete"); @@ -15,6 +15,9 @@ export default async function (blocker: CacheableUser, blockee: CacheableUser) { blockeeId: blockee.id, }); + const cache = await UserBlockedCache.init(blockee.id); + await cache.delete(blocker.id); + if (blocking == null) { logger.warn( "ブロック解除がリクエストされましたがブロックしていませんでした",