From 63c445554a008fcc9a3d7b77739c3d209d38c73b Mon Sep 17 00:00:00 2001 From: Namekuji Date: Wed, 9 Aug 2023 08:15:26 -0400 Subject: [PATCH] fix: emoji cache --- packages/backend/src/db/scylla.ts | 2 +- packages/backend/src/misc/cache.ts | 6 +++ packages/backend/src/misc/populate-emojis.ts | 9 ++-- .../backend/src/models/repositories/note.ts | 41 +++++++++++++++---- .../server/api/endpoints/notes/reactions.ts | 1 - .../src/server/api/endpoints/notes/show.ts | 2 + .../src/services/note/reaction/create.ts | 19 +++++---- 7 files changed, 58 insertions(+), 22 deletions(-) diff --git a/packages/backend/src/db/scylla.ts b/packages/backend/src/db/scylla.ts index 9aa19f680a..9a76bda85c 100644 --- a/packages/backend/src/db/scylla.ts +++ b/packages/backend/src/db/scylla.ts @@ -137,7 +137,7 @@ export const prepared = { select: { byNoteId: `SELECT * FROM reaction WHERE "noteId" IN ?`, byUserId: `SELECT * FROM reaction_by_userid WHERE "userId" IN ?`, - byNoteAndUser: `SELECT * FROM reaction WHERE "noteId" = ? AND "userId" = ?`, + byNoteAndUser: `SELECT * FROM reaction WHERE "noteId" IN ? AND "userId" IN ?`, byId: `SELECT * FROM reaction WHERE "id" IN ?`, }, delete: `DELETE FROM reaction WHERE "noteId" = ? AND "userId" = ?`, diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts index 818edb3279..83d21bef68 100644 --- a/packages/backend/src/misc/cache.ts +++ b/packages/backend/src/misc/cache.ts @@ -36,6 +36,12 @@ export class Cache { await commander.set(_key, _value, "EX", this.ttl); } + public async exists(...keys: string[]): Promise { + return ( + (await redisClient.exists(keys.map((key) => this.prefixedKey(key)))) > 0 + ); + } + public async get(key: string | null, renew = false): Promise { const _key = this.prefixedKey(key); const cached = await redisClient.getBuffer(_key); diff --git a/packages/backend/src/misc/populate-emojis.ts b/packages/backend/src/misc/populate-emojis.ts index 8b9ab35534..bce734f5d3 100644 --- a/packages/backend/src/misc/populate-emojis.ts +++ b/packages/backend/src/misc/populate-emojis.ts @@ -152,9 +152,12 @@ export function aggregateNoteEmojis(notes: Note[]) { export async function prefetchEmojis( emojis: { name: string; host: string | null }[], ): Promise { - const notCachedEmojis = emojis.filter( - async (emoji) => !(await EmojiCache.get(`${emoji.name} ${emoji.host}`)), - ); + const notCachedEmojis: { name: string; host: string | null }[] = []; + for (const emoji of emojis) { + if (!(await EmojiCache.exists(`${emoji.name} ${emoji.host}`))) { + notCachedEmojis.push(emoji); + } + } const emojisQuery: any[] = []; const hosts = new Set(notCachedEmojis.map((e) => e.host)); for (const host of hosts) { diff --git a/packages/backend/src/models/repositories/note.ts b/packages/backend/src/models/repositories/note.ts index ba7b9937ac..26d4d9e508 100644 --- a/packages/backend/src/models/repositories/note.ts +++ b/packages/backend/src/models/repositories/note.ts @@ -28,10 +28,11 @@ import { import { db } from "@/db/postgre.js"; import { IdentifiableError } from "@/misc/identifiable-error.js"; import { - ScyllaNote, + type ScyllaNote, parseScyllaNote, prepared, scyllaClient, + parseScyllaReaction, } from "@/db/scylla.js"; import { LocalFollowingsCache } from "@/misc/cache.js"; import { userByIdCache } from "@/services/user-cache.js"; @@ -91,10 +92,22 @@ async function populateMyReaction( // 実装上抜けがあるだけかもしれないので、「ヒントに含まれてなかったら(=undefinedなら)return」のようにはしない } - const reaction = await NoteReactions.findOneBy({ - userId: meId, - noteId: note.id, - }); + let reaction: NoteReaction | null = null; + if (scyllaClient) { + const result = await scyllaClient.execute( + prepared.reaction.select.byNoteAndUser, + [[note.id], [meId]], + { prepare: true }, + ); + if (result.rowLength > 0) { + reaction = parseScyllaReaction(result.first()); + } + } else { + reaction = await NoteReactions.findOneBy({ + userId: meId, + noteId: note.id, + }); + } if (reaction) { return convertLegacyReaction(reaction.reaction); @@ -358,10 +371,20 @@ export const NoteRepository = db.getRepository(Note).extend({ .filter((n) => !!n.renoteId) .map((n) => n.renoteId) as string[]; const targets = [...notes.map((n) => n.id), ...renoteIds]; - const myReactions = await NoteReactions.findBy({ - userId: meId, - noteId: In(targets), - }); + let myReactions: NoteReaction[] = []; + if (scyllaClient) { + const result = await scyllaClient.execute( + prepared.reaction.select.byNoteAndUser, + [targets, [meId]], + { prepare: true }, + ); + myReactions = result.rows.map(parseScyllaReaction); + } else { + myReactions = await NoteReactions.findBy({ + userId: meId, + noteId: In(targets), + }); + } for (const target of targets) { myReactionsMap.set( diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts index 3c8af119ab..6ff7548355 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts @@ -1,5 +1,4 @@ import type { FindOptionsWhere } from "typeorm"; -import { DeepPartial } from "typeorm"; import { NoteReactions } from "@/models/index.js"; import type { NoteReaction } from "@/models/entities/note-reaction.js"; import define from "../../define.js"; diff --git a/packages/backend/src/server/api/endpoints/notes/show.ts b/packages/backend/src/server/api/endpoints/notes/show.ts index 8c5f91c5c1..9733e76846 100644 --- a/packages/backend/src/server/api/endpoints/notes/show.ts +++ b/packages/backend/src/server/api/endpoints/notes/show.ts @@ -2,6 +2,7 @@ import { Notes } from "@/models/index.js"; import define from "../../define.js"; import { getNote } from "../../common/getters.js"; import { ApiError } from "../../error.js"; +import { scyllaClient } from "@/db/scylla.js"; export const meta = { tags: ["notes"], @@ -44,6 +45,7 @@ export default define(meta, paramDef, async (ps, user) => { return await Notes.pack(note, user, { // FIXME: packing with detail may throw an error if the reply or renote is not visible (#8774) detail: true, + scyllaNote: !!scyllaClient }).catch((err) => { if (err.id === "9725d0ce-ba28-4dde-95a7-2cbb2c15de24") throw new ApiError(meta.errors.noSuchNote); diff --git a/packages/backend/src/services/note/reaction/create.ts b/packages/backend/src/services/note/reaction/create.ts index a5ef8670b4..935f9361ff 100644 --- a/packages/backend/src/services/note/reaction/create.ts +++ b/packages/backend/src/services/note/reaction/create.ts @@ -22,7 +22,7 @@ import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js import type { NoteReaction } from "@/models/entities/note-reaction.js"; import { IdentifiableError } from "@/misc/identifiable-error.js"; import { prepared, scyllaClient } from "@/db/scylla.js"; -import { populateEmojis } from "@/misc/populate-emojis.js"; +import { EmojiCache } from "@/misc/populate-emojis.js"; export default async ( user: { id: User["id"]; host: User["host"] }, @@ -137,13 +137,16 @@ export default async ( // カスタム絵文字リアクションだったら絵文字情報も送る const decodedReaction = decodeReaction(_reaction); - const emoji = await Emojis.findOne({ - where: { - name: decodedReaction.name, - host: decodedReaction.host ?? IsNull(), - }, - select: ["name", "host", "originalUrl", "publicUrl"], - }); + const emoji = await EmojiCache.fetch( + `${decodedReaction.name} ${decodedReaction.host}`, + () => + Emojis.findOne({ + where: { + name: decodedReaction.name, + host: decodedReaction.host ?? IsNull(), + }, + }), + ); publishNoteStream(note.id, "reacted", { reaction: decodedReaction.reaction,