Skip to content

Commit

Permalink
perf(editor): Improve node rendering performance when opening large w…
Browse files Browse the repository at this point in the history
…orkflows (#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).
  • Loading branch information
MiloradFilipovic committed Dec 1, 2023
1 parent 403ba6e commit a8049a0
Show file tree
Hide file tree
Showing 3 changed files with 16 additions and 15 deletions.
16 changes: 5 additions & 11 deletions packages/editor-ui/src/components/Node.vue
Expand Up @@ -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 ?? '',
);
Expand Down Expand Up @@ -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<ConnectionTypes | INodeInputConfiguration>);
const inputTypes = NodeHelpers.getConnectionTypes(inputs);
Expand All @@ -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<ConnectionTypes | INodeOutputConfiguration>);
const outputTypes = NodeHelpers.getConnectionTypes(outputs);
Expand Down Expand Up @@ -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) {
Expand Down
11 changes: 7 additions & 4 deletions packages/editor-ui/src/mixins/nodeBase.ts
Expand Up @@ -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';
Expand Down Expand Up @@ -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) {
Expand All @@ -123,9 +128,8 @@ export const nodeBase = defineComponent({
[key: string]: number;
} = {};

const workflow = this.workflowsStore.getCurrentWorkflow();
const inputs: Array<ConnectionTypes | INodeInputConfiguration> =
NodeHelpers.getNodeInputs(workflow, this.data!, nodeTypeData) || [];
NodeHelpers.getNodeInputs(this.workflow, this.data!, nodeTypeData) || [];
this.inputs = inputs;

const sortedInputs = [...inputs];
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions packages/editor-ui/src/views/NodeView.vue
Expand Up @@ -58,6 +58,7 @@
:isActive="!!activeNode && activeNode.name === nodeData.name"
:hideActions="pullConnActive"
:isProductionExecutionPreview="isProductionExecutionPreview"
:workflow="currentWorkflowObject"
>
<template #custom-tooltip>
<span
Expand Down Expand Up @@ -691,6 +692,9 @@ export default defineComponent({
instance(): BrowserJsPlumbInstance {
return this.canvasStore.jsPlumbInstance;
},
currentWorkflowObject(): Workflow {
return this.workflowsStore.getCurrentWorkflow();
},
},
data() {
return {
Expand Down

0 comments on commit a8049a0

Please sign in to comment.