From 5e9a8ce8833af5320094a9a625d73aef397ab842 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 21 Mar 2022 20:43:43 +0900 Subject: [PATCH] perf(server): reduce db query --- locales/ja-JP.yml | 1 + packages/backend/src/services/note/create.ts | 38 +++++++++++++------ .../client/src/pages/settings/word-mute.vue | 2 +- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index e2cf11b49..befa92bd3 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -840,6 +840,7 @@ tenMinutes: "10分" oneHour: "1時間" oneDay: "1日" oneWeek: "1週間" +reflectMayTakeTime: "反映されるまで時間がかかる場合があります。" _emailUnavailable: used: "既に使用されています" diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts index 8c5f13362..2e8b2ffa0 100644 --- a/packages/backend/src/services/note/create.ts +++ b/packages/backend/src/services/note/create.ts @@ -35,6 +35,12 @@ import { Channel } from '@/models/entities/channel.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { getAntennas } from '@/misc/antenna-cache.js'; import { endedPollNotificationQueue } from '@/queue/queues.js'; +import { Cache } from '@/misc/cache.js'; +import { UserProfile } from '@/models/entities/user-profile.js'; + +const usersCache = new Cache(Infinity); + +const mutedWordsCache = new Cache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5); type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; @@ -91,6 +97,13 @@ class NotificationManager { } } +type MinimumUser = { + id: User['id']; + host: User['host']; + username: User['username']; + uri: User['uri']; +}; + type Option = { createdAt?: Date | null; name?: string | null; @@ -102,9 +115,9 @@ type Option = { localOnly?: boolean | null; cw?: string | null; visibility?: string; - visibleUsers?: User[] | null; + visibleUsers?: MinimumUser[] | null; channel?: Channel | null; - apMentions?: User[] | null; + apMentions?: MinimumUser[] | null; apHashtags?: string[] | null; apEmojis?: string[] | null; uri?: string | null; @@ -199,7 +212,7 @@ export default async (user: { id: User['id']; username: User['username']; host: tags = tags.filter(tag => Array.from(tag || '').length <= 128).splice(0, 32); if (data.reply && (user.id !== data.reply.userId) && !mentionedUsers.some(u => u.id === data.reply!.userId)) { - mentionedUsers.push(await Users.findOneOrFail(data.reply.userId)); + mentionedUsers.push(await usersCache.fetch(data.reply.userId, () => Users.findOneOrFail(data.reply!.userId))); } if (data.visibility === 'specified') { @@ -212,7 +225,7 @@ export default async (user: { id: User['id']; username: User['username']; host: } if (data.reply && !data.visibleUsers.some(x => x.id === data.reply!.userId)) { - data.visibleUsers.push(await Users.findOneOrFail(data.reply.userId)); + data.visibleUsers.push(await usersCache.fetch(data.reply.userId, () => Users.findOneOrFail(data.reply!.userId))); } } @@ -241,10 +254,12 @@ export default async (user: { id: User['id']; username: User['username']; host: incNotesCountOfUser(user); // Word mute - // TODO: cache - UserProfiles.find({ - enableWordMute: true, - }).then(us => { + mutedWordsCache.fetch(null, () => UserProfiles.find({ + where: { + enableWordMute: true, + }, + select: ['userId', 'mutedWords'], + })).then(us => { for (const u of us) { checkWordMute(note, { id: u.userId }, u.mutedWords).then(shouldMute => { if (shouldMute) { @@ -260,11 +275,12 @@ export default async (user: { id: User['id']; username: User['username']; host: }); // Antenna + // TODO: キャッシュしたい Followings.createQueryBuilder('following') .andWhere(`following.followeeId = :userId`, { userId: note.userId }) .getMany() .then(async followings => { - const blockings = await Blockings.find({ blockerId: user.id }); // TODO: キャッシュしたい + const blockings = await Blockings.find({ blockerId: user.id }); const followers = followings.map(f => f.followerId); for (const antenna of (await getAntennas())) { if (blockings.some(blocking => blocking.blockeeId === antenna.userId)) continue; // この処理は checkHitAntenna 内でやるようにしてもいいかも @@ -465,7 +481,7 @@ function incRenoteCount(renote: Note) { .execute(); } -async function insertNote(user: { id: User['id']; host: User['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: User[]) { +async function insertNote(user: { id: User['id']; host: User['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]) { const insert = new Note({ id: genId(data.createdAt!), createdAt: data.createdAt!, @@ -597,7 +613,7 @@ async function notifyToWatchersOfReplyee(reply: Note, user: { id: User['id']; }, } } -async function createMentionedEvents(mentionedUsers: User[], note: Note, nm: NotificationManager) { +async function createMentionedEvents(mentionedUsers: MinimumUser[], note: Note, nm: NotificationManager) { for (const u of mentionedUsers.filter(u => Users.isLocalUser(u))) { const threadMuted = await NoteThreadMutings.findOne({ userId: u.id, diff --git a/packages/client/src/pages/settings/word-mute.vue b/packages/client/src/pages/settings/word-mute.vue index 2767e6351..c11707b6c 100644 --- a/packages/client/src/pages/settings/word-mute.vue +++ b/packages/client/src/pages/settings/word-mute.vue @@ -13,7 +13,7 @@
- {{ $ts._wordMute.hardDescription }} + {{ $ts._wordMute.hardDescription }} {{ $ts.reflectMayTakeTime }} {{ $ts._wordMute.muteWords }}