1
0
Fork 0
mirror of https://github.com/n8n-io/n8n.git synced 2025-03-05 20:50:17 -08:00

fix(cli): Fix issue with n8n crashing when error in poll method (#4008)

* 🐛 Fix issue with n8n crashing when error in poll method

* Remove unnecessary imports and add async property

* Remove unnecessary imports

*  Move createErrorExecution to genericHelper

*  Improvements

Co-authored-by: Omar Ajoue <krynble@gmail.com>
This commit is contained in:
Ricardo Espinoza 2022-09-15 16:16:54 -04:00 committed by GitHub
parent 07672cce7d
commit 6c41b29ad2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 126 additions and 11 deletions

View file

@ -57,6 +57,7 @@ import { User } from './databases/entities/User';
import { whereClause } from './WorkflowHelpers';
import { WorkflowEntity } from './databases/entities/WorkflowEntity';
import * as ActiveExecutions from './ActiveExecutions';
import { createErrorExecution } from './GenericHelpers';
const activeExecutions = ActiveExecutions.getInstance();
@ -650,7 +651,14 @@ export class ActiveWorkflowRunner {
activation,
);
// eslint-disable-next-line no-underscore-dangle
returnFunctions.__emit = (data: INodeExecutionData[][]): void => {
returnFunctions.__emit = async (
data: INodeExecutionData[][] | ExecutionError,
): Promise<void> => {
if (data instanceof Error) {
await createErrorExecution(data, node, workflowData, workflow, mode);
this.executeErrorWorkflow(data, workflowData, mode);
return;
}
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
Logger.debug(`Received event to trigger execution for workflow "${workflow.name}"`);
WorkflowHelpers.saveStaticData(workflow);

View file

@ -8,12 +8,27 @@
import express from 'express';
import { join as pathJoin } from 'path';
import { readFile as fsReadFile } from 'fs/promises';
import { IDataObject } from 'n8n-workflow';
import {
ExecutionError,
IDataObject,
INode,
IRunExecutionData,
Workflow,
WorkflowExecuteMode,
} from 'n8n-workflow';
import { validate } from 'class-validator';
import config from '../config';
// eslint-disable-next-line import/no-cycle
import { Db, ICredentialsDb, IPackageVersions, ResponseHelper } from '.';
import {
Db,
ICredentialsDb,
IExecutionDb,
IExecutionFlattedDb,
IPackageVersions,
IWorkflowDb,
ResponseHelper,
} from '.';
// eslint-disable-next-line import/order
import { Like } from 'typeorm';
// eslint-disable-next-line import/no-cycle
@ -214,4 +229,85 @@ export async function validateEntity(
}
}
/**
* Create an error execution
*
* @param {INode} node
* @param {IWorkflowDb} workflowData
* @param {Workflow} workflow
* @param {WorkflowExecuteMode} mode
* @returns
* @memberof ActiveWorkflowRunner
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export async function createErrorExecution(
error: ExecutionError,
node: INode,
workflowData: IWorkflowDb,
workflow: Workflow,
mode: WorkflowExecuteMode,
): Promise<void> {
const saveDataErrorExecutionDisabled = workflowData?.settings?.saveDataErrorExecution === 'none';
if (saveDataErrorExecutionDisabled) return;
const executionData: IRunExecutionData = {
startData: {
destinationNode: node.name,
runNodeFilter: [node.name],
},
executionData: {
contextData: {},
nodeExecutionStack: [
{
node,
data: {
main: [
[
{
json: {},
pairedItem: {
item: 0,
},
},
],
],
},
source: null,
},
],
waitingExecution: {},
waitingExecutionSource: {},
},
resultData: {
runData: {
[node.name]: [
{
startTime: 0,
executionTime: 0,
error,
source: [],
},
],
},
error,
lastNodeExecuted: node.name,
},
};
const fullExecutionData: IExecutionDb = {
data: executionData,
mode,
finished: false,
startedAt: new Date(),
workflowData,
workflowId: workflow.id,
stoppedAt: new Date(),
};
const execution = ResponseHelper.flattenExecutionData(fullExecutionData);
await Db.collections.Execution.save(execution as IExecutionFlattedDb);
}
export const DEFAULT_EXECUTIONS_GET_ALL_LIMIT = 20;

View file

@ -163,24 +163,35 @@ export class ActiveWorkflows {
// Get all the trigger times
const cronTimes = (pollTimes.item || []).map(toCronExpression);
// The trigger function to execute when the cron-time got reached
const executeTrigger = async () => {
const executeTrigger = async (testingTrigger = false) => {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
Logger.debug(`Polling trigger initiated for workflow "${workflow.name}"`, {
workflowName: workflow.name,
workflowId: workflow.id,
});
const pollResponse = await workflow.runPoll(node, pollFunctions);
if (pollResponse !== null) {
// eslint-disable-next-line no-underscore-dangle
pollFunctions.__emit(pollResponse);
try {
const pollResponse = await workflow.runPoll(node, pollFunctions);
if (pollResponse !== null) {
// eslint-disable-next-line no-underscore-dangle
pollFunctions.__emit(pollResponse);
}
} catch (error) {
// If the poll function failes in the first activation
// throw the error back so we let the user know there is
// an issue with the trigger.
if (testingTrigger) {
throw error;
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, no-underscore-dangle
pollFunctions.__emit(error);
}
};
// Execute the trigger directly to be able to know if it works
await executeTrigger();
await executeTrigger(true);
const timezone = pollFunctions.getTimezone();

View file

@ -715,7 +715,7 @@ export interface IHookFunctions {
}
export interface IPollFunctions {
__emit(data: INodeExecutionData[][]): void;
__emit(data: INodeExecutionData[][] | NodeApiError): void;
getCredentials(type: string): Promise<ICredentialDataDecryptedObject>;
getMode(): WorkflowExecuteMode;
getActivationMode(): WorkflowActivateMode;