fix: post count for charts

This commit is contained in:
Namekuji 2023-09-05 07:43:17 -04:00
parent 4439d5210f
commit 98ffb20c6a
No known key found for this signature in database
GPG key ID: 1D62332C07FBA532
8 changed files with 69 additions and 28 deletions

View file

@ -104,6 +104,16 @@ CREATE MATERIALIZED VIEW note_by_user_id AS
PRIMARY KEY ("userId", "createdAt", "createdAtDate", "userHost", "visibility") PRIMARY KEY ("userId", "createdAt", "createdAtDate", "userHost", "visibility")
WITH CLUSTERING ORDER BY ("createdAt" DESC); WITH CLUSTERING ORDER BY ("createdAt" DESC);
CREATE MATERIALIZED VIEW local_note_by_user_id AS
SELECT "userId", "createdAt", "createdAtDate", "userHost", "visibility" FROM note
WHERE "userId" IS NOT NULL
AND "createdAt" IS NOT NULL
AND "createdAtDate" IS NOT NULL
AND "userHost" = 'local'
AND "visibility" IS NOT NULL
PRIMARY KEY ("userId", "createdAt", "createdAtDate", "userHost", "visibility")
WITH CLUSTERING ORDER BY ("createdAt" DESC);
CREATE MATERIALIZED VIEW note_by_renote_id AS CREATE MATERIALIZED VIEW note_by_renote_id AS
SELECT * FROM note SELECT * FROM note
WHERE "renoteId" IS NOT NULL WHERE "renoteId" IS NOT NULL
@ -157,16 +167,6 @@ CREATE MATERIALIZED VIEW local_timeline AS
PRIMARY KEY ("createdAtDate", "createdAt", "userId", "userHost", "visibility") PRIMARY KEY ("createdAtDate", "createdAt", "userId", "userHost", "visibility")
WITH CLUSTERING ORDER BY ("createdAt" DESC); WITH CLUSTERING ORDER BY ("createdAt" DESC);
CREATE MATERIALIZED VIEW local_note AS
SELECT "createdAtDate", "createdAt", "userId", "userHost", "visibility" FROM note
WHERE "createdAtDate" IS NOT NULL
AND "createdAt" IS NOT NULL
AND "userId" IS NOT NULL
AND "userHost" = 'local'
AND "visibility" IS NOT NULL
PRIMARY KEY ("createdAtDate", "createdAt", "userId", "userHost", "visibility")
WITH CLUSTERING ORDER BY ("createdAt" DESC);
CREATE MATERIALIZED VIEW score_feed AS CREATE MATERIALIZED VIEW score_feed AS
SELECT * FROM note SELECT * FROM note
WHERE "createdAtDate" IS NOT NULL WHERE "createdAtDate" IS NOT NULL

View file

