use cache in NotificationEntityService

This commit is contained in:
Hazelnoot 2025-09-18 20:25:42 -04:00
parent b3b08e2315
commit 3fc17fd0ee
4 changed files with 36 additions and 36 deletions

View file

@ -95,7 +95,10 @@ export class NotificationService implements OnApplicationShutdown {
data: Omit<FilterUnionByProperty<MiNotification, 'type', T>, 'type' | 'id' | 'createdAt' | 'notifierId'>,
notifierId?: MiUser['id'] | null,
): Promise<MiNotification | null> {
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;

View file

@ -69,7 +69,7 @@ export class NotificationEntityService implements OnModuleInit {
*/
async #packInternal <T extends MiNotification | MiGroupedNotification> (
src: T,
meId: MiUser['id'],
me: MiUser,
options: {
checkValidNotifier?: boolean;
},
@ -80,13 +80,13 @@ export class NotificationEntityService implements OnModuleInit {
): Promise<Packed<'Notification'> | 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 <T extends MiNotification | MiGroupedNotification> (
notifications: T[],
meId: MiUser['id'],
me: MiUser,
): Promise<T[]> {
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<MiUser['id'], Packed<'UserLite'>>;
},
): Promise<Packed<'Notification'> | 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<MiNotification[]> {
return await this.#packManyInternal(notifications, meId);
return await this.#packManyInternal(notifications, me);
}
@bindThis
public async packGroupedMany(
notifications: MiGroupedNotification[],
meId: MiUser['id'],
me: MiUser,
): Promise<MiGroupedNotification[]> {
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<MiUser['id']>,
userMutedInstances: Set<string>,
notifiers: MiUser[],
notifiers: Map<string, MiUser>,
): 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<T[]> {
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;

View file

@ -191,7 +191,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // 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);
});
}
}

View file

@ -95,7 +95,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
this.notificationService.readAllNotification(me.id);
}
return await this.notificationEntityService.packMany(notifications, me.id);
return await this.notificationEntityService.packMany(notifications, me);
});
}
}