diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index bf4a4e6da2..f435e58eab 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -1437,14 +1437,14 @@ class App { limit = parseInt(req.query.limit as string, 10); } - let executingWorkflowIds; + const executingWorkflowIds: string[] = []; if (config.get('executions.mode') === 'queue') { const currentJobs = await Queue.getInstance().getJobs(['active', 'waiting']); - executingWorkflowIds = currentJobs.map(job => job.data.executionId) as string[]; - } else { - executingWorkflowIds = this.activeExecutionsInstance.getActiveExecutions().map(execution => execution.id.toString()) as string[]; + executingWorkflowIds.push(...currentJobs.map(job => job.data.executionId) as string[]); } + // We may have manual executions even with queue so we must account for these. + executingWorkflowIds.push(...this.activeExecutionsInstance.getActiveExecutions().map(execution => execution.id.toString()) as string[]); const countFilter = JSON.parse(JSON.stringify(filter)); countFilter.select = ['id']; @@ -1645,7 +1645,12 @@ class App { if (config.get('executions.mode') === 'queue') { const currentJobs = await Queue.getInstance().getJobs(['active', 'waiting']); - const currentlyRunningExecutionIds = currentJobs.map(job => job.data.executionId); + const currentlyRunningQueueIds = currentJobs.map(job => job.data.executionId); + + const currentlyRunningManualExecutions = this.activeExecutionsInstance.getActiveExecutions(); + const manualExecutionIds = currentlyRunningManualExecutions.map(execution => execution.id); + + const currentlyRunningExecutionIds = currentlyRunningQueueIds.concat(manualExecutionIds); if (currentlyRunningExecutionIds.length === 0) { return []; diff --git a/packages/cli/src/WorkflowExecuteAdditionalData.ts b/packages/cli/src/WorkflowExecuteAdditionalData.ts index 3e60163466..d1ef25985e 100644 --- a/packages/cli/src/WorkflowExecuteAdditionalData.ts +++ b/packages/cli/src/WorkflowExecuteAdditionalData.ts @@ -520,6 +520,10 @@ export async function executeWorkflow(workflowInfo: IExecuteWorkflowInfo, additi // different webooks const additionalDataIntegrated = await getBase(credentials); additionalDataIntegrated.hooks = getWorkflowHooksIntegrated(runData.executionMode, executionId, workflowData!, { parentProcessMode: additionalData.hooks!.mode }); + // Make sure we pass on the original executeWorkflow function we received + // This one already contains changes to talk to parent process + // and get executionID from `activeExecutions` running on main process + additionalDataIntegrated.executeWorkflow = additionalData.executeWorkflow; // Execute the workflow diff --git a/packages/editor-ui/src/components/ExecutionsList.vue b/packages/editor-ui/src/components/ExecutionsList.vue index da18f3f208..051f85513c 100644 --- a/packages/editor-ui/src/components/ExecutionsList.vue +++ b/packages/editor-ui/src/components/ExecutionsList.vue @@ -88,14 +88,17 @@ Success - + Error + + Unknown + - + @@ -410,12 +413,13 @@ export default mixins( this.$store.commit('setActiveExecutions', activeExecutions); }, async loadAutoRefresh () : Promise { - let firstId: string | number | undefined = 0; - if (this.finishedExecutions.length !== 0) { - firstId = this.finishedExecutions[0].id; - } const filter = this.workflowFilterPast; - const pastExecutionsPromise: Promise = this.restApi().getPastExecutions(filter, this.requestItemsPerRequest, undefined, firstId); + // We cannot use firstId here as some executions finish out of order. Let's say + // You have execution ids 500 to 505 running. + // Suppose 504 finishes before 500, 501, 502 and 503. + // iF you use firstId, filtering id >= 504 you won't + // ever get ids 500, 501, 502 and 503 when they finish + const pastExecutionsPromise: Promise = this.restApi().getPastExecutions(filter, 30); const currentExecutionsPromise: Promise = this.restApi().getCurrentExecutions({}); const results = await Promise.all([pastExecutionsPromise, currentExecutionsPromise]); @@ -428,7 +432,38 @@ export default mixins( this.$store.commit('setActiveExecutions', results[1]); - this.finishedExecutions.unshift.apply(this.finishedExecutions, results[0].results); + const alreadyPresentExecutionIds = this.finishedExecutions.map(exec => exec.id); + for(let i = results[0].results.length - 1; i >= 0; i--) { + const currentItem = results[0].results[i]; + // Check new results from end to start + // Add new items accordingly. + const executionIndex = alreadyPresentExecutionIds.indexOf(currentItem.id); + if (executionIndex !== -1) { + // Execution that we received is already present. + + if (this.finishedExecutions[executionIndex].finished === false && currentItem.finished === true) { + // Concurrency stuff. This might happen if the execution finishes + // prior to saving all information to database. Somewhat rare but + // With auto refresh and several executions, it happens sometimes. + // So we replace the execution data so it displays correctly. + this.finishedExecutions[executionIndex] = currentItem; + } + + continue; + } + + // Find the correct position to place this newcomer + let j; + for (j = this.finishedExecutions.length - 1; j >= 0; j--) { + if (currentItem.id < this.finishedExecutions[j].id) { + this.finishedExecutions.splice(j + 1, 0, currentItem); + break; + } + } + if (j === -1) { + this.finishedExecutions.unshift(currentItem); + } + } this.finishedExecutionsCount = results[0].count; }, async loadFinishedExecutions (): Promise { @@ -554,6 +589,8 @@ export default mixins( return `The workflow execution was a retry of "${entry.retryOf}" and failed.
New retries have to be started from the original execution.`; } else if (entry.retrySuccessId !== undefined) { return `The workflow execution failed but the retry "${entry.retrySuccessId}" was successful.`; + } else if (entry.stoppedAt === null) { + return 'The workflow execution is probably still running but it may have crashed and n8n cannot safely tell. '; } else { return 'The workflow execution failed.'; } @@ -610,6 +647,10 @@ export default mixins( color: $--custom-error-text; background-color: $--custom-error-background; margin-left: 5px; + &.warning { + background-color: $--custom-warning-background; + color: $--custom-warning-text; + } } .selection-options { @@ -640,6 +681,11 @@ export default mixins( background-color: $--custom-success-background; color: $--custom-success-text; } + + &.warning { + background-color: $--custom-warning-background; + color: $--custom-warning-text; + } } .workflow-name { diff --git a/packages/editor-ui/src/n8n-theme-variables.scss b/packages/editor-ui/src/n8n-theme-variables.scss index f0520c5bf1..6561ffe922 100644 --- a/packages/editor-ui/src/n8n-theme-variables.scss +++ b/packages/editor-ui/src/n8n-theme-variables.scss @@ -22,6 +22,8 @@ $--custom-running-background : #ffffe5; $--custom-running-text : #eb9422; $--custom-success-background : #e3f0e4; $--custom-success-text : #40c351; +$--custom-warning-background : #ffffe5; +$--custom-warning-text : #eb9422; $--custom-node-view-background : #faf9fe;