From a8049a0def21506ebf4fb1d3b69ae28ec35fdc21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milorad=20FIlipovi=C4=87?= Date: Fri, 1 Dec 2023 17:50:11 +0100 Subject: [PATCH] perf(editor): Improve node rendering performance when opening large workflows (#7904) ## Summary In an effort to do as little processing as possible in each `Node` component, this PR passes current workflow object to it as a property instead of calling the slow `getCurrentWorkflow` store getter a few times in each node. This should substantially improve loading times for large workflows. As a benchmark, I was using a workflow from [this Linear ticket](https://linear.app/n8n/issue/ADO-1501/deliveryhero-enterprise-instance-very-slow-loading-workflows) and this fix brought down opening time by **20 seconds**. Together with fixes from #7901, this workflow was opening in less than **10 seconds** on my laptop. [Latest e2e run](https://github.com/n8n-io/n8n/actions/runs/7062162739) #### How to test the change: 1. Open a large workflow 2. Observe loading time ## Issues fixed ADO-1523 https://community.n8n.io/t/ui-very-slow-with-more-than-100-nodes/8236/14 ## Review / Merge checklist - [x] PR title and summary are descriptive. **Remember, the title automatically goes into the changelog. Use `(no-changelog)` otherwise.** ([conventions](https://github.com/n8n-io/n8n/blob/master/.github/pull_request_title_conventions.md)) - [ ] [Docs updated](https://github.com/n8n-io/n8n-docs) or follow-up ticket created. - [ ] Tests included. > A bug is not considered fixed, unless a test is added to prevent it from happening again. A feature is not complete without tests. > > *(internal)* You can use Slack commands to trigger [e2e tests](https://www.notion.so/n8n/How-to-use-Test-Instances-d65f49dfc51f441ea44367fb6f67eb0a?pvs=4#a39f9e5ba64a48b58a71d81c837e8227) or [deploy test instance](https://www.notion.so/n8n/How-to-use-Test-Instances-d65f49dfc51f441ea44367fb6f67eb0a?pvs=4#f6a177d32bde4b57ae2da0b8e454bfce) or [deploy early access version on Cloud](https://www.notion.so/n8n/Cloudbot-3dbe779836004972b7057bc989526998?pvs=4#fef2d36ab02247e1a0f65a74f6fb534e). --- packages/editor-ui/src/components/Node.vue | 16 +++++----------- packages/editor-ui/src/mixins/nodeBase.ts | 11 +++++++---- packages/editor-ui/src/views/NodeView.vue | 4 ++++ 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/packages/editor-ui/src/components/Node.vue b/packages/editor-ui/src/components/Node.vue index e1ff7b8c52..de16549f0d 100644 --- a/packages/editor-ui/src/components/Node.vue +++ b/packages/editor-ui/src/components/Node.vue @@ -286,15 +286,11 @@ export default defineComponent({ return this.data.type === MANUAL_TRIGGER_NODE_TYPE; }, isConfigNode(): boolean { - return this.nodeTypesStore.isConfigNode( - this.getCurrentWorkflow(), - this.data, - this.data?.type ?? '', - ); + return this.nodeTypesStore.isConfigNode(this.workflow, this.data, this.data?.type ?? ''); }, isConfigurableNode(): boolean { return this.nodeTypesStore.isConfigurableNode( - this.getCurrentWorkflow(), + this.workflow, this.data, this.data?.type ?? '', ); @@ -349,9 +345,8 @@ export default defineComponent({ }; if (this.node && this.nodeType) { - const workflow = this.workflowsStore.getCurrentWorkflow(); const inputs = - NodeHelpers.getNodeInputs(workflow, this.node, this.nodeType) || + NodeHelpers.getNodeInputs(this.workflow, this.node, this.nodeType) || ([] as Array); const inputTypes = NodeHelpers.getConnectionTypes(inputs); @@ -372,7 +367,7 @@ export default defineComponent({ } const outputs = - NodeHelpers.getNodeOutputs(workflow, this.node, this.nodeType) || + NodeHelpers.getNodeOutputs(this.workflow, this.node, this.nodeType) || ([] as Array); const outputTypes = NodeHelpers.getConnectionTypes(outputs); @@ -634,8 +629,7 @@ export default defineComponent({ // and ends up bogging down the UI with big workflows, for example when pasting a workflow or even opening a node... // so we only update it when necessary (when node is mounted and when it's opened and closed (isActive)) try { - const nodeSubtitle = - this.getNodeSubtitle(this.data, this.nodeType, this.getCurrentWorkflow()) || ''; + const nodeSubtitle = this.getNodeSubtitle(this.data, this.nodeType, this.workflow) || ''; this.nodeSubtitle = nodeSubtitle.includes(CUSTOM_API_CALL_KEY) ? '' : nodeSubtitle; } catch (e) { diff --git a/packages/editor-ui/src/mixins/nodeBase.ts b/packages/editor-ui/src/mixins/nodeBase.ts index 97a82ae31b..470a2ca167 100644 --- a/packages/editor-ui/src/mixins/nodeBase.ts +++ b/packages/editor-ui/src/mixins/nodeBase.ts @@ -17,6 +17,7 @@ import type { INodeInputConfiguration, INodeTypeDescription, INodeOutputConfiguration, + Workflow, } from 'n8n-workflow'; import { useUIStore } from '@/stores/ui.store'; import { useWorkflowsStore } from '@/stores/workflows.store'; @@ -104,6 +105,10 @@ export const nodeBase = defineComponent({ showCustomTooltip: { type: Boolean, }, + workflow: { + type: Object as () => Workflow, + required: true, + }, }, methods: { __addEndpointTestingData(endpoint: Endpoint, type: string, inputIndex: number) { @@ -123,9 +128,8 @@ export const nodeBase = defineComponent({ [key: string]: number; } = {}; - const workflow = this.workflowsStore.getCurrentWorkflow(); const inputs: Array = - NodeHelpers.getNodeInputs(workflow, this.data!, nodeTypeData) || []; + NodeHelpers.getNodeInputs(this.workflow, this.data!, nodeTypeData) || []; this.inputs = inputs; const sortedInputs = [...inputs]; @@ -338,8 +342,7 @@ export const nodeBase = defineComponent({ [key: string]: number; } = {}; - const workflow = this.workflowsStore.getCurrentWorkflow(); - this.outputs = NodeHelpers.getNodeOutputs(workflow, this.data, nodeTypeData) || []; + this.outputs = NodeHelpers.getNodeOutputs(this.workflow, this.data, nodeTypeData) || []; // TODO: There are still a lot of references of "main" in NodesView and // other locations. So assume there will be more problems diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index 06e4186cc0..3ef14e9ce2 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -58,6 +58,7 @@ :isActive="!!activeNode && activeNode.name === nodeData.name" :hideActions="pullConnActive" :isProductionExecutionPreview="isProductionExecutionPreview" + :workflow="currentWorkflowObject" >