perf: Move recalculation of memberships to async job (#9567)

* perf: Move recalculation of memberships to async job

* tsc
This commit is contained in:
Tom Moor
2025-07-08 18:01:18 -04:00
committed by GitHub
parent a60634139e
commit 68f87f7254
3 changed files with 100 additions and 74 deletions
+1 -65
View File
@@ -1,15 +1,7 @@
import { Transaction } from "sequelize";
import { createContext } from "@server/context";
import { traceFunction } from "@server/logging/tracing";
import {
User,
Document,
Collection,
Pin,
Event,
UserMembership,
GroupMembership,
} from "@server/models";
import { User, Document, Collection, Pin, Event } from "@server/models";
type Props = {
/** User attempting to move the document */
@@ -227,44 +219,6 @@ async function documentMover({
await document.save({ transaction });
result.documents.push(document);
// If there are any sourced memberships for this document, we need to go to the source
// memberships and recalculate the membership for the user or group.
const [
userMemberships,
parentDocumentUserMemberships,
groupMemberships,
parentDocumentGroupMemberships,
] = await Promise.all([
UserMembership.findRootMembershipsForDocument(document.id, undefined, {
transaction,
}),
parentDocumentId
? UserMembership.findRootMembershipsForDocument(
parentDocumentId,
undefined,
{ transaction }
)
: [],
GroupMembership.findRootMembershipsForDocument(document.id, undefined, {
transaction,
}),
parentDocumentId
? GroupMembership.findRootMembershipsForDocument(
parentDocumentId,
undefined,
{ transaction }
)
: [],
]);
await recalculateUserMemberships(userMemberships, transaction);
await recalculateUserMemberships(parentDocumentUserMemberships, transaction);
await recalculateGroupMemberships(groupMemberships, transaction);
await recalculateGroupMemberships(
parentDocumentGroupMemberships,
transaction
);
await Event.create(
{
name: "documents.move",
@@ -288,24 +242,6 @@ async function documentMover({
return result;
}
async function recalculateUserMemberships(
memberships: UserMembership[],
transaction?: Transaction
) {
for (const membership of memberships) {
await UserMembership.createSourcedMemberships(membership, { transaction });
}
}
async function recalculateGroupMemberships(
memberships: GroupMembership[],
transaction?: Transaction
) {
for (const membership of memberships) {
await GroupMembership.createSourcedMemberships(membership, { transaction });
}
}
export default traceFunction({
spanName: "documentMover",
})(documentMover);
@@ -0,0 +1,87 @@
import { Transaction } from "sequelize";
import { Document, GroupMembership, UserMembership } from "@server/models";
import { sequelize } from "@server/storage/database";
import { DocumentMovedEvent, Event } from "@server/types";
import BaseProcessor from "./BaseProcessor";
export default class DocumentMovedProcessor extends BaseProcessor {
static applicableEvents: Event["name"][] = ["documents.move"];
async perform(event: DocumentMovedEvent) {
await sequelize.transaction(async (transaction) => {
const document = await Document.findByPk(event.documentId, {
transaction,
});
if (!document) {
return;
}
// If there are any sourced memberships for this document, we need to go to the source
// memberships and recalculate the membership for the user or group.
const [
userMemberships,
parentDocumentUserMemberships,
groupMemberships,
parentDocumentGroupMemberships,
] = await Promise.all([
UserMembership.findRootMembershipsForDocument(document.id, undefined, {
transaction,
}),
document.parentDocumentId
? UserMembership.findRootMembershipsForDocument(
document.parentDocumentId,
undefined,
{ transaction }
)
: [],
GroupMembership.findRootMembershipsForDocument(document.id, undefined, {
transaction,
}),
document.parentDocumentId
? GroupMembership.findRootMembershipsForDocument(
document.parentDocumentId,
undefined,
{ transaction }
)
: [],
]);
await this.recalculateUserMemberships(userMemberships, transaction);
await this.recalculateUserMemberships(
parentDocumentUserMemberships,
transaction
);
await this.recalculateGroupMemberships(groupMemberships, transaction);
await this.recalculateGroupMemberships(
parentDocumentGroupMemberships,
transaction
);
});
}
private async recalculateUserMemberships(
memberships: UserMembership[],
transaction?: Transaction
) {
await Promise.all(
memberships.map((membership) =>
UserMembership.createSourcedMemberships(membership, {
transaction,
})
)
);
}
private async recalculateGroupMemberships(
memberships: GroupMembership[],
transaction?: Transaction
) {
await Promise.all(
memberships.map((membership) =>
GroupMembership.createSourcedMemberships(membership, {
transaction,
})
)
);
}
}
+12 -9
View File
@@ -180,6 +180,16 @@ export type UserMembershipEvent = BaseEvent<UserMembership> & {
};
};
export type DocumentMovedEvent = BaseEvent<Document> & {
name: "documents.move";
documentId: string;
collectionId: string;
data: {
collectionIds: string[];
documentIds: string[];
};
};
export type DocumentEvent = BaseEvent<Document> &
(
| {
@@ -212,15 +222,6 @@ export type DocumentEvent = BaseEvent<Document> &
sourceCollectionId: string;
};
}
| {
name: "documents.move";
documentId: string;
collectionId: string;
data: {
collectionIds: string[];
documentIds: string[];
};
}
| {
name:
| "documents.update"
@@ -245,6 +246,7 @@ export type DocumentEvent = BaseEvent<Document> &
previousTitle: string;
};
}
| DocumentMovedEvent
);
export type EmptyTrashEvent = {
@@ -492,6 +494,7 @@ export type Event =
| AuthenticationProviderEvent
| DocumentEvent
| DocumentUserEvent
| DocumentMovedEvent
| DocumentGroupEvent
| PinEvent
| CommentEvent