perf: add materialized view for renotes
This commit is contained in:
parent
18a172cd29
commit
292c7651dc
9 changed files with 43 additions and 21 deletions
packages/backend
native-utils/scylla-migration/cql/1689400417034_timeline
src
db
server/api/endpoints/notes
|
@ -2,6 +2,7 @@ DROP MATERIALIZED VIEW IF EXISTS reaction_by_id;
|
||||||
DROP MATERIALIZED VIEW IF EXISTS reaction_by_userid;
|
DROP MATERIALIZED VIEW IF EXISTS reaction_by_userid;
|
||||||
DROP INDEX IF EXISTS reaction_by_id;
|
DROP INDEX IF EXISTS reaction_by_id;
|
||||||
DROP TABLE IF EXISTS reaction;
|
DROP TABLE IF EXISTS reaction;
|
||||||
|
DROP MATERIALIZED VIEW IF EXISTS note_by_renote_id;
|
||||||
DROP MATERIALIZED VIEW IF EXISTS note_by_userid;
|
DROP MATERIALIZED VIEW IF EXISTS note_by_userid;
|
||||||
DROP MATERIALIZED VIEW IF EXISTS note_by_id;
|
DROP MATERIALIZED VIEW IF EXISTS note_by_id;
|
||||||
DROP INDEX IF EXISTS note_by_uri;
|
DROP INDEX IF EXISTS note_by_uri;
|
||||||
|
|
|
@ -83,7 +83,7 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS note_by_id AS
|
||||||
PRIMARY KEY ("id", "createdAt", "createdAtDate")
|
PRIMARY KEY ("id", "createdAt", "createdAtDate")
|
||||||
WITH CLUSTERING ORDER BY ("createdAt" DESC);
|
WITH CLUSTERING ORDER BY ("createdAt" DESC);
|
||||||
|
|
||||||
CREATE MATERIALIZED VIEW IF NOT EXISTS note_by_userid AS
|
CREATE MATERIALIZED VIEW IF NOT EXISTS note_by_user_id AS
|
||||||
SELECT * FROM note
|
SELECT * FROM note
|
||||||
WHERE "userId" IS NOT NULL
|
WHERE "userId" IS NOT NULL
|
||||||
AND "createdAt" IS NOT NULL
|
AND "createdAt" IS NOT NULL
|
||||||
|
@ -92,6 +92,15 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS note_by_userid AS
|
||||||
PRIMARY KEY ("userId", "createdAt", "createdAtDate", "id")
|
PRIMARY KEY ("userId", "createdAt", "createdAtDate", "id")
|
||||||
WITH CLUSTERING ORDER BY ("createdAt" DESC);
|
WITH CLUSTERING ORDER BY ("createdAt" DESC);
|
||||||
|
|
||||||
|
CREATE MATERIALIZED VIEW IF NOT EXISTS note_by_renote_id AS
|
||||||
|
SELECT * FROM note
|
||||||
|
WHERE "renoteId" IS NOT NULL
|
||||||
|
AND "createdAt" IS NOT NULL
|
||||||
|
AND "createdAtDate" IS NOT NULL
|
||||||
|
AND "id" IS NOT NULL
|
||||||
|
PRIMARY KEY ("renoteId", "createdAt", "createdAtDate", "id")
|
||||||
|
WITH CLUSTERING ORDER BY ("createdAt" DESC);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS reaction (
|
CREATE TABLE IF NOT EXISTS reaction (
|
||||||
"id" text,
|
"id" text,
|
||||||
"noteId" ascii,
|
"noteId" ascii,
|
||||||
|
@ -102,7 +111,7 @@ CREATE TABLE IF NOT EXISTS reaction (
|
||||||
PRIMARY KEY ("noteId", "userId") -- this key constraints one reaction per user for the same post
|
PRIMARY KEY ("noteId", "userId") -- this key constraints one reaction per user for the same post
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE MATERIALIZED VIEW IF NOT EXISTS reaction_by_userid AS
|
CREATE MATERIALIZED VIEW IF NOT EXISTS reaction_by_user_id AS
|
||||||
SELECT * FROM reaction
|
SELECT * FROM reaction
|
||||||
WHERE "userId" IS NOT NULL
|
WHERE "userId" IS NOT NULL
|
||||||
AND "createdAt" IS NOT NULL
|
AND "createdAt" IS NOT NULL
|
||||||
|
|
|
@ -112,7 +112,8 @@ export const prepared = {
|
||||||
byUri: `SELECT * FROM note WHERE "uri" = ?`,
|
byUri: `SELECT * FROM note WHERE "uri" = ?`,
|
||||||
byUrl: `SELECT * FROM note WHERE "url" = ?`,
|
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_user_id WHERE "userId" IN ?`,
|
||||||
|
byRenoteId: `SELECT * FROM note_by_renote_id WHERE "renoteId" IN ?`,
|
||||||
},
|
},
|
||||||
delete: `DELETE FROM note WHERE "createdAtDate" = ? AND "createdAt" = ? AND "id" = ?`,
|
delete: `DELETE FROM note WHERE "createdAtDate" = ? AND "createdAt" = ? AND "id" = ?`,
|
||||||
update: {
|
update: {
|
||||||
|
@ -136,7 +137,7 @@ export const prepared = {
|
||||||
VALUES (?, ?, ?, ?, ?, ?)`,
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||||
select: {
|
select: {
|
||||||
byNoteId: `SELECT * FROM reaction_by_id WHERE "noteId" IN ?`,
|
byNoteId: `SELECT * FROM reaction_by_id WHERE "noteId" IN ?`,
|
||||||
byUserId: `SELECT * FROM reaction_by_userid WHERE "userId" IN ?`,
|
byUserId: `SELECT * FROM reaction_by_user_id WHERE "userId" IN ?`,
|
||||||
byNoteAndUser: `SELECT * FROM reaction WHERE "noteId" IN ? AND "userId" IN ?`,
|
byNoteAndUser: `SELECT * FROM reaction WHERE "noteId" IN ? AND "userId" IN ?`,
|
||||||
byId: `SELECT * FROM reaction WHERE "id" IN ?`,
|
byId: `SELECT * FROM reaction WHERE "id" IN ?`,
|
||||||
},
|
},
|
||||||
|
@ -253,8 +254,13 @@ export function prepareTimelineQuery(ps: {
|
||||||
untilDate?: number;
|
untilDate?: number;
|
||||||
sinceId?: string;
|
sinceId?: string;
|
||||||
sinceDate?: number;
|
sinceDate?: number;
|
||||||
|
noteId?: string;
|
||||||
}): { query: string; untilDate: Date; sinceDate: Date | null } {
|
}): { query: string; untilDate: Date; sinceDate: Date | null } {
|
||||||
const queryParts = [`${prepared.note.select.byDate} AND "createdAt" < ?`];
|
const queryParts = [
|
||||||
|
`${
|
||||||
|
ps.noteId ? prepared.note.select.byRenoteId : prepared.note.select.byDate
|
||||||
|
} AND "createdAt" < ?`,
|
||||||
|
];
|
||||||
|
|
||||||
let until = new Date();
|
let until = new Date();
|
||||||
if (ps.untilId) {
|
if (ps.untilId) {
|
||||||
|
@ -284,13 +290,14 @@ export function prepareTimelineQuery(ps: {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function execTimelineQuery(
|
export async function execNotePaginationQuery(
|
||||||
ps: {
|
ps: {
|
||||||
limit: number;
|
limit: number;
|
||||||
untilId?: string;
|
untilId?: string;
|
||||||
untilDate?: number;
|
untilDate?: number;
|
||||||
sinceId?: string;
|
sinceId?: string;
|
||||||
sinceDate?: number;
|
sinceDate?: number;
|
||||||
|
noteId?: string;
|
||||||
},
|
},
|
||||||
filter?: (_: ScyllaNote[]) => Promise<ScyllaNote[]>,
|
filter?: (_: ScyllaNote[]) => Promise<ScyllaNote[]>,
|
||||||
maxDays = 30,
|
maxDays = 30,
|
||||||
|
@ -304,11 +311,16 @@ export async function execTimelineQuery(
|
||||||
|
|
||||||
// Try to get posts of at most <maxDays> in the single request
|
// Try to get posts of at most <maxDays> in the single request
|
||||||
while (foundNotes.length < ps.limit && scannedEmptyPartitions < maxDays) {
|
while (foundNotes.length < ps.limit && scannedEmptyPartitions < maxDays) {
|
||||||
const params: (Date | string | string[] | number)[] = [untilDate, untilDate];
|
const params: (Date | string | string[] | number)[] = [];
|
||||||
|
if (ps.noteId) {
|
||||||
|
params.push(ps.noteId);
|
||||||
|
} else {
|
||||||
|
params.push(untilDate, untilDate);
|
||||||
|
}
|
||||||
if (sinceDate) {
|
if (sinceDate) {
|
||||||
params.push(sinceDate);
|
params.push(sinceDate);
|
||||||
}
|
}
|
||||||
params.push(ps.limit)
|
params.push(ps.limit);
|
||||||
|
|
||||||
const result = await scyllaClient.execute(query, params, {
|
const result = await scyllaClient.execute(query, params, {
|
||||||
prepare: true,
|
prepare: true,
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
|
||||||
import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js";
|
import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js";
|
||||||
import {
|
import {
|
||||||
ScyllaNote,
|
ScyllaNote,
|
||||||
execTimelineQuery,
|
execNotePaginationQuery,
|
||||||
filterBlockedUser,
|
filterBlockedUser,
|
||||||
filterMutedNote,
|
filterMutedNote,
|
||||||
filterMutedRenotes,
|
filterMutedRenotes,
|
||||||
|
@ -138,7 +138,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
return filtered;
|
return filtered;
|
||||||
};
|
};
|
||||||
|
|
||||||
const foundNotes = await execTimelineQuery(ps, filter);
|
const foundNotes = await execNotePaginationQuery(ps, filter);
|
||||||
return await Notes.packMany(foundNotes.slice(0, ps.limit), user, {
|
return await Notes.packMany(foundNotes.slice(0, ps.limit), user, {
|
||||||
scyllaNote: true,
|
scyllaNote: true,
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
|
||||||
import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js";
|
import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js";
|
||||||
import {
|
import {
|
||||||
ScyllaNote,
|
ScyllaNote,
|
||||||
execTimelineQuery,
|
execNotePaginationQuery,
|
||||||
filterBlockedUser,
|
filterBlockedUser,
|
||||||
filterChannel,
|
filterChannel,
|
||||||
filterMutedNote,
|
filterMutedNote,
|
||||||
|
@ -165,7 +165,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
return filtered;
|
return filtered;
|
||||||
};
|
};
|
||||||
|
|
||||||
const foundNotes = await execTimelineQuery(ps, filter);
|
const foundNotes = await execNotePaginationQuery(ps, filter);
|
||||||
return await Notes.packMany(foundNotes.slice(0, ps.limit), user, {
|
return await Notes.packMany(foundNotes.slice(0, ps.limit), user, {
|
||||||
scyllaNote: true,
|
scyllaNote: true,
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
|
||||||
import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js";
|
import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js";
|
||||||
import {
|
import {
|
||||||
ScyllaNote,
|
ScyllaNote,
|
||||||
execTimelineQuery,
|
execNotePaginationQuery,
|
||||||
filterBlockedUser,
|
filterBlockedUser,
|
||||||
filterChannel,
|
filterChannel,
|
||||||
filterMutedNote,
|
filterMutedNote,
|
||||||
|
@ -180,7 +180,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
return filtered;
|
return filtered;
|
||||||
};
|
};
|
||||||
|
|
||||||
const foundNotes = await execTimelineQuery(ps, filter);
|
const foundNotes = await execNotePaginationQuery(ps, filter);
|
||||||
return await Notes.packMany(foundNotes.slice(0, ps.limit), user, {
|
return await Notes.packMany(foundNotes.slice(0, ps.limit), user, {
|
||||||
scyllaNote: true,
|
scyllaNote: true,
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
|
||||||
import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js";
|
import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js";
|
||||||
import {
|
import {
|
||||||
ScyllaNote,
|
ScyllaNote,
|
||||||
execTimelineQuery,
|
execNotePaginationQuery,
|
||||||
filterBlockedUser,
|
filterBlockedUser,
|
||||||
filterMutedNote,
|
filterMutedNote,
|
||||||
filterMutedRenotes,
|
filterMutedRenotes,
|
||||||
|
@ -177,7 +177,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
return filtered;
|
return filtered;
|
||||||
};
|
};
|
||||||
|
|
||||||
const foundNotes = await execTimelineQuery(ps, filter);
|
const foundNotes = await execNotePaginationQuery(ps, filter);
|
||||||
return await Notes.packMany(foundNotes.slice(0, ps.limit), user, {
|
return await Notes.packMany(foundNotes.slice(0, ps.limit), user, {
|
||||||
scyllaNote: true,
|
scyllaNote: true,
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { makePaginationQuery } from "../../common/make-pagination-query.js";
|
||||||
import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
|
import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
|
||||||
import {
|
import {
|
||||||
ScyllaNote,
|
ScyllaNote,
|
||||||
execTimelineQuery,
|
execNotePaginationQuery,
|
||||||
filterBlockedUser,
|
filterBlockedUser,
|
||||||
filterMutedUser,
|
filterMutedUser,
|
||||||
filterVisibility,
|
filterVisibility,
|
||||||
|
@ -85,7 +85,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const filter = async (notes: ScyllaNote[]) => {
|
const filter = async (notes: ScyllaNote[]) => {
|
||||||
let filtered = notes.filter((n) => n.renoteId === note.id);
|
let filtered = notes;
|
||||||
if (ps.userId) {
|
if (ps.userId) {
|
||||||
filtered = filtered.filter((n) => n.userId === ps.userId);
|
filtered = filtered.filter((n) => n.userId === ps.userId);
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
return filtered;
|
return filtered;
|
||||||
};
|
};
|
||||||
|
|
||||||
const foundNotes = await execTimelineQuery(ps, filter, 1);
|
const foundNotes = await execNotePaginationQuery(ps, filter);
|
||||||
return await Notes.packMany(foundNotes.slice(0, ps.limit), user, {
|
return await Notes.packMany(foundNotes.slice(0, ps.limit), user, {
|
||||||
scyllaNote: true,
|
scyllaNote: true,
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
filterChannel,
|
filterChannel,
|
||||||
filterReply,
|
filterReply,
|
||||||
filterVisibility,
|
filterVisibility,
|
||||||
execTimelineQuery,
|
execNotePaginationQuery,
|
||||||
filterMutedUser,
|
filterMutedUser,
|
||||||
filterMutedNote,
|
filterMutedNote,
|
||||||
filterBlockedUser,
|
filterBlockedUser,
|
||||||
|
@ -152,7 +152,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
return filtered;
|
return filtered;
|
||||||
};
|
};
|
||||||
|
|
||||||
const foundNotes = await execTimelineQuery(ps, filter);
|
const foundNotes = await execNotePaginationQuery(ps, filter);
|
||||||
return await Notes.packMany(foundNotes.slice(0, ps.limit), user, {
|
return await Notes.packMany(foundNotes.slice(0, ps.limit), user, {
|
||||||
scyllaNote: true,
|
scyllaNote: true,
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue