mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(core) speed up a step in document deletion
Summary: The `_repairWorkspaceGuests` method is slow for workspaces with large numbers of documents. It makes a query that produces a lot of rows. The query itself is tolerable, but TypeORM processing uses enough CPU to be a likely culprit in some production instability. This diff splits the query into two pieces that are logically independent, but which when combined were resulting in the number of rows being the product of the two pieces. Once split, there is also a where clause that can be applied to one of the pieces. The purpose of the method is to add every user that a document within a workspace is shared with to a "guest" group of the workspace itself. The design of "guest" groups is not ideal, but this diff leaves the design unchanged and is intended only to speed up operation. Made some small tweaks to the timing of a flakey test, and temporarily recreated the `samples` directory removed in a previous diff (this is currently breaking tests badly on a fresh worker without a `samples` directory lying around) Test Plan: added test; existing tests pass Reviewers: jarek Reviewed By: jarek Differential Revision: https://phab.getgrist.com/D2844
This commit is contained in:
parent
69ac8fb4b3
commit
29c2b35dcc
@ -2420,20 +2420,27 @@ export class HomeDBManager extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
private async _repairWorkspaceGuests(scope: Scope, wsId: number, transaction?: EntityManager): Promise<void> {
|
private async _repairWorkspaceGuests(scope: Scope, wsId: number, transaction?: EntityManager): Promise<void> {
|
||||||
return await this._runInTransaction(transaction, async manager => {
|
return await this._runInTransaction(transaction, async manager => {
|
||||||
|
// Get guest group for workspace.
|
||||||
const wsQuery = this._workspace(scope, wsId, {manager})
|
const wsQuery = this._workspace(scope, wsId, {manager})
|
||||||
.leftJoinAndSelect('workspaces.aclRules', 'acl_rules')
|
.leftJoinAndSelect('workspaces.aclRules', 'acl_rules')
|
||||||
.leftJoinAndSelect('acl_rules.group', 'groups')
|
.leftJoinAndSelect('acl_rules.group', 'groups');
|
||||||
.leftJoinAndSelect('workspaces.docs', 'docs')
|
const workspace: Workspace = (await wsQuery.getOne())!;
|
||||||
.leftJoinAndSelect('docs.aclRules', 'doc_acl_rules')
|
|
||||||
.leftJoinAndSelect('doc_acl_rules.group', 'doc_groups')
|
|
||||||
.leftJoinAndSelect('doc_groups.memberUsers', 'doc_users');
|
|
||||||
const workspace: Workspace = (await wsQuery.getOne())!;
|
|
||||||
const wsGuestGroup = workspace.aclRules.map(aclRule => aclRule.group)
|
const wsGuestGroup = workspace.aclRules.map(aclRule => aclRule.group)
|
||||||
.find(_grp => _grp.name === roles.GUEST);
|
.find(_grp => _grp.name === roles.GUEST);
|
||||||
if (!wsGuestGroup) {
|
if (!wsGuestGroup) {
|
||||||
throw new Error(`_repairWorkspaceGuests error: could not find ${roles.GUEST} ACL group`);
|
throw new Error(`_repairWorkspaceGuests error: could not find ${roles.GUEST} ACL group`);
|
||||||
}
|
}
|
||||||
wsGuestGroup.memberUsers = this._filterEveryone(getResourceUsers(workspace.docs));
|
|
||||||
|
// Get explicitly added users of docs inside the workspace, as a separate query
|
||||||
|
// to avoid multiplying rows and to allow filtering the result in sql.
|
||||||
|
const wsWithDocsQuery = this._workspace(scope, wsId, {manager})
|
||||||
|
.leftJoinAndSelect('workspaces.docs', 'docs')
|
||||||
|
.leftJoinAndSelect('docs.aclRules', 'doc_acl_rules')
|
||||||
|
.leftJoinAndSelect('doc_acl_rules.group', 'doc_groups')
|
||||||
|
.leftJoinAndSelect('doc_groups.memberUsers', 'doc_users')
|
||||||
|
.andWhere('doc_users.id is not null');
|
||||||
|
const wsWithDocs = await wsWithDocsQuery.getOne();
|
||||||
|
wsGuestGroup.memberUsers = this._filterEveryone(getResourceUsers(wsWithDocs?.docs || []));
|
||||||
await manager.save(wsGuestGroup);
|
await manager.save(wsGuestGroup);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -153,9 +153,15 @@ export class FlexServer implements GristServer {
|
|||||||
log.info(`== Grist version is ${version.version} (commit ${version.gitcommit})`);
|
log.info(`== Grist version is ${version.version} (commit ${version.gitcommit})`);
|
||||||
this.info.push(['appRoot', this.appRoot]);
|
this.info.push(['appRoot', this.appRoot]);
|
||||||
// This directory hold Grist documents.
|
// This directory hold Grist documents.
|
||||||
this.docsRoot = fse.realpathSync(path.resolve((this.options && this.options.dataDir) ||
|
const docsRoot = path.resolve((this.options && this.options.dataDir) ||
|
||||||
process.env.GRIST_DATA_DIR ||
|
process.env.GRIST_DATA_DIR ||
|
||||||
getAppPathTo(this.appRoot, 'samples')));
|
getAppPathTo(this.appRoot, 'samples'));
|
||||||
|
// Create directory if it doesn't exist.
|
||||||
|
// TODO: track down all dependencies on 'samples' existing in tests and
|
||||||
|
// in dev environment, and remove them. Then it would probably be best
|
||||||
|
// to simply fail if the docs root directory does not exist.
|
||||||
|
fse.mkdirpSync(docsRoot);
|
||||||
|
this.docsRoot = fse.realpathSync(docsRoot);
|
||||||
this.info.push(['docsRoot', this.docsRoot]);
|
this.info.push(['docsRoot', this.docsRoot]);
|
||||||
|
|
||||||
const homeUrl = process.env.APP_HOME_URL;
|
const homeUrl = process.env.APP_HOME_URL;
|
||||||
|
Loading…
Reference in New Issue
Block a user