@ -6,6 +6,7 @@ import type { NoteReaction } from "@/models/entities/note-reaction.js";
import { Client, types, tracker } from "cassandra-driver"; import { Client, types, tracker } from "cassandra-driver";
import type { User } from "@/models/entities/user.js"; import type { User } from "@/models/entities/user.js";
import { import {
Cache,
ChannelFollowingsCache, ChannelFollowingsCache,
InstanceMutingsCache, InstanceMutingsCache,
LocalFollowingsCache, LocalFollowingsCache,
@ -67,6 +68,32 @@ export const scyllaClient = newClient();
export const prepared = scyllaQueries; export const prepared = scyllaQueries;
const localPostCountCache = new Cache<number>("localPostCount", 1000 * 60 * 10);
export const allPostCountCache = new Cache<number>(
"allPostCount",
1000 * 60 * 10,
);
export async function fetchPostCount(local = false): Promise<number> {
if (!scyllaClient) {
throw new Error("ScyllaDB is disabled");
}
if (local) {
return await localPostCountCache.fetch(null, () =>
scyllaClient
.execute("SELECT COUNT(*) FROM local_note_by_user_id")
.then((result) => result.first().get("count") as number),
);
}
return await allPostCountCache.fetch(null, () =>
scyllaClient
.execute("SELECT COUNT(*) FROM note")
.then((result) => result.first().get("count") as number),
);
}
export interface ScyllaNotification { export interface ScyllaNotification {
targetId: string; targetId: string;
createdAtDate: Date; createdAtDate: Date;
@ -444,11 +471,15 @@ export async function execPaginationQuery(
untilDate = notifications[notifications.length - 1].createdAt; untilDate = notifications[notifications.length - 1].createdAt;
} else if (kind === "reaction") { } else if (kind === "reaction") {
const reactions = result.rows.map(parseScyllaReaction); const reactions = result.rows.map(parseScyllaReaction);
(found as ScyllaNoteReaction[]).push(...(filter?.reaction ? await filter.reaction(reactions) : reactions)); (found as ScyllaNoteReaction[]).push(
...(filter?.reaction ? await filter.reaction(reactions) : reactions),
);
untilDate = reactions[reactions.length - 1].createdAt; untilDate = reactions[reactions.length - 1].createdAt;
} else { } else {
const notes = result.rows.map(parseScyllaNote); const notes = result.rows.map(parseScyllaNote);
(found as ScyllaNote[]).push(...(filter?.note ? await filter.note(notes) : notes)); (found as ScyllaNote[]).push(
...(filter?.note ? await filter.note(notes) : notes),
);
untilDate = notes[notes.length - 1].createdAt; untilDate = notes[notes.length - 1].createdAt;
} }
} }

View file

@ -7,7 +7,7 @@ import { MoreThan } from "typeorm";
import { index } from "@/services/note/create.js"; import { index } from "@/services/note/create.js";
import { Note } from "@/models/entities/note.js"; import { Note } from "@/models/entities/note.js";
import meilisearch from "../../../db/meilisearch.js"; import meilisearch from "../../../db/meilisearch.js";
import { scyllaClient } from "@/db/scylla.js"; import { fetchPostCount, scyllaClient } from "@/db/scylla.js";
const logger = queueLogger.createSubLogger("index-all-notes"); const logger = queueLogger.createSubLogger("index-all-notes");
@ -57,9 +57,7 @@ export default async function indexAllNotes(
try { try {
const count = await (scyllaClient const count = await (scyllaClient
? scyllaClient ? fetchPostCount(false)
.execute("SELECT COUNT(1) FROM note")
.then((result) => result.first().get("count") as number)
: Notes.count()); : Notes.count());
total = count; total = count;
await job.update({ indexedCount, cursor, total }); await job.update({ indexedCount, cursor, total });

View file

@ -4,7 +4,7 @@ import { fetchMeta } from "@/misc/fetch-meta.js";
import { Users, Notes } from "@/models/index.js"; import { Users, Notes } from "@/models/index.js";
import { IsNull } from "typeorm"; import { IsNull } from "typeorm";
import { MAX_NOTE_TEXT_LENGTH, FILE_TYPE_BROWSERSAFE } from "@/const.js"; import { MAX_NOTE_TEXT_LENGTH, FILE_TYPE_BROWSERSAFE } from "@/const.js";
import { scyllaClient } from "@/db/scylla"; import { fetchPostCount, scyllaClient } from "@/db/scylla";
export async function getInstance( export async function getInstance(
response: Entity.Instance, response: Entity.Instance,
@ -14,9 +14,7 @@ export async function getInstance(
fetchMeta(true), fetchMeta(true),
Users.count({ where: { host: IsNull() } }), Users.count({ where: { host: IsNull() } }),
scyllaClient scyllaClient
? scyllaClient ? fetchPostCount(true)
.execute("SELECT COUNT(1) FROM note")
.then((result) => result.first().get("count") as number)
: Notes.count({ where: { userHost: IsNull() } }), : Notes.count({ where: { userHost: IsNull() } }),
]); ]);

View file

@ -5,7 +5,7 @@ import { Users, Notes } from "@/models/index.js";
import { IsNull, MoreThan } from "typeorm"; import { IsNull, MoreThan } from "typeorm";
import { MAX_NOTE_TEXT_LENGTH, MAX_CAPTION_TEXT_LENGTH } from "@/const.js"; import { MAX_NOTE_TEXT_LENGTH, MAX_CAPTION_TEXT_LENGTH } from "@/const.js";
import { Cache } from "@/misc/cache.js"; import { Cache } from "@/misc/cache.js";
import { scyllaClient } from "@/db/scylla"; import { fetchPostCount, scyllaClient } from "@/db/scylla";
const router = new Router(); const router = new Router();
@ -43,9 +43,7 @@ const nodeinfo2 = async () => {
}, },
}), }),
scyllaClient scyllaClient
? scyllaClient ? fetchPostCount(true)
.execute("SELECT COUNT(1) FROM local_note")
.then((result) => result.first().get("count") as number)
: Notes.count({ where: { userHost: IsNull() } }), : Notes.count({ where: { userHost: IsNull() } }),
]); ]);

View file

@ -5,6 +5,7 @@ import type { DriveFile } from "@/models/entities/drive-file.js";
import type { Note } from "@/models/entities/note.js"; import type { Note } from "@/models/entities/note.js";
import { toPuny } from "@/misc/convert-host.js"; import { toPuny } from "@/misc/convert-host.js";
import { name, schema } from "./entities/instance.js"; import { name, schema } from "./entities/instance.js";
import { scyllaClient } from "@/db/scylla.js";
/** /**
* *
@ -18,9 +19,10 @@ export default class InstanceChart extends Chart<typeof schema> {
protected async tickMajor( protected async tickMajor(
group: string, group: string,
): Promise<Partial<KVs<typeof schema>>> { ): Promise<Partial<KVs<typeof schema>>> {
const zero = async () => 0;
const [notesCount, usersCount, followingCount, followersCount, driveFiles] = const [notesCount, usersCount, followingCount, followersCount, driveFiles] =
await Promise.all([ await Promise.all([
Notes.countBy({ userHost: group }), scyllaClient ? zero() : Notes.countBy({ userHost: group }),
Users.countBy({ host: group }), Users.countBy({ host: group }),
Followings.countBy({ followerHost: group }), Followings.countBy({ followerHost: group }),
Followings.countBy({ followeeHost: group }), Followings.countBy({ followeeHost: group }),

View file

@ -4,6 +4,7 @@ import { Notes } from "@/models/index.js";
import { Not, IsNull } from "typeorm"; import { Not, IsNull } from "typeorm";
import type { Note } from "@/models/entities/note.js"; import type { Note } from "@/models/entities/note.js";
import { name, schema } from "./entities/notes.js"; import { name, schema } from "./entities/notes.js";
import { fetchPostCount, scyllaClient } from "@/db/scylla.js";
/** /**
* *
@ -16,8 +17,12 @@ export default class NotesChart extends Chart<typeof schema> {
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> { protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
const [localCount, remoteCount] = await Promise.all([ const [localCount, remoteCount] = await Promise.all([
Notes.countBy({ userHost: IsNull() }), scyllaClient
Notes.countBy({ userHost: Not(IsNull()) }), ? fetchPostCount(true)
: Notes.countBy({ userHost: IsNull() }),
scyllaClient
? fetchPostCount(false)
: Notes.countBy({ userHost: Not(IsNull()) }),
]); ]);
return { return {

View file

@ -4,6 +4,7 @@ import type { User } from "@/models/entities/user.js";
import { Notes } from "@/models/index.js"; import { Notes } from "@/models/index.js";
import type { Note } from "@/models/entities/note.js"; import type { Note } from "@/models/entities/note.js";
import { name, schema } from "./entities/per-user-notes.js"; import { name, schema } from "./entities/per-user-notes.js";
import { scyllaClient } from "@/db/scylla.js";
/** /**
* *
@ -17,7 +18,15 @@ export default class PerUserNotesChart extends Chart<typeof schema> {
protected async tickMajor( protected async tickMajor(
group: string, group: string,
): Promise<Partial<KVs<typeof schema>>> { ): Promise<Partial<KVs<typeof schema>>> {
const [count] = await Promise.all([Notes.countBy({ userId: group })]); const count = await (scyllaClient
? scyllaClient
.execute(
`SELECT COUNT(*) note_by_user_id WHERE "userId" = ?`,
[group],
{ prepare: true },
)
.then((result) => result.first().get("count") as number)
: Notes.countBy({ userId: group }));
return { return {
total: count, total: count,