Skip to content

Commit

Permalink
fix(core): Remove run data of utility nodes for partial executions v2 (
Browse files Browse the repository at this point in the history
  • Loading branch information
despairblue authored Jan 20, 2025
1 parent 2f81b29 commit b66a9dc
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
import type { IRunData } from 'n8n-workflow';
// NOTE: Diagrams in this file have been created with https://asciiflow.com/#/
// If you update the tests, please update the diagrams as well.
// If you add a test, please create a new diagram.
//
// Map
// 0 means the output has no run data
// 1 means the output has run data
// ►► denotes the node that the user wants to execute to
// XX denotes that the node is disabled
// PD denotes that the node has pinned data

import { NodeConnectionType, type IRunData } from 'n8n-workflow';

import { createNodeData, toITaskData } from './helpers';
import { cleanRunData } from '../clean-run-data';
Expand Down Expand Up @@ -111,4 +122,73 @@ describe('cleanRunData', () => {
[node1.name]: [toITaskData([{ data: { value: 1 } }])],
});
});

// ┌─────┐ ┌────────┐
// │node1├─────►rootNode│
// └─────┘ └───▲────┘
// │
// ┌───┴───┐
// │subNode│
// └───────┘
test('removes run data of sub nodes when the start node is a root node', () => {
// ARRANGE
const node1 = createNodeData({ name: 'Node1' });
const rootNode = createNodeData({ name: 'Root Node' });
const subNode = createNodeData({ name: 'Sub Node' });
const graph = new DirectedGraph()
.addNodes(node1, rootNode, subNode)
.addConnections(
{ from: node1, to: rootNode },
{ from: subNode, to: rootNode, type: NodeConnectionType.AiLanguageModel },
);
const runData: IRunData = {
[node1.name]: [toITaskData([{ data: { value: 1 } }])],
[rootNode.name]: [toITaskData([{ data: { value: 2 } }])],
[subNode.name]: [toITaskData([{ data: { value: 3 } }])],
};

// ACT
const newRunData = cleanRunData(runData, graph, new Set([rootNode]));

// ASSERT
expect(newRunData).toEqual({
[node1.name]: [toITaskData([{ data: { value: 1 } }])],
});
});

// ┌─────┐ ┌─────┐ ┌────────┐
// │node1├───►node2├────►rootNode│
// └─────┘ └─────┘ └───▲────┘
// │
// ┌───┴───┐
// │subNode│
// └───────┘
test('removes run data of sub nodes for root nodes downstream of the start node', () => {
// ARRANGE
const node1 = createNodeData({ name: 'Node1' });
const node2 = createNodeData({ name: 'Node2' });
const rootNode = createNodeData({ name: 'Root Node' });
const subNode = createNodeData({ name: 'Sub Node' });
const graph = new DirectedGraph()
.addNodes(node1, node2, rootNode, subNode)
.addConnections(
{ from: node1, to: node2 },
{ from: node2, to: rootNode },
{ from: subNode, to: rootNode, type: NodeConnectionType.AiLanguageModel },
);
const runData: IRunData = {
[node1.name]: [toITaskData([{ data: { value: 1 } }])],
[node2.name]: [toITaskData([{ data: { value: 1 } }])],
[rootNode.name]: [toITaskData([{ data: { value: 2 } }])],
[subNode.name]: [toITaskData([{ data: { value: 3 } }])],
};

// ACT
const newRunData = cleanRunData(runData, graph, new Set([node2]));

// ASSERT
expect(newRunData).toEqual({
[node1.name]: [toITaskData([{ data: { value: 1 } }])],
});
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { INode, IRunData } from 'n8n-workflow';
import { NodeConnectionType, type INode, type IRunData } from 'n8n-workflow';

import type { DirectedGraph } from './directed-graph';

Expand All @@ -16,10 +16,22 @@ export function cleanRunData(

for (const startNode of startNodes) {
delete newRunData[startNode.name];

const children = graph.getChildren(startNode);
for (const node of [startNode, ...children]) {
delete newRunData[node.name];

// Delete runData for subNodes
const subNodeConnections = graph.getParentConnections(node);
for (const subNodeConnection of subNodeConnections) {
// Sub nodes never use the Main connection type, so this filters our
// the connection that goes upstream of the startNode.
if (subNodeConnection.type === NodeConnectionType.Main) {
continue;
}

for (const child of children) {
delete newRunData[child.name];
delete newRunData[subNodeConnection.from.name];
}
}
}

Expand Down

0 comments on commit b66a9dc

Please sign in to comment.