mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-24 04:27:27 +08:00
feat: improve workspace list perf
This commit is contained in:
@@ -0,0 +1,36 @@
|
|||||||
|
-- Add persisted size column for snapshots (nullable for backwards compatibility)
|
||||||
|
ALTER TABLE "snapshots" ADD COLUMN IF NOT EXISTS "size" BIGINT;
|
||||||
|
|
||||||
|
-- Ensure size is populated on insert/update even for older app versions
|
||||||
|
CREATE OR REPLACE FUNCTION snapshots_set_size() RETURNS TRIGGER AS $$
|
||||||
|
BEGIN
|
||||||
|
NEW."size" := COALESCE(NEW."size", octet_length(NEW."blob"));
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS snapshots_set_size_before_write ON "snapshots";
|
||||||
|
CREATE TRIGGER snapshots_set_size_before_write
|
||||||
|
BEFORE INSERT OR UPDATE OF "blob" ON "snapshots"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION snapshots_set_size();
|
||||||
|
|
||||||
|
-- Backfill existing rows
|
||||||
|
UPDATE "snapshots"
|
||||||
|
SET "size" = octet_length("blob")
|
||||||
|
WHERE "size" IS NULL;
|
||||||
|
|
||||||
|
-- Support faster admin aggregates
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_wur_owner" ON "workspace_user_permissions" ("workspace_id")
|
||||||
|
WHERE "type" = 99 AND "status" = 'Accepted'::"WorkspaceMemberStatus";
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_wur_workspace" ON "workspace_user_permissions" ("workspace_id");
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_blobs_active" ON "blobs" ("workspace_id")
|
||||||
|
WHERE "deleted_at" IS NULL AND "status" = 'completed';
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_workspace_pages_public" ON "workspace_pages" ("workspace_id")
|
||||||
|
WHERE "public" = TRUE;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_workspace_features_activated" ON "workspace_features" ("workspace_id")
|
||||||
|
WHERE "activated" = TRUE;
|
||||||
@@ -273,6 +273,8 @@ model Snapshot {
|
|||||||
workspaceId String @map("workspace_id") @db.VarChar
|
workspaceId String @map("workspace_id") @db.VarChar
|
||||||
id String @default(uuid()) @map("guid") @db.VarChar
|
id String @default(uuid()) @map("guid") @db.VarChar
|
||||||
blob Bytes @db.ByteA
|
blob Bytes @db.ByteA
|
||||||
|
// persisted size of blob to avoid summing over bytea
|
||||||
|
size BigInt? @map("size") @db.BigInt
|
||||||
state Bytes? @db.ByteA
|
state Bytes? @db.ByteA
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||||
// the `updated_at` field will not record the time of record changed,
|
// the `updated_at` field will not record the time of record changed,
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ export class DocModel extends BaseModel {
|
|||||||
async upsert(doc: Doc) {
|
async upsert(doc: Doc) {
|
||||||
const { spaceId, docId, blob, timestamp, editorId } = doc;
|
const { spaceId, docId, blob, timestamp, editorId } = doc;
|
||||||
const updatedAt = new Date(timestamp);
|
const updatedAt = new Date(timestamp);
|
||||||
|
const size = blob.byteLength ?? blob.length;
|
||||||
// CONCERNS:
|
// CONCERNS:
|
||||||
// i. Because we save the real user's last seen action time as `updatedAt`,
|
// i. Because we save the real user's last seen action time as `updatedAt`,
|
||||||
// it's possible to simply compare the `updatedAt` to determine if the snapshot is older than the one we are going to save.
|
// it's possible to simply compare the `updatedAt` to determine if the snapshot is older than the one we are going to save.
|
||||||
@@ -158,10 +159,10 @@ export class DocModel extends BaseModel {
|
|||||||
// where: { workspaceId_id: {}, updatedAt: { lt: updatedAt } }
|
// where: { workspaceId_id: {}, updatedAt: { lt: updatedAt } }
|
||||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
const result: { updatedAt: Date }[] = await this.db.$queryRaw`
|
const result: { updatedAt: Date }[] = await this.db.$queryRaw`
|
||||||
INSERT INTO "snapshots" ("workspace_id", "guid", "blob", "created_at", "updated_at", "created_by", "updated_by")
|
INSERT INTO "snapshots" ("workspace_id", "guid", "blob", "size", "created_at", "updated_at", "created_by", "updated_by")
|
||||||
VALUES (${spaceId}, ${docId}, ${blob}, DEFAULT, ${updatedAt}, ${editorId}, ${editorId})
|
VALUES (${spaceId}, ${docId}, ${blob}, ${size}, DEFAULT, ${updatedAt}, ${editorId}, ${editorId})
|
||||||
ON CONFLICT ("workspace_id", "guid")
|
ON CONFLICT ("workspace_id", "guid")
|
||||||
DO UPDATE SET "blob" = ${blob}, "updated_at" = ${updatedAt}, "updated_by" = ${editorId}
|
DO UPDATE SET "blob" = ${blob}, "size" = ${size}, "updated_at" = ${updatedAt}, "updated_by" = ${editorId}
|
||||||
WHERE "snapshots"."workspace_id" = ${spaceId} AND "snapshots"."guid" = ${docId} AND "snapshots"."updated_at" <= ${updatedAt}
|
WHERE "snapshots"."workspace_id" = ${spaceId} AND "snapshots"."guid" = ${docId} AND "snapshots"."updated_at" <= ${updatedAt}
|
||||||
RETURNING "snapshots"."workspace_id" as "workspaceId", "snapshots"."guid" as "id", "snapshots"."updated_at" as "updatedAt"
|
RETURNING "snapshots"."workspace_id" as "workspaceId", "snapshots"."guid" as "id", "snapshots"."updated_at" as "updatedAt"
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ export class WorkspaceModel extends BaseModel {
|
|||||||
),
|
),
|
||||||
snapshot_stats AS (
|
snapshot_stats AS (
|
||||||
SELECT workspace_id,
|
SELECT workspace_id,
|
||||||
SUM(octet_length(blob)) AS snapshot_size,
|
SUM(COALESCE(size, octet_length(blob))) AS snapshot_size,
|
||||||
COUNT(*) AS snapshot_count
|
COUNT(*) AS snapshot_count
|
||||||
FROM snapshots
|
FROM snapshots
|
||||||
GROUP BY workspace_id
|
GROUP BY workspace_id
|
||||||
|
|||||||
Reference in New Issue
Block a user