wip: read row timeline from scylla

This commit is contained in:
Namekuji 2023-08-01 02:26:19 -04:00
parent b8d8e2e5f5
commit 2937c7fa43
No known key found for this signature in database
GPG key ID: 1D62332C07FBA532
3 changed files with 69 additions and 58 deletions

View file

@ -183,24 +183,32 @@ export const NoteRepository = db.getRepository(Note).extend({
const meId = me ? me.id : null; const meId = me ? me.id : null;
let note: Note | null = null; let note: Note | null = null;
const noteId = typeof src === "object" ? src.id : src; let foundScyllaNote = false;
if (scyllaClient) { const isSrcNote = typeof src === "object";
const result = await scyllaClient.execute(
prepared.note.select.byId, // Always lookup from ScyllaDB if enabled
[[noteId]], if (isSrcNote && !scyllaClient) {
{ prepare: true }, note = src;
); } else {
if (result.rowLength > 0) { const noteId = isSrcNote ? src.id : src;
note = parseScyllaNote(result.first()); if (scyllaClient) {
const result = await scyllaClient.execute(
prepared.note.select.byId,
[[noteId]],
{ prepare: true },
);
if (result.rowLength > 0) {
note = parseScyllaNote(result.first());
foundScyllaNote = true;
}
}
if (!foundScyllaNote) {
// Fallback to Postgres
note = await this.findOneBy({ id: noteId });
} }
} }
if (!note) { if (!note) {
// Fallback to Postgres
note = await this.findOneBy({ id: noteId });
}
if (note === null) {
throw new IdentifiableError( throw new IdentifiableError(
"9725d0ce-ba28-4dde-95a7-2cbb2c15de24", "9725d0ce-ba28-4dde-95a7-2cbb2c15de24",
"No such note.", "No such note.",
@ -260,18 +268,19 @@ export const NoteRepository = db.getRepository(Note).extend({
emojis: noteEmoji, emojis: noteEmoji,
tags: note.tags.length > 0 ? note.tags : undefined, tags: note.tags.length > 0 ? note.tags : undefined,
fileIds: note.fileIds, fileIds: note.fileIds,
files: scyllaClient files:
? (note as ScyllaNote).files.map((file) => ({ scyllaClient && foundScyllaNote
...file, ? (note as ScyllaNote).files.map((file) => ({
createdAt: file.createdAt.toISOString(), ...file,
properties: { createdAt: file.createdAt.toISOString(),
width: file.width ?? undefined, properties: {
height: file.height ?? undefined, width: file.width ?? undefined,
}, height: file.height ?? undefined,
userId: null, },
folderId: null, userId: null,
})) folderId: null,
: DriveFiles.packMany(note.fileIds), }))
: DriveFiles.packMany(note.fileIds),
replyId: note.replyId, replyId: note.replyId,
renoteId: note.renoteId, renoteId: note.renoteId,
channelId: note.channelId || undefined, channelId: note.channelId || undefined,

View file

@ -32,7 +32,7 @@ export async function getNote(
} }
} }
// For legacy notes // Fallback to Postgres
if (!note) { if (!note) {
const query = Notes.createQueryBuilder("note").where("note.id = :id", { const query = Notes.createQueryBuilder("note").where("note.id = :id", {
id: noteId, id: noteId,

View file

@ -11,7 +11,12 @@ import { generateChannelQuery } from "../../common/generate-channel-query.js";
import { generateBlockedUserQuery } from "../../common/generate-block-query.js"; 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 { ApiError } from "../../error.js"; import { ApiError } from "../../error.js";
import { parseScyllaNote, prepared, scyllaClient } from "@/db/scylla.js"; import {
type ScyllaNote,
parseScyllaNote,
prepared,
scyllaClient,
} from "@/db/scylla.js";
import { LocalFollowingsCache } from "@/misc/cache.js"; import { LocalFollowingsCache } from "@/misc/cache.js";
export const meta = { export const meta = {
@ -69,27 +74,35 @@ export default define(meta, paramDef, async (ps, user) => {
const followingsCache = await LocalFollowingsCache.init(user.id); const followingsCache = await LocalFollowingsCache.init(user.id);
if (scyllaClient) { if (scyllaClient) {
const untilDate = ps.untilDate ? new Date(ps.untilDate) : new Date(); let untilDate = new Date();
const query = [`${prepared.note.select.byDate} AND "createdAt" <= ?`]; const foundNotes: ScyllaNote[] = [];
const params: (Date | string | string[])[] = [untilDate, untilDate]; const validIds = [user.id].concat(await followingsCache.getAll());
if (ps.sinceDate) {
query.push(`AND "createdAt" >= ?`); while (foundNotes.length < ps.limit) {
params.push(new Date(ps.sinceDate)); const query = [`${prepared.note.select.byDate} AND "createdAt" < ?`];
} const params: (Date | string | string[])[] = [untilDate, untilDate];
if (ps.untilId) { if (ps.untilId) {
query.push(`AND "id" <= ?`); query.push(`AND "id" < ?`);
params.push(ps.untilId); params.push(ps.untilId);
} }
if (ps.sinceId) { query.push("LIMIT 50"); // Hardcoded to enable prepared query for performance
query.push(`AND "id" >= ?`);
params.push(ps.sinceId); const result = await scyllaClient.execute(query.join(" "), params, {
prepare: true,
});
if (result.rowLength === 0) {
break;
}
const notes = result.rows.map(parseScyllaNote);
const filtered = notes.filter((note) => validIds.includes(note.userId));
foundNotes.push(...filtered);
untilDate = notes[notes.length - 1].createdAt;
} }
const result = await scyllaClient.execute(query.join(" "), params, { return Notes.packMany(foundNotes, user);
prepare: true,
});
const notes = result.rows.map(parseScyllaNote);
return Notes.packMany(notes, user);
} }
const hasFollowing = await followingsCache.hasFollowing(); const hasFollowing = await followingsCache.hasFollowing();
@ -113,17 +126,6 @@ export default define(meta, paramDef, async (ps, user) => {
qb.orWhere(`note.userId IN (${followingQuery.getQuery()})`); qb.orWhere(`note.userId IN (${followingQuery.getQuery()})`);
}), }),
) )
.innerJoinAndSelect("note.user", "user")
.leftJoinAndSelect("user.avatar", "avatar")
.leftJoinAndSelect("user.banner", "banner")
.leftJoinAndSelect("note.reply", "reply")
.leftJoinAndSelect("note.renote", "renote")
.leftJoinAndSelect("reply.user", "replyUser")
.leftJoinAndSelect("replyUser.avatar", "replyUserAvatar")
.leftJoinAndSelect("replyUser.banner", "replyUserBanner")
.leftJoinAndSelect("renote.user", "renoteUser")
.leftJoinAndSelect("renoteUser.avatar", "renoteUserAvatar")
.leftJoinAndSelect("renoteUser.banner", "renoteUserBanner")
.setParameters(followingQuery.getParameters()); .setParameters(followingQuery.getParameters());
generateChannelQuery(query, user); generateChannelQuery(query, user);