perf(editor): Improve performance when opening large workflows with node issues (#7901)

This PR should improve performance when opening large workflows that
contain node issues (Fixes ADO-1521)

**Background**
Main reason for this poor performance is that our `getCurrentWorkflow()`
store getter is unnecessarily heavy but on top of that we are calling it
more than we need. This addresses the second part of the issue by
changing the following:
- Pausing node issue processing while workflows are loading
- Only getting current workflow once (instead for every node) when
calling `refreshNodeIssues`

**Benchmark**
This was tested on a workflow attached to [this Linear
ticket](https://linear.app/n8n/issue/ADO-1501/deliveryhero-enterprise-instance-very-slow-loading-workflows)
and this fix brings down opening time from **~1m10s** to **~28s** on my
laptop.

**Tests**
- [Latest e2e tests
run](https://github.com/n8n-io/n8n/actions/runs/7060874994)

https://community.n8n.io/t/ui-very-slow-with-more-than-100-nodes/8236/14
This commit is contained in:
Milorad FIlipović 2023-12-04 10:57:03 +01:00 committed by GitHub
parent 132d691cbf
commit 4bd7ae29f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 16 additions and 9 deletions

View file

@ -126,6 +126,7 @@ describe('Debug', () => {
cy.url().should('not.include', '/debug');
workflowPage.actions.executeWorkflow();
workflowPage.actions.zoomToFit();
workflowPage.actions.deleteNode(IF_NODE_NAME);
executionsTab.actions.switchToExecutionsTab();

View file

@ -91,6 +91,7 @@ export const nodeHelpers = defineComponent({
// Updates all the issues on all the nodes
refreshNodeIssues(): void {
const nodes = this.workflowsStore.allNodes;
const workflow = this.workflowsStore.getCurrentWorkflow();
let nodeType: INodeTypeDescription | null;
let foundNodeIssues: INodeIssues | null;
@ -99,7 +100,7 @@ export const nodeHelpers = defineComponent({
return;
}
nodeType = this.nodeTypesStore.getNodeType(node.type, node.typeVersion);
foundNodeIssues = this.getNodeIssues(nodeType, node);
foundNodeIssues = this.getNodeIssues(nodeType, node, workflow);
if (foundNodeIssues !== null) {
node.issues = foundNodeIssues;
}
@ -110,6 +111,7 @@ export const nodeHelpers = defineComponent({
getNodeIssues(
nodeType: INodeTypeDescription | null,
node: INodeUi,
workflow: Workflow,
ignoreIssues?: string[],
): INodeIssues | null {
const pinDataNodeNames = Object.keys(this.workflowsStore.getPinData || {});
@ -147,7 +149,6 @@ export const nodeHelpers = defineComponent({
}
}
const workflow = this.workflowsStore.getCurrentWorkflow();
const nodeInputIssues = this.getNodeInputIssues(workflow, node, nodeType);
if (nodeIssues === null) {
nodeIssues = nodeInputIssues;

View file

@ -605,7 +605,7 @@ export const workflowHelpers = defineComponent({
typeUnknown: true,
};
} else {
nodeIssues = this.getNodeIssues(nodeType.description, node, ['execution']);
nodeIssues = this.getNodeIssues(nodeType.description, node, workflow, ['execution']);
}
if (nodeIssues !== null) {

View file

@ -692,6 +692,9 @@ export default defineComponent({
instance(): BrowserJsPlumbInstance {
return this.canvasStore.jsPlumbInstance;
},
isLoading(): boolean {
return this.loadingService !== null;
},
currentWorkflowObject(): Workflow {
return this.workflowsStore.getCurrentWorkflow();
},
@ -2697,10 +2700,6 @@ export default defineComponent({
this.dropPrevented = true;
this.workflowsStore.addConnection({ connection: connectionData });
this.uiStore.stateIsDirty = true;
if (!this.suspendRecordingDetachedConnections) {
this.historyStore.pushCommandToUndo(new AddConnectionCommand(connectionData));
}
if (!this.isReadOnlyRoute && !this.readOnlyEnv) {
NodeViewUtils.hideOutputNameLabel(info.sourceEndpoint);
@ -2742,8 +2741,14 @@ export default defineComponent({
}
}
this.dropPrevented = false;
this.updateNodesInputIssues();
this.resetEndpointsErrors();
if (!this.isLoading) {
this.uiStore.stateIsDirty = true;
if (!this.suspendRecordingDetachedConnections) {
this.historyStore.pushCommandToUndo(new AddConnectionCommand(connectionData));
}
this.updateNodesInputIssues();
this.resetEndpointsErrors();
}
} catch (e) {
console.error(e);
}