fix: emoji cache
This commit is contained in:
parent
746e2fac41
commit
63c445554a
7 changed files with 58 additions and 22 deletions
|
@ -137,7 +137,7 @@ export const prepared = {
|
||||||
select: {
|
select: {
|
||||||
byNoteId: `SELECT * FROM reaction WHERE "noteId" IN ?`,
|
byNoteId: `SELECT * FROM reaction WHERE "noteId" IN ?`,
|
||||||
byUserId: `SELECT * FROM reaction_by_userid WHERE "userId" 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 ?`,
|
byId: `SELECT * FROM reaction WHERE "id" IN ?`,
|
||||||
},
|
},
|
||||||
delete: `DELETE FROM reaction WHERE "noteId" = ? AND "userId" = ?`,
|
delete: `DELETE FROM reaction WHERE "noteId" = ? AND "userId" = ?`,
|
||||||
|
|
|
@ -36,6 +36,12 @@ export class Cache<T> {
|
||||||
await commander.set(_key, _value, "EX", this.ttl);
|
await commander.set(_key, _value, "EX", this.ttl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async exists(...keys: string[]): Promise<boolean> {
|
||||||
|
return (
|
||||||
|
(await redisClient.exists(keys.map((key) => this.prefixedKey(key)))) > 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public async get(key: string | null, renew = false): Promise<T | undefined> {
|
public async get(key: string | null, renew = false): Promise<T | undefined> {
|
||||||
const _key = this.prefixedKey(key);
|
const _key = this.prefixedKey(key);
|
||||||
const cached = await redisClient.getBuffer(_key);
|
const cached = await redisClient.getBuffer(_key);
|
||||||
|
|
|
@ -152,9 +152,12 @@ export function aggregateNoteEmojis(notes: Note[]) {
|
||||||
export async function prefetchEmojis(
|
export async function prefetchEmojis(
|
||||||
emojis: { name: string; host: string | null }[],
|
emojis: { name: string; host: string | null }[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const notCachedEmojis = emojis.filter(
|
const notCachedEmojis: { name: string; host: string | null }[] = [];
|
||||||
async (emoji) => !(await EmojiCache.get(`${emoji.name} ${emoji.host}`)),
|
for (const emoji of emojis) {
|
||||||
);
|
if (!(await EmojiCache.exists(`${emoji.name} ${emoji.host}`))) {
|
||||||
|
notCachedEmojis.push(emoji);
|
||||||
|
}
|
||||||
|
}
|
||||||
const emojisQuery: any[] = [];
|
const emojisQuery: any[] = [];
|
||||||
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) {
|
||||||
|
|
|
@ -28,10 +28,11 @@ import {
|
||||||
import { db } from "@/db/postgre.js";
|
import { db } from "@/db/postgre.js";
|
||||||
import { IdentifiableError } from "@/misc/identifiable-error.js";
|
import { IdentifiableError } from "@/misc/identifiable-error.js";
|
||||||
import {
|
import {
|
||||||
ScyllaNote,
|
type ScyllaNote,
|
||||||
parseScyllaNote,
|
parseScyllaNote,
|
||||||
prepared,
|
prepared,
|
||||||
scyllaClient,
|
scyllaClient,
|
||||||
|
parseScyllaReaction,
|
||||||
} from "@/db/scylla.js";
|
} from "@/db/scylla.js";
|
||||||
import { LocalFollowingsCache } from "@/misc/cache.js";
|
import { LocalFollowingsCache } from "@/misc/cache.js";
|
||||||
import { userByIdCache } from "@/services/user-cache.js";
|
import { userByIdCache } from "@/services/user-cache.js";
|
||||||
|
@ -91,10 +92,22 @@ async function populateMyReaction(
|
||||||
// 実装上抜けがあるだけかもしれないので、「ヒントに含まれてなかったら(=undefinedなら)return」のようにはしない
|
// 実装上抜けがあるだけかもしれないので、「ヒントに含まれてなかったら(=undefinedなら)return」のようにはしない
|
||||||
}
|
}
|
||||||
|
|
||||||
const reaction = await NoteReactions.findOneBy({
|
let reaction: NoteReaction | null = null;
|
||||||
userId: meId,
|
if (scyllaClient) {
|
||||||
noteId: note.id,
|
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) {
|
if (reaction) {
|
||||||
return convertLegacyReaction(reaction.reaction);
|
return convertLegacyReaction(reaction.reaction);
|
||||||
|
@ -358,10 +371,20 @@ export const NoteRepository = db.getRepository(Note).extend({
|
||||||
.filter((n) => !!n.renoteId)
|
.filter((n) => !!n.renoteId)
|
||||||
.map((n) => n.renoteId) as string[];
|
.map((n) => n.renoteId) as string[];
|
||||||
const targets = [...notes.map((n) => n.id), ...renoteIds];
|
const targets = [...notes.map((n) => n.id), ...renoteIds];
|
||||||
const myReactions = await NoteReactions.findBy({
|
let myReactions: NoteReaction[] = [];
|
||||||
userId: meId,
|
if (scyllaClient) {
|
||||||
noteId: In(targets),
|
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) {
|
for (const target of targets) {
|
||||||
myReactionsMap.set(
|
myReactionsMap.set(
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import type { FindOptionsWhere } from "typeorm";
|
import type { FindOptionsWhere } from "typeorm";
|
||||||
import { DeepPartial } from "typeorm";
|
|
||||||
import { NoteReactions } from "@/models/index.js";
|
import { NoteReactions } from "@/models/index.js";
|
||||||
import type { NoteReaction } from "@/models/entities/note-reaction.js";
|
import type { NoteReaction } from "@/models/entities/note-reaction.js";
|
||||||
import define from "../../define.js";
|
import define from "../../define.js";
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Notes } from "@/models/index.js";
|
||||||
import define from "../../define.js";
|
import define from "../../define.js";
|
||||||
import { getNote } from "../../common/getters.js";
|
import { getNote } from "../../common/getters.js";
|
||||||
import { ApiError } from "../../error.js";
|
import { ApiError } from "../../error.js";
|
||||||
|
import { scyllaClient } from "@/db/scylla.js";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["notes"],
|
tags: ["notes"],
|
||||||
|
@ -44,6 +45,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
return await Notes.pack(note, user, {
|
return await Notes.pack(note, user, {
|
||||||
// FIXME: packing with detail may throw an error if the reply or renote is not visible (#8774)
|
// FIXME: packing with detail may throw an error if the reply or renote is not visible (#8774)
|
||||||
detail: true,
|
detail: true,
|
||||||
|
scyllaNote: !!scyllaClient
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
if (err.id === "9725d0ce-ba28-4dde-95a7-2cbb2c15de24")
|
if (err.id === "9725d0ce-ba28-4dde-95a7-2cbb2c15de24")
|
||||||
throw new ApiError(meta.errors.noSuchNote);
|
throw new ApiError(meta.errors.noSuchNote);
|
||||||
|
|
|
@ -22,7 +22,7 @@ import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js
|
||||||
import type { NoteReaction } from "@/models/entities/note-reaction.js";
|
import type { NoteReaction } from "@/models/entities/note-reaction.js";
|
||||||
import { IdentifiableError } from "@/misc/identifiable-error.js";
|
import { IdentifiableError } from "@/misc/identifiable-error.js";
|
||||||
import { prepared, scyllaClient } from "@/db/scylla.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 (
|
export default async (
|
||||||
user: { id: User["id"]; host: User["host"] },
|
user: { id: User["id"]; host: User["host"] },
|
||||||
|
@ -137,13 +137,16 @@ export default async (
|
||||||
// カスタム絵文字リアクションだったら絵文字情報も送る
|
// カスタム絵文字リアクションだったら絵文字情報も送る
|
||||||
const decodedReaction = decodeReaction(_reaction);
|
const decodedReaction = decodeReaction(_reaction);
|
||||||
|
|
||||||
const emoji = await Emojis.findOne({
|
const emoji = await EmojiCache.fetch(
|
||||||
where: {
|
`${decodedReaction.name} ${decodedReaction.host}`,
|
||||||
name: decodedReaction.name,
|
() =>
|
||||||
host: decodedReaction.host ?? IsNull(),
|
Emojis.findOne({
|
||||||
},
|
where: {
|
||||||
select: ["name", "host", "originalUrl", "publicUrl"],
|
name: decodedReaction.name,
|
||||||
});
|
host: decodedReaction.host ?? IsNull(),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
publishNoteStream(note.id, "reacted", {
|
publishNoteStream(note.id, "reacted", {
|
||||||
reaction: decodedReaction.reaction,
|
reaction: decodedReaction.reaction,
|
||||||
|
|
Loading…
Reference in a new issue