perf(server): Reduce database query

This commit is contained in:
syuilo 2021-03-22 12:41:33 +09:00
parent 1f0abef084
commit 8f41dfec2e
3 changed files with 53 additions and 1 deletions

View file

@ -1,7 +1,10 @@
import { In } from 'typeorm';
import { Emojis } from '../models'; import { Emojis } from '../models';
import { Emoji } from '../models/entities/emoji'; import { Emoji } from '../models/entities/emoji';
import { Note } from '../models/entities/note';
import { Cache } from './cache'; import { Cache } from './cache';
import { isSelfHost, toPunyNullable } from './convert-host'; import { isSelfHost, toPunyNullable } from './convert-host';
import { decodeReaction } from './reaction-lib';
const cache = new Cache<Emoji | null>(1000 * 60 * 60); const cache = new Cache<Emoji | null>(1000 * 60 * 60);
@ -70,3 +73,47 @@ export async function populateEmojis(emojiNames: string[], noteUserHost: string
return emojis.filter((x): x is PopulatedEmoji => x != null); return emojis.filter((x): x is PopulatedEmoji => x != null);
} }
export function aggregateNoteEmojis(notes: Note[]) {
let emojis: { name: string | null; host: string | null; }[] = [];
for (const note of notes) {
emojis = emojis.concat(note.emojis
.map(e => parseEmojiStr(e, note.userHost)));
if (note.renote) {
emojis = emojis.concat(note.renote.emojis
.map(e => parseEmojiStr(e, note.renote!.userHost)));
if (note.renote.user) {
emojis = emojis.concat(note.renote.user.emojis
.map(e => parseEmojiStr(e, note.renote!.userHost)));
}
}
const customReactions = Object.keys(note.reactions).map(x => decodeReaction(x)).filter(x => x.name != null) as typeof emojis;
emojis = emojis.concat(customReactions);
if (note.user) {
emojis = emojis.concat(note.user.emojis
.map(e => parseEmojiStr(e, note.userHost)));
}
}
return emojis.filter(x => x.name != null) as { name: string; host: string | null; }[];
}
/**
*
*/
export async function prefetchEmojis(emojis: { name: string; host: string | null; }[]): Promise<void> {
const notCachedEmojis = emojis.filter(emoji => cache.get(`${emoji.name} ${emoji.host}`) == null);
const emojisQuery: any[] = [];
const hosts = new Set(notCachedEmojis.map(e => e.host));
for (const host of hosts) {
emojisQuery.push({
name: In(notCachedEmojis.filter(e => e.host === host).map(e => e.name)),
host: host
});
}
const _emojis = emojisQuery.length > 0 ? await Emojis.find({
where: emojisQuery,
select: ['name', 'host', 'url']
}) : [];
for (const emoji of _emojis) {
cache.set(`${emoji.name} ${emoji.host}`, emoji);
}
}

View file

@ -8,7 +8,7 @@ import { convertLegacyReaction, convertLegacyReactions, decodeReaction } from '.
import { toString } from '../../mfm/to-string'; import { toString } from '../../mfm/to-string';
import { parse } from '../../mfm/parse'; import { parse } from '../../mfm/parse';
import { NoteReaction } from '../entities/note-reaction'; import { NoteReaction } from '../entities/note-reaction';
import { populateEmojis } from '../../misc/populate-emojis'; import { aggregateNoteEmojis, populateEmojis, prefetchEmojis } from '../../misc/populate-emojis';
export type PackedNote = SchemaType<typeof packedNoteSchema>; export type PackedNote = SchemaType<typeof packedNoteSchema>;
@ -259,6 +259,8 @@ export class NoteRepository extends Repository<Note> {
} }
} }
await prefetchEmojis(aggregateNoteEmojis(notes));
return await Promise.all(notes.map(n => this.pack(n, me, { return await Promise.all(notes.map(n => this.pack(n, me, {
...options, ...options,
_hint_: { _hint_: {

View file

@ -6,6 +6,7 @@ import { SchemaType } from '../../misc/schema';
import { Note } from '../entities/note'; import { Note } from '../entities/note';
import { NoteReaction } from '../entities/note-reaction'; import { NoteReaction } from '../entities/note-reaction';
import { User } from '../entities/user'; import { User } from '../entities/user';
import { aggregateNoteEmojis, prefetchEmojis } from '../../misc/populate-emojis';
export type PackedNotification = SchemaType<typeof packedNotificationSchema>; export type PackedNotification = SchemaType<typeof packedNotificationSchema>;
@ -98,6 +99,8 @@ export class NotificationRepository extends Repository<Notification> {
myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) || null); myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) || null);
} }
await prefetchEmojis(aggregateNoteEmojis(notes));
return await Promise.all(notifications.map(x => this.pack(x, { return await Promise.all(notifications.map(x => this.pack(x, {
_hintForEachNotes_: { _hintForEachNotes_: {
myReactions: myReactionsMap myReactions: myReactionsMap