feat: Implement new partial execution logic for acyclic workflows (no-changelog) (#10256)

Co-authored-by: Tomi Turtiainen <10324676+tomi@users.noreply.github.com>
This commit is contained in:
Danny Martini
2024-09-18 15:06:36 +02:00
committed by GitHub
parent 73f89ef101
commit 2a084f96f8
31 changed files with 2367 additions and 20 deletions

View File

@@ -49,6 +49,16 @@ import {
import get from 'lodash/get';
import * as NodeExecuteFunctions from './NodeExecuteFunctions';
import * as assert from 'assert/strict';
import { recreateNodeExecutionStack } from './PartialExecutionUtils/recreateNodeExecutionStack';
import {
DirectedGraph,
findCycles,
findStartNodes,
findSubgraph,
findTriggerForPartialExecution,
} from './PartialExecutionUtils';
export class WorkflowExecute {
private status: ExecutionStatus = 'new';
@@ -305,6 +315,82 @@ export class WorkflowExecute {
return this.processRunExecutionData(workflow);
}
// IMPORTANT: Do not add "async" to this function, it will then convert the
// PCancelable to a regular Promise and does so not allow canceling
// active executions anymore
// eslint-disable-next-line @typescript-eslint/promise-function-async
runPartialWorkflow2(
workflow: Workflow,
runData: IRunData,
destinationNodeName?: string,
pinData?: IPinData,
): PCancelable<IRun> {
// TODO: Refactor the call-site to make `destinationNodeName` a required
// after removing the old partial execution flow.
assert.ok(
destinationNodeName,
'a destinationNodeName is required for the new partial execution flow',
);
const destinationNode = workflow.getNode(destinationNodeName);
assert.ok(
destinationNode,
`Could not find a node with the name ${destinationNodeName} in the workflow.`,
);
// 1. Find the Trigger
const trigger = findTriggerForPartialExecution(workflow, destinationNodeName);
if (trigger === undefined) {
throw new ApplicationError(
'The destination node is not connected to any trigger. Partial executions need a trigger.',
);
}
// 2. Find the Subgraph
const subgraph = findSubgraph(DirectedGraph.fromWorkflow(workflow), destinationNode, trigger);
const filteredNodes = subgraph.getNodes();
// 3. Find the Start Nodes
const startNodes = findStartNodes(subgraph, trigger, destinationNode, runData);
// 4. Detect Cycles
const cycles = findCycles(workflow);
// 5. Handle Cycles
if (cycles.length) {
// TODO: handle
}
// 6. Clean Run Data
// TODO:
// 7. Recreate Execution Stack
const { nodeExecutionStack, waitingExecution, waitingExecutionSource } =
recreateNodeExecutionStack(subgraph, startNodes, destinationNode, runData, pinData ?? {});
// 8. Execute
this.status = 'running';
this.runExecutionData = {
startData: {
destinationNode: destinationNodeName,
runNodeFilter: Array.from(filteredNodes.values()).map((node) => node.name),
},
resultData: {
runData,
pinData,
},
executionData: {
contextData: {},
nodeExecutionStack,
metadata: {},
waitingExecution,
waitingExecutionSource,
},
};
return this.processRunExecutionData(subgraph.toWorkflow({ ...workflow }));
}
/**
* Executes the hook with the given name
*