perf: cache renote muting

This commit is contained in:
Namekuji 2023-08-07 04:57:18 -04:00
parent 3779b77c25
commit 090bb9a2c3
No known key found for this signature in database
GPG key ID: 1D62332C07FBA532
6 changed files with 55 additions and 18 deletions

View file

@ -9,6 +9,7 @@ import {
ChannelFollowingsCache, ChannelFollowingsCache,
InstanceMutingsCache, InstanceMutingsCache,
LocalFollowingsCache, LocalFollowingsCache,
RenoteMutingsCache,
UserBlockedCache, UserBlockedCache,
UserMutingsCache, UserMutingsCache,
userWordMuteCache, userWordMuteCache,
@ -16,7 +17,7 @@ import {
import { getTimestamp } from "@/misc/gen-id.js"; import { getTimestamp } from "@/misc/gen-id.js";
import Logger from "@/services/logger.js"; import Logger from "@/services/logger.js";
import { UserProfiles } from "@/models/index.js"; import { UserProfiles } from "@/models/index.js";
import { getWordHardMute } from "@/misc/check-word-mute"; import { getWordHardMute } from "@/misc/check-word-mute.js";
function newClient(): Client | null { function newClient(): Client | null {
if (!config.scylla) { if (!config.scylla) {
@ -107,8 +108,8 @@ export const prepared = {
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
select: { select: {
byDate: `SELECT * FROM note WHERE "createdAtDate" = ?`, byDate: `SELECT * FROM note WHERE "createdAtDate" = ?`,
byUri: `SELECT * FROM note WHERE "uri" IN ?`, byUri: `SELECT * FROM note WHERE "uri" = ?`,
byUrl: `SELECT * FROM note WHERE "url" IN ?`, byUrl: `SELECT * FROM note WHERE "url" = ?`,
byId: `SELECT * FROM note_by_id WHERE "id" IN ?`, byId: `SELECT * FROM note_by_id WHERE "id" IN ?`,
byUserId: `SELECT * FROM note_by_userid WHERE "userId" IN ?`, byUserId: `SELECT * FROM note_by_userid WHERE "userId" IN ?`,
}, },
@ -482,3 +483,15 @@ export async function filterBlockedUser(
!(note.renoteUserId && blockerIds.includes(note.renoteUserId)), !(note.renoteUserId && blockerIds.includes(note.renoteUserId)),
); );
} }
export async function filterMutedRenotes(
notes: ScyllaNote[],
user: { id: User["id"] },
): Promise<ScyllaNote[]> {
const cache = await RenoteMutingsCache.init(user.id);
const muteeIds = await cache.getAll();
return notes.filter(
(note) => note.text || !note.renoteId || !muteeIds.includes(note.userId),
);
}

View file

@ -7,6 +7,7 @@ import {
Followings, Followings,
MutedNotes, MutedNotes,
Mutings, Mutings,
RenoteMutings,
UserProfiles, UserProfiles,
} from "@/models/index.js"; } from "@/models/index.js";
import { IsNull } from "typeorm"; import { IsNull } from "typeorm";
@ -73,7 +74,7 @@ export class Cache<T> {
public async delete(...keys: (string | null)[]): Promise<void> { public async delete(...keys: (string | null)[]): Promise<void> {
if (keys.length > 0) { if (keys.length > 0) {
const _keys = keys.map(this.prefixedKey); const _keys = keys.map((key) => this.prefixedKey(key));
await redisClient.del(_keys); await redisClient.del(_keys);
} }
} }
@ -233,7 +234,9 @@ class HashCache {
} }
public async delete(...fields: string[]) { public async delete(...fields: string[]) {
await redisClient.hdel(this.key, ...fields); if (fields.length > 0) {
await redisClient.hdel(this.key, ...fields);
}
} }
public async clear() { public async clear() {
@ -401,3 +404,22 @@ export class UserBlockedCache extends SetCache {
} }
export const userWordMuteCache = new Cache<string[][]>("mutedWord", 60 * 30); export const userWordMuteCache = new Cache<string[][]>("mutedWord", 60 * 30);
export class RenoteMutingsCache extends SetCache {
private constructor(userId: string) {
const fetcher = () =>
RenoteMutings.find({
select: ["muteeId"],
where: { muterId: userId },
}).then((mutes) => mutes.map(({ muteeId }) => muteeId));
super("renoteMute", userId, fetcher);
}
public static async init(userId: string): Promise<RenoteMutingsCache> {
const cache = new RenoteMutingsCache(userId);
await cache.fetch();
return cache;
}
}

View file

@ -21,6 +21,7 @@ import {
filterMutedUser, filterMutedUser,
filterMutedNote, filterMutedNote,
filterBlockedUser, filterBlockedUser,
filterMutedRenotes,
} from "@/db/scylla.js"; } from "@/db/scylla.js";
import { ChannelFollowingsCache, LocalFollowingsCache } from "@/misc/cache.js"; import { ChannelFollowingsCache, LocalFollowingsCache } from "@/misc/cache.js";
@ -92,6 +93,7 @@ export default define(meta, paramDef, async (ps, user) => {
filtered = await filterMutedUser(filtered, user); filtered = await filterMutedUser(filtered, user);
filtered = await filterMutedNote(filtered, user); filtered = await filterMutedNote(filtered, user);
filtered = await filterBlockedUser(filtered, user); filtered = await filterBlockedUser(filtered, user);
filtered = await filterMutedRenotes(filtered, user);
return filtered; return filtered;
}; };

View file

@ -4,6 +4,7 @@ import { RenoteMuting } from "@/models/entities/renote-muting.js";
import define from "../../define.js"; import define from "../../define.js";
import { ApiError } from "../../error.js"; import { ApiError } from "../../error.js";
import { getUser } from "../../common/getters.js"; import { getUser } from "../../common/getters.js";
import { RenoteMutingsCache } from "@/misc/cache.js";
export const meta = { export const meta = {
tags: ["account"], tags: ["account"],
@ -47,12 +48,8 @@ export default define(meta, paramDef, async (ps, user) => {
}); });
// Check if already muting // Check if already muting
const exist = await RenoteMutings.exist({ const cache = await RenoteMutingsCache.init(muter.id);
where: { const exist = await cache.has(mutee.id);
muterId: muter.id,
muteeId: mutee.id,
},
});
if (exist) { if (exist) {
throw new ApiError(meta.errors.alreadyMuting); throw new ApiError(meta.errors.alreadyMuting);
@ -66,5 +63,7 @@ export default define(meta, paramDef, async (ps, user) => {
muteeId: mutee.id, muteeId: mutee.id,
} as RenoteMuting); } as RenoteMuting);
await cache.add(mutee.id);
// publishUserEvent(user.id, "mute", mutee); // publishUserEvent(user.id, "mute", mutee);
}); });

View file

@ -2,6 +2,7 @@ import { RenoteMutings } from "@/models/index.js";
import define from "../../define.js"; import define from "../../define.js";
import { ApiError } from "../../error.js"; import { ApiError } from "../../error.js";
import { getUser } from "../../common/getters.js"; import { getUser } from "../../common/getters.js";
import { RenoteMutingsCache } from "@/misc/cache.js";
export const meta = { export const meta = {
tags: ["account"], tags: ["account"],
@ -45,19 +46,19 @@ export default define(meta, paramDef, async (ps, user) => {
}); });
// Check not muting // Check not muting
const muting = await RenoteMutings.findOneBy({ const cache = await RenoteMutingsCache.init(muter.id);
muterId: muter.id, const muting = await cache.has(mutee.id);
muteeId: mutee.id,
});
if (muting == null) { if (!muting) {
throw new ApiError(meta.errors.notMuting); throw new ApiError(meta.errors.notMuting);
} }
// Delete mute // Delete mute
await RenoteMutings.delete({ await RenoteMutings.delete({
id: muting.id, muterId: muter.id,
muteeId: mutee.id,
}); });
await cache.delete(mutee.id);
// publishUserEvent(user.id, "unmute", mutee); // publishUserEvent(user.id, "unmute", mutee);
}); });

View file

@ -711,7 +711,7 @@ async function insertNote(
emojis: string[], emojis: string[],
mentionedUsers: MinimumUser[], mentionedUsers: MinimumUser[],
) { ) {
if (data.createdAt === null || data.createdAt === undefined) { if (!data.createdAt || isNaN(data.createdAt.getTime())) {
data.createdAt = new Date(); data.createdAt = new Date();
} }
const insert = new Note({ const insert = new Note({