From 4bd7ae29f7c82b8817420e617a123024147c6c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milorad=20FIlipovi=C4=87?= Date: Mon, 4 Dec 2023 10:57:03 +0100 Subject: [PATCH] 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 --- cypress/e2e/28-debug.cy.ts | 1 + packages/editor-ui/src/mixins/nodeHelpers.ts | 5 +++-- .../editor-ui/src/mixins/workflowHelpers.ts | 2 +- packages/editor-ui/src/views/NodeView.vue | 17 +++++++++++------ 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/cypress/e2e/28-debug.cy.ts b/cypress/e2e/28-debug.cy.ts index 953cdd5f521f7..699f07d53ff63 100644 --- a/cypress/e2e/28-debug.cy.ts +++ b/cypress/e2e/28-debug.cy.ts @@ -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(); diff --git a/packages/editor-ui/src/mixins/nodeHelpers.ts b/packages/editor-ui/src/mixins/nodeHelpers.ts index 578f2959bf2ed..e8276e5f69fb3 100644 --- a/packages/editor-ui/src/mixins/nodeHelpers.ts +++ b/packages/editor-ui/src/mixins/nodeHelpers.ts @@ -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; diff --git a/packages/editor-ui/src/mixins/workflowHelpers.ts b/packages/editor-ui/src/mixins/workflowHelpers.ts index 71b7cad786792..c993389976390 100644 --- a/packages/editor-ui/src/mixins/workflowHelpers.ts +++ b/packages/editor-ui/src/mixins/workflowHelpers.ts @@ -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) { diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index 3ef14e9ce25f4..11542a1ff1019 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -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); }