From 3fc17fd0ee4626ccd76c463433b848cc486813ab Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Thu, 18 Sep 2025 20:25:42 -0400 Subject: [PATCH] use cache in NotificationEntityService --- .../backend/src/core/NotificationService.ts | 11 ++-- .../entities/NotificationEntityService.ts | 57 +++++++++---------- .../api/endpoints/i/notifications-grouped.ts | 2 +- .../server/api/endpoints/i/notifications.ts | 2 +- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts index 0f66087602..7d718494af 100644 --- a/packages/backend/src/core/NotificationService.ts +++ b/packages/backend/src/core/NotificationService.ts @@ -95,7 +95,10 @@ export class NotificationService implements OnApplicationShutdown { data: Omit, 'type' | 'id' | 'createdAt' | 'notifierId'>, notifierId?: MiUser['id'] | null, ): Promise { - const profile = await this.cacheService.userProfileCache.fetch(notifieeId); + const [profile, notifiee] = await Promise.all([ + this.cacheService.userProfileCache.fetch(notifieeId), + this.cacheService.findUserById(notifieeId), + ]); // 古いMisskeyバージョンのキャッシュが残っている可能性がある // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition @@ -179,7 +182,7 @@ export class NotificationService implements OnApplicationShutdown { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition } while (true); - const packed = await this.notificationEntityService.pack(notification, notifieeId, {}); + const packed = await this.notificationEntityService.pack(notification, notifiee, {}); if (packed == null) return null; @@ -196,8 +199,8 @@ export class NotificationService implements OnApplicationShutdown { this.globalEventService.publishMainStream(notifieeId, 'unreadNotification', packed); this.pushNotificationService.pushNotification(notifieeId, 'notification', packed); - if (type === 'follow') this.emailNotificationFollow(notifieeId, await this.usersRepository.findOneByOrFail({ id: notifierId! })); - if (type === 'receiveFollowRequest') this.emailNotificationReceiveFollowRequest(notifieeId, await this.usersRepository.findOneByOrFail({ id: notifierId! })); + if (type === 'follow') this.emailNotificationFollow(notifieeId, await this.cacheService.findUserById(notifierId!)); + if (type === 'receiveFollowRequest') this.emailNotificationReceiveFollowRequest(notifieeId, await this.cacheService.findUserById(notifierId!)); }, () => { /* aborted, ignore it */ }); return notification; diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index 0e45807ad7..264d66aec1 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -69,7 +69,7 @@ export class NotificationEntityService implements OnModuleInit { */ async #packInternal ( src: T, - meId: MiUser['id'], + me: MiUser, options: { checkValidNotifier?: boolean; }, @@ -80,13 +80,13 @@ export class NotificationEntityService implements OnModuleInit { ): Promise | null> { const notification = src; - if (options.checkValidNotifier !== false && !(await this.#isValidNotifier(notification, meId))) return null; + if (options.checkValidNotifier !== false && !(await this.#isValidNotifier(notification, me.id))) return null; const needsNote = NOTE_REQUIRED_NOTIFICATION_TYPES.has(notification.type) && 'noteId' in notification; const noteIfNeed = needsNote ? ( hint?.packedNotes != null ? hint.packedNotes.get(notification.noteId) - : undefOnMissing(this.noteEntityService.pack(notification.noteId, { id: meId }, { + : undefOnMissing(this.noteEntityService.pack(notification.noteId, me, { detail: true, })) ) : undefined; @@ -97,7 +97,7 @@ export class NotificationEntityService implements OnModuleInit { const userIfNeed = needsUser ? ( hint?.packedUsers != null ? hint.packedUsers.get(notification.notifierId) - : undefOnMissing(this.userEntityService.pack(notification.notifierId, { id: meId })) + : undefOnMissing(this.userEntityService.pack(notification.notifierId, me)) ) : undefined; // if the user has been deleted, don't show this notification if (needsUser && !userIfNeed) return null; @@ -107,7 +107,7 @@ export class NotificationEntityService implements OnModuleInit { const reactions = (await Promise.all(notification.reactions.map(async reaction => { const user = hint?.packedUsers != null ? hint.packedUsers.get(reaction.userId)! - : await undefOnMissing(this.userEntityService.pack(reaction.userId, { id: meId })); + : await undefOnMissing(this.userEntityService.pack(reaction.userId, me)); return { user, reaction: reaction.reaction, @@ -132,7 +132,7 @@ export class NotificationEntityService implements OnModuleInit { return packedUser; } - return undefOnMissing(this.userEntityService.pack(userId, { id: meId })); + return undefOnMissing(this.userEntityService.pack(userId, me)); }))).filter(x => x != null); // if all users have been deleted, don't show this notification if (users.length === 0) { @@ -159,7 +159,7 @@ export class NotificationEntityService implements OnModuleInit { } const needsChatRoomInvitation = notification.type === 'chatRoomInvitationReceived'; - const chatRoomInvitation = needsChatRoomInvitation ? await this.chatEntityService.packRoomInvitation(notification.invitationId, { id: meId }).catch(() => null) : undefined; + const chatRoomInvitation = needsChatRoomInvitation ? await this.chatEntityService.packRoomInvitation(notification.invitationId, me).catch(() => null) : undefined; // if the invitation has been deleted, don't show this notification if (needsChatRoomInvitation && !chatRoomInvitation) { return null; @@ -212,20 +212,20 @@ export class NotificationEntityService implements OnModuleInit { async #packManyInternal ( notifications: T[], - meId: MiUser['id'], + me: MiUser, ): Promise { if (notifications.length === 0) return []; let validNotifications = notifications; - validNotifications = await this.#filterValidNotifier(validNotifications, meId); + validNotifications = await this.#filterValidNotifier(validNotifications, me.id); const noteIds = validNotifications.map(x => 'noteId' in x ? x.noteId : null).filter(x => x != null); const notes = noteIds.length > 0 ? await this.notesRepository.find({ where: { id: In(noteIds) }, - relations: ['user', 'reply', 'reply.user', 'renote', 'renote.user'], + relations: ['user', 'reply', 'reply.user', 'renote', 'renote.user', 'renote.reply'], }) : []; - const packedNotesArray = await this.noteEntityService.packMany(notes, { id: meId }, { + const packedNotesArray = await this.noteEntityService.packMany(notes, me, { detail: true, }); const packedNotes = new Map(packedNotesArray.map(p => [p.id, p])); @@ -238,10 +238,8 @@ export class NotificationEntityService implements OnModuleInit { if (notification.type === 'reaction:grouped') userIds.push(...notification.reactions.map(x => x.userId)); if (notification.type === 'renote:grouped') userIds.push(...notification.userIds); } - const users = userIds.length > 0 ? await this.usersRepository.find({ - where: { id: In(userIds) }, - }) : []; - const packedUsersArray = await this.userEntityService.packMany(users, { id: meId }); + const users = await this.cacheService.getUsers(userIds); + const packedUsersArray = await this.userEntityService.packMany(Array.from(users.values()), me); const packedUsers = new Map(packedUsersArray.map(p => [p.id, p])); // 既に解決されたフォローリクエストの通知を除外 @@ -256,7 +254,7 @@ export class NotificationEntityService implements OnModuleInit { const packPromises = validNotifications.map(x => { return this.pack( x, - meId, + me, { checkValidNotifier: false }, { packedNotes, packedUsers }, ); @@ -268,7 +266,7 @@ export class NotificationEntityService implements OnModuleInit { @bindThis public async pack( src: MiNotification | MiGroupedNotification, - meId: MiUser['id'], + me: MiUser, options: { checkValidNotifier?: boolean; @@ -278,23 +276,23 @@ export class NotificationEntityService implements OnModuleInit { packedUsers: Map>; }, ): Promise | null> { - return await this.#packInternal(src, meId, options, hint); + return await this.#packInternal(src, me, options, hint); } @bindThis public async packMany( notifications: MiNotification[], - meId: MiUser['id'], + me: MiUser, ): Promise { - return await this.#packManyInternal(notifications, meId); + return await this.#packManyInternal(notifications, me); } @bindThis public async packGroupedMany( notifications: MiGroupedNotification[], - meId: MiUser['id'], + me: MiUser, ): Promise { - return await this.#packManyInternal(notifications, meId); + return await this.#packManyInternal(notifications, me); } /** @@ -304,12 +302,12 @@ export class NotificationEntityService implements OnModuleInit { notification: T, userIdsWhoMeMuting: Set, userMutedInstances: Set, - notifiers: MiUser[], + notifiers: Map, ): boolean { if (!('notifierId' in notification)) return true; if (userIdsWhoMeMuting.has(notification.notifierId)) return false; - const notifier = notifiers.find(x => x.id === notification.notifierId) ?? null; + const notifier = notifiers.get(notification.notifierId) ?? null; if (notifier == null) return false; if (notifier.host && userMutedInstances.has(notifier.host)) return false; @@ -336,19 +334,18 @@ export class NotificationEntityService implements OnModuleInit { notifications: T[], meId: MiUser['id'], ): Promise { + const notifierIds = notifications.map(notification => 'notifierId' in notification ? notification.notifierId : null).filter(x => x != null); + const [ userIdsWhoMeMuting, userMutedInstances, + notifiers, ] = await Promise.all([ this.cacheService.userMutingsCache.fetch(meId), - this.cacheService.userProfileCache.fetch(meId).then(p => new Set(p.mutedInstances)), + this.cacheService.userMutingsCache.fetch(meId), + this.cacheService.getUsers(notifierIds), ]); - const notifierIds = notifications.map(notification => 'notifierId' in notification ? notification.notifierId : null).filter(x => x != null); - const notifiers = notifierIds.length > 0 ? await this.usersRepository.find({ - where: { id: In(notifierIds) }, - }) : []; - const filteredNotifications = ((await Promise.all(notifications.map(async (notification) => { const isValid = this.#validateNotifier(notification, userIdsWhoMeMuting, userMutedInstances, notifiers); return isValid ? notification : null; diff --git a/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts b/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts index 3821b5a20e..4fd962ea4c 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts @@ -191,7 +191,7 @@ export default class extends Endpoint { // eslint- // this matches the logic in NotificationService and it's what MkPagination expects if (ps.sinceId && !ps.untilId) groupedNotifications.reverse(); - return await this.notificationEntityService.packGroupedMany(groupedNotifications, me.id); + return await this.notificationEntityService.packGroupedMany(groupedNotifications, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index f5a48b2f69..c6aeaeb18f 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -95,7 +95,7 @@ export default class extends Endpoint { // eslint- this.notificationService.readAllNotification(me.id); } - return await this.notificationEntityService.packMany(notifications, me.id); + return await this.notificationEntityService.packMany(notifications, me); }); } }