perf(server): reduce db query
This commit is contained in:
		
							parent
							
								
									81ee9025fb
								
							
						
					
					
						commit
						ff19640171
					
				
					 3 changed files with 29 additions and 12 deletions
				
			
		|  | @ -840,6 +840,7 @@ tenMinutes: "10分" | ||||||
| oneHour: "1時間" | oneHour: "1時間" | ||||||
| oneDay: "1日" | oneDay: "1日" | ||||||
| oneWeek: "1週間" | oneWeek: "1週間" | ||||||
|  | reflectMayTakeTime: "反映されるまで時間がかかる場合があります。" | ||||||
| 
 | 
 | ||||||
| _emailUnavailable: | _emailUnavailable: | ||||||
|   used: "既に使用されています" |   used: "既に使用されています" | ||||||
|  |  | ||||||
|  | @ -35,6 +35,12 @@ import { Channel } from '@/models/entities/channel.js'; | ||||||
| import { normalizeForSearch } from '@/misc/normalize-for-search.js'; | import { normalizeForSearch } from '@/misc/normalize-for-search.js'; | ||||||
| import { getAntennas } from '@/misc/antenna-cache.js'; | import { getAntennas } from '@/misc/antenna-cache.js'; | ||||||
| import { endedPollNotificationQueue } from '@/queue/queues.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<MinimumUser>(Infinity); | ||||||
|  | 
 | ||||||
|  | const mutedWordsCache = new Cache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5); | ||||||
| 
 | 
 | ||||||
| type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; | 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 = { | type Option = { | ||||||
| 	createdAt?: Date | null; | 	createdAt?: Date | null; | ||||||
| 	name?: string | null; | 	name?: string | null; | ||||||
|  | @ -102,9 +115,9 @@ type Option = { | ||||||
| 	localOnly?: boolean | null; | 	localOnly?: boolean | null; | ||||||
| 	cw?: string | null; | 	cw?: string | null; | ||||||
| 	visibility?: string; | 	visibility?: string; | ||||||
| 	visibleUsers?: User[] | null; | 	visibleUsers?: MinimumUser[] | null; | ||||||
| 	channel?: Channel | null; | 	channel?: Channel | null; | ||||||
| 	apMentions?: User[] | null; | 	apMentions?: MinimumUser[] | null; | ||||||
| 	apHashtags?: string[] | null; | 	apHashtags?: string[] | null; | ||||||
| 	apEmojis?: string[] | null; | 	apEmojis?: string[] | null; | ||||||
| 	uri?: 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); | 	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)) { | 	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') { | 	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)) { | 		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); | 	incNotesCountOfUser(user); | ||||||
| 
 | 
 | ||||||
| 	// Word mute
 | 	// Word mute
 | ||||||
| 	// TODO: cache
 | 	mutedWordsCache.fetch(null, () => UserProfiles.find({ | ||||||
| 	UserProfiles.find({ | 		where: { | ||||||
| 		enableWordMute: true, | 			enableWordMute: true, | ||||||
| 	}).then(us => { | 		}, | ||||||
|  | 		select: ['userId', 'mutedWords'], | ||||||
|  | 	})).then(us => { | ||||||
| 		for (const u of us) { | 		for (const u of us) { | ||||||
| 			checkWordMute(note, { id: u.userId }, u.mutedWords).then(shouldMute => { | 			checkWordMute(note, { id: u.userId }, u.mutedWords).then(shouldMute => { | ||||||
| 				if (shouldMute) { | 				if (shouldMute) { | ||||||
|  | @ -260,11 +275,12 @@ export default async (user: { id: User['id']; username: User['username']; host: | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	// Antenna
 | 	// Antenna
 | ||||||
|  | 	 // TODO: キャッシュしたい
 | ||||||
| 	Followings.createQueryBuilder('following') | 	Followings.createQueryBuilder('following') | ||||||
| 		.andWhere(`following.followeeId = :userId`, { userId: note.userId }) | 		.andWhere(`following.followeeId = :userId`, { userId: note.userId }) | ||||||
| 		.getMany() | 		.getMany() | ||||||
| 		.then(async followings => { | 		.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); | 			const followers = followings.map(f => f.followerId); | ||||||
| 			for (const antenna of (await getAntennas())) { | 			for (const antenna of (await getAntennas())) { | ||||||
| 				if (blockings.some(blocking => blocking.blockeeId === antenna.userId)) continue; // この処理は checkHitAntenna 内でやるようにしてもいいかも
 | 				if (blockings.some(blocking => blocking.blockeeId === antenna.userId)) continue; // この処理は checkHitAntenna 内でやるようにしてもいいかも
 | ||||||
|  | @ -465,7 +481,7 @@ function incRenoteCount(renote: Note) { | ||||||
| 		.execute(); | 		.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({ | 	const insert = new Note({ | ||||||
| 		id: genId(data.createdAt!), | 		id: genId(data.createdAt!), | ||||||
| 		createdAt: 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))) { | 	for (const u of mentionedUsers.filter(u => Users.isLocalUser(u))) { | ||||||
| 		const threadMuted = await NoteThreadMutings.findOne({ | 		const threadMuted = await NoteThreadMutings.findOne({ | ||||||
| 			userId: u.id, | 			userId: u.id, | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ | ||||||
| 			</FormTextarea> | 			</FormTextarea> | ||||||
| 		</div> | 		</div> | ||||||
| 		<div v-show="tab === 'hard'"> | 		<div v-show="tab === 'hard'"> | ||||||
| 			<MkInfo class="_formBlock">{{ $ts._wordMute.hardDescription }}</MkInfo> | 			<MkInfo class="_formBlock">{{ $ts._wordMute.hardDescription }} {{ $ts.reflectMayTakeTime }}</MkInfo> | ||||||
| 			<FormTextarea v-model="hardMutedWords" class="_formBlock"> | 			<FormTextarea v-model="hardMutedWords" class="_formBlock"> | ||||||
| 				<span>{{ $ts._wordMute.muteWords }}</span> | 				<span>{{ $ts._wordMute.muteWords }}</span> | ||||||
| 				<template #caption>{{ $ts._wordMute.muteWordsDescription }}<br>{{ $ts._wordMute.muteWordsDescription2 }}</template> | 				<template #caption>{{ $ts._wordMute.muteWordsDescription }}<br>{{ $ts._wordMute.muteWordsDescription2 }}</template> | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue