delete note and reaction in scylla

This commit is contained in:
Namekuji 2023-07-24 12:23:57 -04:00
parent ee6795ac9d
commit a8b54b0c92
No known key found for this signature in database
GPG key ID: 1D62332C07FBA532
6 changed files with 95 additions and 33 deletions

View file

@ -1,6 +1,7 @@
import config from "@/config/index.js"; import config from "@/config/index.js";
import { DriveFile } from "@/models/entities/drive-file.js"; import type { PopulatedEmoji } from "@/misc/populate-emojis.js";
import { Client } from "cassandra-driver"; import type { NoteReaction } from "@/models/entities/note-reaction.js";
import { Client, types } from "cassandra-driver";
function newClient(): Client | null { function newClient(): Client | null {
if (!config.scylla) { if (!config.scylla) {
@ -52,11 +53,11 @@ export const prepared = {
VALUES VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
select: { select: {
byDate: `SELECT * FROM note WHERE "createdAtDate" = ? AND "createdAt" < ?`, byDate: `SELECT * FROM note WHERE "createdAtDate" IN ?`,
byId: `SELECT * FROM note WHERE "id" IN ?`, byId: `SELECT * FROM note WHERE "id" IN ?`,
byUri: `SELECT * FROM note WHERE "uri" = ?`, byUri: `SELECT * FROM note WHERE "uri" IN ?`,
byUrl: `SELECT * FROM note WHERE "url" = ?`, byUrl: `SELECT * FROM note WHERE "url" IN ?`,
byUserId: `SELECT * FROM note_by_userid WHERE "userId" = ? AND "createdAt" < ?`, byUserId: `SELECT * FROM note_by_userid WHERE "userId" IN ?`,
}, },
delete: `DELETE FROM note WHERE "createdAtDate" = ? AND "createdAt" = ?`, delete: `DELETE FROM note WHERE "createdAtDate" = ? AND "createdAt" = ?`,
update: { update: {
@ -68,6 +69,13 @@ export const prepared = {
insert: `INSERT INTO reaction insert: `INSERT INTO reaction
("id", "noteId", "userId", "reaction", "emoji", "createdAt") ("id", "noteId", "userId", "reaction", "emoji", "createdAt")
VALUES (?, ?, ?, ?, ?, ?)`, VALUES (?, ?, ?, ?, ?, ?)`,
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" = ?`,
byId: `SELECT * FROM reaction WHERE "id" IN ?`,
},
delete: `DELETE FROM reaction WHERE "noteId" = ? AND "userId" = ?`,
}, },
}; };
@ -87,3 +95,18 @@ export interface ScyllaDriveFile {
width: number; width: number;
height: number; height: number;
} }
export type ScyllaNoteReaction = NoteReaction & {
emoji: PopulatedEmoji
}
export function parseScyllaReaction(row: types.Row): ScyllaNoteReaction {
return {
id: row.get("id"),
noteId: row.get("noteId"),
userId: row.get("userId"),
reaction: row.get("reaction"),
createdAt: row.get("createdAt"),
emoji: row.get("emoji"),
}
}

View file

@ -14,7 +14,7 @@ export const EmojiCache = new Cache<Emoji | null>("populateEmojis", 60 * 60 * 12
/** /**
* *
*/ */
type PopulatedEmoji = { export type PopulatedEmoji = {
name: string; name: string;
url: string; url: string;
width: number | null; width: number | null;

View file

@ -67,10 +67,8 @@ import { shouldSilenceInstance } from "@/misc/should-block-instance.js";
import meilisearch from "../../db/meilisearch.js"; import meilisearch from "../../db/meilisearch.js";
import { redisClient } from "@/db/redis.js"; import { redisClient } from "@/db/redis.js";
import { Mutex } from "redis-semaphore"; import { Mutex } from "redis-semaphore";
import { prepared, scyllaClient, ScyllaDriveFile } from "@/db/scylla.js"; import { prepared, scyllaClient } from "@/db/scylla.js";
import { populateEmojis } from "@/misc/populate-emojis.js"; import { populateEmojis } from "@/misc/populate-emojis.js";
import { decodeReaction } from "@/misc/reaction-lib.js";
import { types } from "cassandra-driver";
const mutedWordsCache = new Cache< const mutedWordsCache = new Cache<
{ userId: UserProfile["userId"]; mutedWords: UserProfile["mutedWords"] }[] { userId: UserProfile["userId"]; mutedWords: UserProfile["mutedWords"] }[]

View file

@ -22,6 +22,7 @@ import { countSameRenotes } from "@/misc/count-same-renotes.js";
import { registerOrFetchInstanceDoc } from "../register-or-fetch-instance-doc.js"; import { registerOrFetchInstanceDoc } from "../register-or-fetch-instance-doc.js";
import { deliverToRelays } from "../relay.js"; import { deliverToRelays } from "../relay.js";
import meilisearch from "@/db/meilisearch.js"; import meilisearch from "@/db/meilisearch.js";
import { prepared, scyllaClient } from "@/db/scylla.js";
/** /**
* 稿 * 稿
@ -116,6 +117,13 @@ export default async function (
} }
} }
if (scyllaClient) {
const date = new Date(note.createdAt.getTime());
await scyllaClient.execute(prepared.note.delete, [date, date], {
prepare: true,
});
}
await Notes.delete({ await Notes.delete({
id: note.id, id: note.id,
userId: user.id, userId: user.id,

View file

@ -68,7 +68,14 @@ export default async (
// Thus, a reaction by the same user will be replaced if exists. // Thus, a reaction by the same user will be replaced if exists.
await scyllaClient.execute( await scyllaClient.execute(
prepared.reaction.insert, prepared.reaction.insert,
[record.id, record.noteId, record.userId, _reaction, emojiData, record.createdAt], [
record.id,
record.noteId,
record.userId,
_reaction,
emojiData,
record.createdAt,
],
{ prepare: true }, { prepare: true },
); );
} else { } else {

View file

@ -8,28 +8,39 @@ import type { User, IRemoteUser } from "@/models/entities/user.js";
import type { Note } from "@/models/entities/note.js"; import type { Note } from "@/models/entities/note.js";
import { NoteReactions, Users, Notes } from "@/models/index.js"; import { NoteReactions, Users, Notes } from "@/models/index.js";
import { decodeReaction } from "@/misc/reaction-lib.js"; import { decodeReaction } from "@/misc/reaction-lib.js";
import { parseScyllaReaction, prepared, scyllaClient } from "@/db/scylla";
import type { NoteReaction } from "@/models/entities/note-reaction.js";
export default async ( export default async (
user: { id: User["id"]; host: User["host"] }, user: { id: User["id"]; host: User["host"] },
note: Note, note: Note,
) => { ) => {
const reaction = await NoteReactions.findOneBy({ let reaction: NoteReaction | null;
noteId: note.id, if (scyllaClient) {
userId: user.id, const result = await scyllaClient.execute(
}); prepared.reaction.select.byNoteAndUser,
[note.id, user.id],
// if already unreacted { prepare: true },
if (reaction == null) {
throw new IdentifiableError(
"60527ec9-b4cb-4a88-a6bd-32d3ad26817d",
"not reacted",
); );
reaction =
result.rowLength > 0 ? parseScyllaReaction(result.rows[0]) : null;
} else {
reaction = await NoteReactions.findOneBy({
noteId: note.id,
userId: user.id,
});
} }
// Delete reaction // Delete reaction
const result = await NoteReactions.delete(reaction.id); if (reaction) {
if (scyllaClient) {
if (result.affected !== 1) { await scyllaClient.execute(prepared.reaction.delete, [note.id, user.id], {
prepare: true,
});
} else {
await NoteReactions.delete(reaction.id);
}
} else {
throw new IdentifiableError( throw new IdentifiableError(
"60527ec9-b4cb-4a88-a6bd-32d3ad26817d", "60527ec9-b4cb-4a88-a6bd-32d3ad26817d",
"not reacted", "not reacted",
@ -37,16 +48,31 @@ export default async (
} }
// Decrement reactions count // Decrement reactions count
const sql = `jsonb_set("reactions", '{${reaction.reaction}}', (COALESCE("reactions"->>'${reaction.reaction}', '0')::int - 1)::text::jsonb)`; if (scyllaClient) {
await Notes.createQueryBuilder() const count = Math.max((note.reactions[reaction.reaction] ?? 0) - 1, 0);
.update() if (count === 0) {
.set({ delete note.reactions[reaction.reaction];
reactions: () => sql, } else {
}) note.reactions[reaction.reaction] = count;
.where("id = :id", { id: note.id }) }
.execute(); const date = new Date(note.createdAt.getTime());
await scyllaClient.execute(
prepared.note.update.reactions,
[note.reactions, Math.max((note.score ?? 0) - 1, 0), date, date],
{ prepare: true },
);
} else {
const sql = `jsonb_set("reactions", '{${reaction.reaction}}', (COALESCE("reactions"->>'${reaction.reaction}', '0')::int - 1)::text::jsonb)`;
await Notes.createQueryBuilder()
.update()
.set({
reactions: () => sql,
})
.where("id = :id", { id: note.id })
.execute();
Notes.decrement({ id: note.id }, "score", 1); Notes.decrement({ id: note.id }, "score", 1);
}
publishNoteStream(note.id, "unreacted", { publishNoteStream(note.id, "unreacted", {
reaction: decodeReaction(reaction.reaction).reaction, reaction: decodeReaction(reaction.reaction).reaction,