fix(core): Always save executions when they go into waiting state

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2024-11-05 19:57:39 +01:00
parent 565f8cd8c7
commit 314504c456
No known key found for this signature in database
5 changed files with 38 additions and 63 deletions

View file

@ -1,5 +1,4 @@
import { import {
deepCopy,
ErrorReporterProxy, ErrorReporterProxy,
type IRunExecutionData, type IRunExecutionData,
type ITaskData, type ITaskData,
@ -87,37 +86,6 @@ test('should update execution when saving progress is enabled', async () => {
expect(reporterSpy).not.toHaveBeenCalled(); expect(reporterSpy).not.toHaveBeenCalled();
}); });
test('should update execution when saving progress is disabled, but waitTill is defined', async () => {
jest.spyOn(fnModule, 'toSaveSettings').mockReturnValue({
...commonSettings,
progress: false,
});
const reporterSpy = jest.spyOn(ErrorReporterProxy, 'error');
executionRepository.findSingleExecution.mockResolvedValue({} as IExecutionResponse);
const args = deepCopy(commonArgs);
args[4].waitTill = new Date();
await saveExecutionProgress(...args);
expect(executionRepository.updateExistingExecution).toHaveBeenCalledWith('some-execution-id', {
data: {
executionData: undefined,
resultData: {
lastNodeExecuted: 'My Node',
runData: {
'My Node': [{}],
},
},
startData: {},
},
status: 'running',
});
expect(reporterSpy).not.toHaveBeenCalled();
});
test('should report error on failure', async () => { test('should report error on failure', async () => {
jest.spyOn(fnModule, 'toSaveSettings').mockReturnValue({ jest.spyOn(fnModule, 'toSaveSettings').mockReturnValue({
...commonSettings, ...commonSettings,

View file

@ -16,7 +16,7 @@ export async function saveExecutionProgress(
) { ) {
const saveSettings = toSaveSettings(workflowData.settings); const saveSettings = toSaveSettings(workflowData.settings);
if (!saveSettings.progress && !executionData.waitTill) return; if (!saveSettings.progress) return;
const logger = Container.get(Logger); const logger = Container.get(Logger);

View file

@ -18,20 +18,20 @@ export function toSaveSettings(workflowSettings: IWorkflowSettings = {}) {
PROGRESS: config.getEnv('executions.saveExecutionProgress'), PROGRESS: config.getEnv('executions.saveExecutionProgress'),
}; };
const {
saveDataErrorExecution = DEFAULTS.ERROR,
saveDataSuccessExecution = DEFAULTS.SUCCESS,
saveManualExecutions = DEFAULTS.MANUAL,
saveExecutionProgress = DEFAULTS.PROGRESS,
} = workflowSettings;
return { return {
error: workflowSettings.saveDataErrorExecution error: saveDataErrorExecution === 'DEFAULT' ? DEFAULTS.ERROR : saveDataErrorExecution === 'all',
? workflowSettings.saveDataErrorExecution !== 'none' success:
: DEFAULTS.ERROR !== 'none', saveDataSuccessExecution === 'DEFAULT'
success: workflowSettings.saveDataSuccessExecution ? DEFAULTS.SUCCESS
? workflowSettings.saveDataSuccessExecution !== 'none' : saveDataSuccessExecution === 'all',
: DEFAULTS.SUCCESS !== 'none', manual: saveManualExecutions === 'DEFAULT' ? DEFAULTS.MANUAL : saveManualExecutions,
manual: progress: saveExecutionProgress === 'DEFAULT' ? DEFAULTS.PROGRESS : saveExecutionProgress,
workflowSettings === undefined || workflowSettings.saveManualExecutions === 'DEFAULT'
? DEFAULTS.MANUAL
: (workflowSettings.saveManualExecutions ?? DEFAULTS.MANUAL),
progress:
workflowSettings === undefined || workflowSettings.saveExecutionProgress === 'DEFAULT'
? DEFAULTS.PROGRESS
: (workflowSettings.saveExecutionProgress ?? DEFAULTS.PROGRESS),
}; };
} }

View file

@ -464,6 +464,11 @@ export async function executeWebhook(
projectId: project?.id, projectId: project?.id,
}; };
// When resuming from a wait node, copy over the pushRef from the execution-data
if (!runData.pushRef) {
runData.pushRef = runExecutionData.pushRef;
}
let responsePromise: IDeferredPromise<IN8nHttpFullResponse> | undefined; let responsePromise: IDeferredPromise<IN8nHttpFullResponse> | undefined;
if (responseMode === 'responseNode') { if (responseMode === 'responseNode') {
responsePromise = createDeferredPromise<IN8nHttpFullResponse>(); responsePromise = createDeferredPromise<IN8nHttpFullResponse>();

View file

@ -448,7 +448,7 @@ function hookFunctionsSave(): IWorkflowExecuteHooks {
const saveSettings = toSaveSettings(this.workflowData.settings); const saveSettings = toSaveSettings(this.workflowData.settings);
if (isManualMode && !saveSettings.manual && !fullRunData.waitTill) { if (isManualMode && !saveSettings.manual && fullRunData.finished) {
/** /**
* When manual executions are not being saved, we only soft-delete * When manual executions are not being saved, we only soft-delete
* the execution so that the user can access its binary data * the execution so that the user can access its binary data
@ -468,22 +468,21 @@ function hookFunctionsSave(): IWorkflowExecuteHooks {
(executionStatus === 'success' && !saveSettings.success) || (executionStatus === 'success' && !saveSettings.success) ||
(executionStatus !== 'success' && !saveSettings.error); (executionStatus !== 'success' && !saveSettings.error);
if (shouldNotSave && !fullRunData.waitTill) { if (shouldNotSave && fullRunData.finished && !isManualMode) {
if (!fullRunData.waitTill && !isManualMode) { executeErrorWorkflow(
executeErrorWorkflow( this.workflowData,
this.workflowData, fullRunData,
fullRunData, this.mode,
this.mode, this.executionId,
this.executionId, this.retryOf,
this.retryOf, );
);
await Container.get(ExecutionRepository).hardDelete({
workflowId: this.workflowData.id,
executionId: this.executionId,
});
return; await Container.get(ExecutionRepository).hardDelete({
} workflowId: this.workflowData.id,
executionId: this.executionId,
});
return;
} }
// Although it is treated as IWorkflowBase here, it's being instantiated elsewhere with properties that may be sensitive // Although it is treated as IWorkflowBase here, it's being instantiated elsewhere with properties that may be sensitive
@ -1117,6 +1116,9 @@ export function getWorkflowHooksWorkerMain(
hookFunctions.nodeExecuteAfter = []; hookFunctions.nodeExecuteAfter = [];
hookFunctions.workflowExecuteAfter = [ hookFunctions.workflowExecuteAfter = [
async function (this: WorkflowHooks, fullRunData: IRun): Promise<void> { async function (this: WorkflowHooks, fullRunData: IRun): Promise<void> {
// Don't delete executions before they are finished
if (!fullRunData.finished) return;
const executionStatus = determineFinalExecutionStatus(fullRunData); const executionStatus = determineFinalExecutionStatus(fullRunData);
const saveSettings = toSaveSettings(this.workflowData.settings); const saveSettings = toSaveSettings(this.workflowData.settings);