Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 54 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
d7c9feb
--wip--
despairblue Jul 22, 2024
1f99236
refactor and simplify recreateNodeExecutionStack
despairblue Jul 30, 2024
6e6069d
make sure all functions terminate for graphs with cycles
despairblue Jul 30, 2024
7e4d658
remove deprecated and unused functions
despairblue Jul 31, 2024
861402e
document all tests with diagrams
despairblue Jul 31, 2024
4c5cb16
update call site
despairblue Jul 31, 2024
7da90c7
allow switching between old and new partial execution using client si…
despairblue Jul 31, 2024
26be6f3
clean up tests
despairblue Jul 31, 2024
6268f2c
clean up comments
despairblue Aug 1, 2024
92656d2
fix e2e tests
despairblue Aug 1, 2024
c7970dc
document executionData
despairblue Aug 1, 2024
11bf1f0
rename findSubgraph2 to findSubgraph
despairblue Aug 1, 2024
59f82c8
add skipped failing tests to continue working on nodes with multiple …
despairblue Aug 1, 2024
1dcdd6c
reorganize code
despairblue Aug 1, 2024
c4a5e56
remove console.logs
despairblue Aug 1, 2024
d5b96db
clean up comments again
despairblue Aug 1, 2024
72edb2f
allow setting the default partial execution flow with an env variable
despairblue Aug 1, 2024
5ef2d9f
remove unused import
despairblue Aug 30, 2024
2527d0e
clean up comments
despairblue Aug 30, 2024
2632f6a
intermediate commit to keep track of changes
despairblue Sep 2, 2024
1dc0ac9
another intermediate commit
despairblue Sep 2, 2024
43adb5e
fix frontend stripping down the runData before sending it to the backend
despairblue Sep 3, 2024
601c905
simplify finding start nodes by extracting the logic for finding the …
despairblue Sep 3, 2024
1763826
extract getSourceDataGroups out
despairblue Sep 4, 2024
15112f7
remove notation file
despairblue Sep 4, 2024
e5ef7f9
restore default coverage reports
despairblue Sep 4, 2024
a45517a
improve comments
despairblue Sep 4, 2024
147b70c
remove old code
despairblue Sep 4, 2024
c819fc1
update comments
despairblue Sep 4, 2024
5f00779
update comments
despairblue Sep 4, 2024
415ffac
update comment
despairblue Sep 4, 2024
78f95f7
update comment and clean dead code
despairblue Sep 4, 2024
0c325e4
update comment
despairblue Sep 4, 2024
cd56573
remove console logs
despairblue Sep 4, 2024
d102d74
remove comments
despairblue Sep 4, 2024
aeede0d
simplify defintion
despairblue Sep 10, 2024
18ab57d
add documentation for `partialExecutionVersion`
despairblue Sep 10, 2024
1a143e7
remove unnecessary parameter
despairblue Sep 10, 2024
ac6c54f
improve parameter name
despairblue Sep 10, 2024
6d4cf7a
use an assertion instead of ignoring an error
despairblue Sep 10, 2024
fa857df
explain which way this array is sorted
despairblue Sep 10, 2024
aaeb0a7
improve naming
despairblue Sep 10, 2024
cc7cf3e
remove unused method
despairblue Sep 10, 2024
d5546ac
remove comment and eslint ignore
despairblue Sep 18, 2024
8adf659
unify arrange, act, assert trifecta
despairblue Sep 18, 2024
7c3b1b6
fixup! remove unused method
despairblue Sep 18, 2024
22dfdc1
add docs to `isDirty`
despairblue Sep 18, 2024
a706cd2
add jsdocs for `findStartNodes`
despairblue Sep 18, 2024
3b2a0c3
add jsdocs for `findSubgraph`
despairblue Sep 18, 2024
2ad0600
add jsdocs for `recreateNodeExecutionStack`
despairblue Sep 18, 2024
3a488bd
use assertions for programmer errors
despairblue Sep 18, 2024
11685f5
add jsdoc to DirectedGraph
despairblue Sep 18, 2024
a14e4bf
fix grammar in docs
despairblue Sep 18, 2024
87cbab8
remove commented out code
despairblue Sep 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions cypress/e2e/19-execution.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ describe('Execution', () => {

workflowPage.getters.clearExecutionDataButton().should('be.visible');

cy.intercept('POST', '/rest/workflows/**/run').as('workflowRun');
cy.intercept('POST', '/rest/workflows/**/run?**').as('workflowRun');

workflowPage.getters
.canvasNodeByName('do something with them')
Expand All @@ -525,7 +525,7 @@ describe('Execution', () => {

workflowPage.getters.zoomToFitButton().click();

cy.intercept('POST', '/rest/workflows/**/run').as('workflowRun');
cy.intercept('POST', '/rest/workflows/**/run?**').as('workflowRun');

workflowPage.getters
.canvasNodeByName('If')
Expand All @@ -547,7 +547,7 @@ describe('Execution', () => {

workflowPage.getters.clearExecutionDataButton().should('be.visible');

cy.intercept('POST', '/rest/workflows/**/run').as('workflowRun');
cy.intercept('POST', '/rest/workflows/**/run?**').as('workflowRun');

workflowPage.getters
.canvasNodeByName('NoOp2')
Expand Down Expand Up @@ -576,7 +576,7 @@ describe('Execution', () => {
it('should successfully execute partial executions with nodes attached to the second output', () => {
cy.createFixtureWorkflow('Test_Workflow_pairedItem_incomplete_manual_bug.json');

cy.intercept('POST', '/rest/workflows/**/run').as('workflowRun');
cy.intercept('POST', '/rest/workflows/**/run?**').as('workflowRun');

workflowPage.getters.zoomToFitButton().click();
workflowPage.getters.executeWorkflowButton().click();
Expand All @@ -596,7 +596,7 @@ describe('Execution', () => {
it('should execute workflow partially up to the node that has issues', () => {
cy.createFixtureWorkflow('Test_workflow_partial_execution_with_missing_credentials.json');

cy.intercept('POST', '/rest/workflows/**/run').as('workflowRun');
cy.intercept('POST', '/rest/workflows/**/run?**').as('workflowRun');

workflowPage.getters.zoomToFitButton().click();
workflowPage.getters.executeWorkflowButton().click();
Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/28-debug.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ describe('Debug', () => {
it('should be able to debug executions', () => {
cy.intercept('GET', '/rest/executions?filter=*').as('getExecutions');
cy.intercept('GET', '/rest/executions/*').as('getExecution');
cy.intercept('POST', '/rest/workflows/**/run').as('postWorkflowRun');
cy.intercept('POST', '/rest/workflows/**/run?**').as('postWorkflowRun');

cy.signinAsOwner();

Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/30-editor-after-route-changes.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ describe('Editor actions should work', () => {
it('after switching between Editor and Debug', () => {
cy.intercept('GET', '/rest/executions?filter=*').as('getExecutions');
cy.intercept('GET', '/rest/executions/*').as('getExecution');
cy.intercept('POST', '/rest/workflows/**/run').as('postWorkflowRun');
cy.intercept('POST', '/rest/workflows/**/run?**').as('postWorkflowRun');

editWorkflowAndDeactivate();
workflowPage.actions.executeWorkflow();
Expand Down
2 changes: 1 addition & 1 deletion cypress/pages/workflow-executions-tab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class WorkflowExecutionsTab extends BasePage {
},
createManualExecutions: (count: number) => {
for (let i = 0; i < count; i++) {
cy.intercept('POST', '/rest/workflows/**/run').as('workflowExecution');
cy.intercept('POST', '/rest/workflows/**/run?**').as('workflowExecution');
workflowPage.actions.executeWorkflow();
cy.wait('@workflowExecution');
}
Expand Down
2 changes: 1 addition & 1 deletion cypress/utils/executions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export function runMockWorkflowExecution({
}) {
const executionId = nanoid(8);

cy.intercept('POST', '/rest/workflows/**/run', {
cy.intercept('POST', '/rest/workflows/**/run?**', {
statusCode: 201,
body: {
data: {
Expand Down
9 changes: 9 additions & 0 deletions packages/cli/src/config/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -640,4 +640,13 @@ export const schema = {
env: 'N8N_PROXY_HOPS',
doc: 'Number of reverse-proxies n8n is running behind',
},

featureFlags: {
partialExecutionVersionDefault: {
format: String,
default: '0',
env: 'PARTIAL_EXECUTION_VERSION_DEFAULT',
doc: 'Set this to 1 to enable the new partial execution logic by default.',
},
},
};
30 changes: 22 additions & 8 deletions packages/cli/src/workflow-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@ export class WorkflowRunner {
}
}

/** Run the workflow */
/** Run the workflow
* @param realtime This is used in queue mode to change the priority of an execution, making sure they are picked up quicker.
despairblue marked this conversation as resolved.
Show resolved Hide resolved
*/
async run(
data: IWorkflowExecutionDataProcess,
loadStaticData?: boolean,
Expand Down Expand Up @@ -278,6 +280,7 @@ export class WorkflowRunner {
data.startNodes === undefined ||
data.startNodes.length === 0
) {
// Full Execution
this.logger.debug(`Execution ID ${executionId} will run executing all nodes.`, {
executionId,
});
Expand All @@ -294,16 +297,27 @@ export class WorkflowRunner {
data.pinData,
);
} else {
// Partial Execution
this.logger.debug(`Execution ID ${executionId} is a partial execution.`, { executionId });
// Execute only the nodes between start and destination nodes
const workflowExecute = new WorkflowExecute(additionalData, data.executionMode);
workflowExecution = workflowExecute.runPartialWorkflow(
workflow,
data.runData,
data.startNodes,
data.destinationNode,
data.pinData,
);

if (data.partialExecutionVersion === '1') {
workflowExecution = workflowExecute.runPartialWorkflow2(
workflow,
data.runData,
data.destinationNode,
data.pinData,
);
} else {
workflowExecution = workflowExecute.runPartialWorkflow(
workflow,
data.runData,
data.startNodes,
data.destinationNode,
data.pinData,
);
}
}

this.activeExecutions.attachWorkflowExecution(executionId, workflowExecution);
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/workflows/workflow-execution.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export class WorkflowExecutionService {
{ workflowData, runData, startNodes, destinationNode }: WorkflowRequest.ManualRunPayload,
user: User,
pushRef?: string,
partialExecutionVersion?: string,
) {
const pinData = workflowData.pinData;
const pinnedTrigger = this.selectPinnedActivatorStarter(
Expand Down Expand Up @@ -135,6 +136,7 @@ export class WorkflowExecutionService {
startNodes,
workflowData,
userId: user.id,
partialExecutionVersion: partialExecutionVersion ?? '0',
};

const hasRunData = (node: INode) => runData !== undefined && !!runData[node.name];
Expand Down
7 changes: 6 additions & 1 deletion packages/cli/src/workflows/workflow.request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ export declare namespace WorkflowRequest {

type NewName = AuthenticatedRequest<{}, {}, {}, { name?: string }>;

type ManualRun = AuthenticatedRequest<{ workflowId: string }, {}, ManualRunPayload>;
type ManualRun = AuthenticatedRequest<
{ workflowId: string },
{},
ManualRunPayload,
{ partialExecutionVersion?: string }
>;

type Share = AuthenticatedRequest<{ workflowId: string }, {}, { shareWithIds: string[] }>;

Expand Down
3 changes: 3 additions & 0 deletions packages/cli/src/workflows/workflows.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,9 @@ export class WorkflowsController {
req.body,
req.user,
req.headers['push-ref'] as string,
req.query.partialExecutionVersion === '-1'
? config.getEnv('featureFlags.partialExecutionVersionDefault')
: req.query.partialExecutionVersion,
);
}

Expand Down
Loading
Loading