From f5719371b10acb4d95779a65049b08d126b52e95 Mon Sep 17 00:00:00 2001 From: Alex Grozav Date: Tue, 26 Aug 2025 16:01:15 +0100 Subject: [PATCH] perf(editor): Introduce execution data throttling for logs and canvas updates (no-changelog) (#18812) --- .../src/composables/useCanvasMapping.ts | 21 ++++++++++++------- packages/frontend/editor-ui/src/constants.ts | 7 +++++++ .../logs/composables/useLogsExecutionData.ts | 6 ++++-- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/packages/frontend/editor-ui/src/composables/useCanvasMapping.ts b/packages/frontend/editor-ui/src/composables/useCanvasMapping.ts index 3dba8fdc21..6b47b040ec 100644 --- a/packages/frontend/editor-ui/src/composables/useCanvasMapping.ts +++ b/packages/frontend/editor-ui/src/composables/useCanvasMapping.ts @@ -7,7 +7,7 @@ import { useI18n } from '@n8n/i18n'; import { useNodeTypesStore } from '@/stores/nodeTypes.store'; import { useWorkflowsStore } from '@/stores/workflows.store'; import type { Ref } from 'vue'; -import { computed } from 'vue'; +import { ref, computed } from 'vue'; import type { BoundingBox, CanvasConnection, @@ -46,6 +46,7 @@ import { } from 'n8n-workflow'; import type { INodeUi } from '@/Interface'; import { + CANVAS_EXECUTION_DATA_THROTTLE_DURATION, CUSTOM_API_CALL_KEY, FORM_NODE_TYPE, SIMULATE_NODE_TYPE, @@ -60,6 +61,7 @@ import { getTriggerNodeServiceName } from '@/utils/nodeTypesUtils'; import { useNodeDirtiness } from '@/composables/useNodeDirtiness'; import { getNodeIconSource } from '../utils/nodeIcon'; import * as workflowUtils from 'n8n-workflow/common'; +import { throttledWatch } from '@vueuse/core'; export function useCanvasMapping({ nodes, @@ -355,9 +357,14 @@ export function useCanvasMapping({ }, {}), ); - const nodeExecutionRunDataOutputMapById = computed(() => - Object.keys(nodeExecutionRunDataById.value).reduce>( - (acc, nodeId) => { + const nodeExecutionRunDataOutputMapById = ref>({}); + + throttledWatch( + nodeExecutionRunDataById, + (value) => { + nodeExecutionRunDataOutputMapById.value = Object.keys(value).reduce< + Record + >((acc, nodeId) => { acc[nodeId] = {}; const outputData = { iterations: 0, total: 0 }; @@ -383,9 +390,9 @@ export function useCanvasMapping({ } return acc; - }, - {}, - ), + }, {}); + }, + { throttle: CANVAS_EXECUTION_DATA_THROTTLE_DURATION, immediate: true }, ); const nodeIssuesById = computed(() => diff --git a/packages/frontend/editor-ui/src/constants.ts b/packages/frontend/editor-ui/src/constants.ts index d2f5c365f5..0f1704e52d 100644 --- a/packages/frontend/editor-ui/src/constants.ts +++ b/packages/frontend/editor-ui/src/constants.ts @@ -984,3 +984,10 @@ export const AI_NODES_PACKAGE_NAME = '@n8n/n8n-nodes-langchain'; export const AI_ASSISTANT_MAX_CONTENT_LENGTH = 100; // in kilobytes export const RUN_DATA_DEFAULT_PAGE_SIZE = 25; + +/** + * Performance Optimizations + */ + +export const LOGS_EXECUTION_DATA_THROTTLE_DURATION = 1000; +export const CANVAS_EXECUTION_DATA_THROTTLE_DURATION = 500; diff --git a/packages/frontend/editor-ui/src/features/logs/composables/useLogsExecutionData.ts b/packages/frontend/editor-ui/src/features/logs/composables/useLogsExecutionData.ts index 4c09e86645..8982c16629 100644 --- a/packages/frontend/editor-ui/src/features/logs/composables/useLogsExecutionData.ts +++ b/packages/frontend/editor-ui/src/features/logs/composables/useLogsExecutionData.ts @@ -9,7 +9,7 @@ import { parse } from 'flatted'; import { useToast } from '@/composables/useToast'; import type { LatestNodeInfo, LogEntry } from '../logs.types'; import { isChatNode } from '@/utils/aiUtils'; -import { PLACEHOLDER_EMPTY_WORKFLOW_ID } from '@/constants'; +import { LOGS_EXECUTION_DATA_THROTTLE_DURATION, PLACEHOLDER_EMPTY_WORKFLOW_ID } from '@/constants'; export function useLogsExecutionData() { const nodeHelpers = useNodeHelpers(); @@ -62,7 +62,9 @@ export function useLogsExecutionData() { ); }); - const updateInterval = computed(() => ((entries.value?.length ?? 0) > 1 ? 1000 : 0)); + const updateInterval = computed(() => + (entries.value?.length ?? 0) > 1 ? LOGS_EXECUTION_DATA_THROTTLE_DURATION : 0, + ); function resetExecutionData() { execData.value = undefined;