server: refactor prefetchEmojis

Exiting earlier might slightly improve performance.
This commit is contained in:
Johann150 2022-11-13 18:24:15 +01:00
parent 8d6476af2a
commit 131c12a30b
Signed by untrusted user: Johann150
GPG key ID: 9EE6577A2A06F8F1

View file

@ -11,7 +11,7 @@ import { decodeReaction } from './reaction-lib.js';
const cache = new Cache<Emoji | null>(1000 * 60 * 60 * 12); const cache = new Cache<Emoji | null>(1000 * 60 * 60 * 12);
/** /**
* * Information needed to attach in ActivityPub
*/ */
type PopulatedEmoji = { type PopulatedEmoji = {
name: string; name: string;
@ -36,17 +36,16 @@ function parseEmojiStr(emojiName: string, noteUserHost: string | null) {
const name = match[1]; const name = match[1];
// ホスト正規化
const host = toPunyNullable(normalizeHost(match[2], noteUserHost)); const host = toPunyNullable(normalizeHost(match[2], noteUserHost));
return { name, host }; return { name, host };
} }
/** /**
* * Resolve emoji information from ActivityPub attachment.
* @param emojiName (:, @. (decodeReactionで可能)) * @param emojiName custom emoji names attached to notes, user profiles or in rections. Colons should not be included. Localhost is denote by @. (see also `decodeReaction`)
* @param noteUserHost * @param noteUserHost host that the content is from, to default to
* @returns , nullは未マッチを意味する * @returns emoji information. `null` means not found.
*/ */
export async function populateEmoji(emojiName: string, noteUserHost: string | null): Promise<PopulatedEmoji | null> { export async function populateEmoji(emojiName: string, noteUserHost: string | null): Promise<PopulatedEmoji | null> {
const { name, host } = parseEmojiStr(emojiName, noteUserHost); const { name, host } = parseEmojiStr(emojiName, noteUserHost);
@ -72,7 +71,7 @@ export async function populateEmoji(emojiName: string, noteUserHost: string | nu
} }
/** /**
* (, ) * Retrieve list of emojis from the cache. Uncached emoji are dropped.
*/ */
export async function populateEmojis(emojiNames: string[], noteUserHost: string | null): Promise<PopulatedEmoji[]> { export async function populateEmojis(emojiNames: string[], noteUserHost: string | null): Promise<PopulatedEmoji[]> {
const emojis = await Promise.all(emojiNames.map(x => populateEmoji(x, noteUserHost))); const emojis = await Promise.all(emojiNames.map(x => populateEmoji(x, noteUserHost)));
@ -103,11 +102,17 @@ export function aggregateNoteEmojis(notes: Note[]) {
} }
/** /**
* * Query list of emojis in bulk and add them to the cache.
*/ */
export async function prefetchEmojis(emojis: { name: string; host: string | null; }[]): Promise<void> { 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 notCachedEmojis = emojis.filter(emoji => cache.get(`${emoji.name} ${emoji.host}`) == null);
// check if there even are any uncached emoji to handle
if (notCachedEmojis.length === 0) return;
// query all uncached emoji
const emojisQuery: any[] = []; const emojisQuery: any[] = [];
// group by hosts to try to reduce query size
const hosts = new Set(notCachedEmojis.map(e => e.host)); const hosts = new Set(notCachedEmojis.map(e => e.host));
for (const host of hosts) { for (const host of hosts) {
emojisQuery.push({ emojisQuery.push({
@ -115,11 +120,14 @@ export async function prefetchEmojis(emojis: { name: string; host: string | null
host: host ?? IsNull(), host: host ?? IsNull(),
}); });
} }
const _emojis = emojisQuery.length > 0 ? await Emojis.find({
await Emojis.find({
where: emojisQuery, where: emojisQuery,
select: ['name', 'host', 'originalUrl', 'publicUrl'], select: ['name', 'host', 'originalUrl', 'publicUrl'],
}) : []; }).then(emojis => {
for (const emoji of _emojis) { // store all emojis into the cache
cache.set(`${emoji.name} ${emoji.host}`, emoji); emojis.forEach(emoji => {
} cache.set(`${emoji.name} ${emoji.host}`, emoji);
});
});
} }