mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-12 05:17:28 -08:00
fix(core): Filter out certain executions from crash recovery (#9904)
Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
This commit is contained in:
parent
61c20d1ae3
commit
7044d1ca28
|
@ -204,6 +204,12 @@ export class ActiveExecutions {
|
||||||
async shutdown(cancelAll = false) {
|
async shutdown(cancelAll = false) {
|
||||||
let executionIds = Object.keys(this.activeExecutions);
|
let executionIds = Object.keys(this.activeExecutions);
|
||||||
|
|
||||||
|
if (config.getEnv('executions.mode') === 'regular') {
|
||||||
|
// removal of active executions will no longer release capacity back,
|
||||||
|
// so that throttled executions cannot resume during shutdown
|
||||||
|
this.concurrencyControl.disable();
|
||||||
|
}
|
||||||
|
|
||||||
if (cancelAll) {
|
if (cancelAll) {
|
||||||
if (config.getEnv('executions.mode') === 'regular') {
|
if (config.getEnv('executions.mode') === 'regular') {
|
||||||
await this.concurrencyControl.removeAll(this.activeExecutions);
|
await this.concurrencyControl.removeAll(this.activeExecutions);
|
||||||
|
|
|
@ -32,6 +32,7 @@ import { ExecutionService } from '@/executions/execution.service';
|
||||||
import { OwnershipService } from '@/services/ownership.service';
|
import { OwnershipService } from '@/services/ownership.service';
|
||||||
import { WorkflowRunner } from '@/WorkflowRunner';
|
import { WorkflowRunner } from '@/WorkflowRunner';
|
||||||
import { ExecutionRecoveryService } from '@/executions/execution-recovery.service';
|
import { ExecutionRecoveryService } from '@/executions/execution-recovery.service';
|
||||||
|
import { EventRelay } from '@/eventbus/event-relay.service';
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires
|
||||||
const open = require('open');
|
const open = require('open');
|
||||||
|
@ -375,6 +376,10 @@ export class Start extends BaseCommand {
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Container.get(EventRelay).emit('execution-started-during-bootup', {
|
||||||
|
executionId: execution.id,
|
||||||
|
});
|
||||||
|
|
||||||
// do not block - each execution either runs concurrently or is queued
|
// do not block - each execution either runs concurrently or is queued
|
||||||
void workflowRunner.run(data, undefined, false, execution.id);
|
void workflowRunner.run(data, undefined, false, execution.id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,13 @@ import type { WorkflowExecuteMode as ExecutionMode } from 'n8n-workflow';
|
||||||
import type { ExecutionRepository } from '@/databases/repositories/execution.repository';
|
import type { ExecutionRepository } from '@/databases/repositories/execution.repository';
|
||||||
import type { IExecutingWorkflowData } from '@/Interfaces';
|
import type { IExecutingWorkflowData } from '@/Interfaces';
|
||||||
import type { Telemetry } from '@/telemetry';
|
import type { Telemetry } from '@/telemetry';
|
||||||
|
import type { EventRelay } from '@/eventbus/event-relay.service';
|
||||||
|
|
||||||
describe('ConcurrencyControlService', () => {
|
describe('ConcurrencyControlService', () => {
|
||||||
const logger = mock<Logger>();
|
const logger = mock<Logger>();
|
||||||
const executionRepository = mock<ExecutionRepository>();
|
const executionRepository = mock<ExecutionRepository>();
|
||||||
const telemetry = mock<Telemetry>();
|
const telemetry = mock<Telemetry>();
|
||||||
|
const eventRelay = mock<EventRelay>();
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
config.set('executions.concurrency.productionLimit', -1);
|
config.set('executions.concurrency.productionLimit', -1);
|
||||||
|
@ -35,7 +37,12 @@ describe('ConcurrencyControlService', () => {
|
||||||
/**
|
/**
|
||||||
* Act
|
* Act
|
||||||
*/
|
*/
|
||||||
const service = new ConcurrencyControlService(logger, executionRepository, telemetry);
|
const service = new ConcurrencyControlService(
|
||||||
|
logger,
|
||||||
|
executionRepository,
|
||||||
|
telemetry,
|
||||||
|
eventRelay,
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assert
|
* Assert
|
||||||
|
@ -56,7 +63,7 @@ describe('ConcurrencyControlService', () => {
|
||||||
/**
|
/**
|
||||||
* Act
|
* Act
|
||||||
*/
|
*/
|
||||||
new ConcurrencyControlService(logger, executionRepository, telemetry);
|
new ConcurrencyControlService(logger, executionRepository, telemetry, eventRelay);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
/**
|
/**
|
||||||
* Assert
|
* Assert
|
||||||
|
@ -74,7 +81,12 @@ describe('ConcurrencyControlService', () => {
|
||||||
/**
|
/**
|
||||||
* Act
|
* Act
|
||||||
*/
|
*/
|
||||||
const service = new ConcurrencyControlService(logger, executionRepository, telemetry);
|
const service = new ConcurrencyControlService(
|
||||||
|
logger,
|
||||||
|
executionRepository,
|
||||||
|
telemetry,
|
||||||
|
eventRelay,
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assert
|
* Assert
|
||||||
|
@ -92,7 +104,12 @@ describe('ConcurrencyControlService', () => {
|
||||||
/**
|
/**
|
||||||
* Act
|
* Act
|
||||||
*/
|
*/
|
||||||
const service = new ConcurrencyControlService(logger, executionRepository, telemetry);
|
const service = new ConcurrencyControlService(
|
||||||
|
logger,
|
||||||
|
executionRepository,
|
||||||
|
telemetry,
|
||||||
|
eventRelay,
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Act
|
* Act
|
||||||
|
@ -111,7 +128,12 @@ describe('ConcurrencyControlService', () => {
|
||||||
/**
|
/**
|
||||||
* Act
|
* Act
|
||||||
*/
|
*/
|
||||||
const service = new ConcurrencyControlService(logger, executionRepository, telemetry);
|
const service = new ConcurrencyControlService(
|
||||||
|
logger,
|
||||||
|
executionRepository,
|
||||||
|
telemetry,
|
||||||
|
eventRelay,
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assert
|
* Assert
|
||||||
|
@ -135,7 +157,12 @@ describe('ConcurrencyControlService', () => {
|
||||||
*/
|
*/
|
||||||
config.set('executions.concurrency.productionLimit', 1);
|
config.set('executions.concurrency.productionLimit', 1);
|
||||||
|
|
||||||
const service = new ConcurrencyControlService(logger, executionRepository, telemetry);
|
const service = new ConcurrencyControlService(
|
||||||
|
logger,
|
||||||
|
executionRepository,
|
||||||
|
telemetry,
|
||||||
|
eventRelay,
|
||||||
|
);
|
||||||
const enqueueSpy = jest.spyOn(ConcurrencyQueue.prototype, 'enqueue');
|
const enqueueSpy = jest.spyOn(ConcurrencyQueue.prototype, 'enqueue');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -156,7 +183,12 @@ describe('ConcurrencyControlService', () => {
|
||||||
*/
|
*/
|
||||||
config.set('executions.concurrency.productionLimit', 1);
|
config.set('executions.concurrency.productionLimit', 1);
|
||||||
|
|
||||||
const service = new ConcurrencyControlService(logger, executionRepository, telemetry);
|
const service = new ConcurrencyControlService(
|
||||||
|
logger,
|
||||||
|
executionRepository,
|
||||||
|
telemetry,
|
||||||
|
eventRelay,
|
||||||
|
);
|
||||||
const enqueueSpy = jest.spyOn(ConcurrencyQueue.prototype, 'enqueue');
|
const enqueueSpy = jest.spyOn(ConcurrencyQueue.prototype, 'enqueue');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -180,7 +212,12 @@ describe('ConcurrencyControlService', () => {
|
||||||
*/
|
*/
|
||||||
config.set('executions.concurrency.productionLimit', 1);
|
config.set('executions.concurrency.productionLimit', 1);
|
||||||
|
|
||||||
const service = new ConcurrencyControlService(logger, executionRepository, telemetry);
|
const service = new ConcurrencyControlService(
|
||||||
|
logger,
|
||||||
|
executionRepository,
|
||||||
|
telemetry,
|
||||||
|
eventRelay,
|
||||||
|
);
|
||||||
const dequeueSpy = jest.spyOn(ConcurrencyQueue.prototype, 'dequeue');
|
const dequeueSpy = jest.spyOn(ConcurrencyQueue.prototype, 'dequeue');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -201,7 +238,12 @@ describe('ConcurrencyControlService', () => {
|
||||||
*/
|
*/
|
||||||
config.set('executions.concurrency.productionLimit', 1);
|
config.set('executions.concurrency.productionLimit', 1);
|
||||||
|
|
||||||
const service = new ConcurrencyControlService(logger, executionRepository, telemetry);
|
const service = new ConcurrencyControlService(
|
||||||
|
logger,
|
||||||
|
executionRepository,
|
||||||
|
telemetry,
|
||||||
|
eventRelay,
|
||||||
|
);
|
||||||
const dequeueSpy = jest.spyOn(ConcurrencyQueue.prototype, 'dequeue');
|
const dequeueSpy = jest.spyOn(ConcurrencyQueue.prototype, 'dequeue');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -225,7 +267,12 @@ describe('ConcurrencyControlService', () => {
|
||||||
*/
|
*/
|
||||||
config.set('executions.concurrency.productionLimit', 1);
|
config.set('executions.concurrency.productionLimit', 1);
|
||||||
|
|
||||||
const service = new ConcurrencyControlService(logger, executionRepository, telemetry);
|
const service = new ConcurrencyControlService(
|
||||||
|
logger,
|
||||||
|
executionRepository,
|
||||||
|
telemetry,
|
||||||
|
eventRelay,
|
||||||
|
);
|
||||||
const removeSpy = jest.spyOn(ConcurrencyQueue.prototype, 'remove');
|
const removeSpy = jest.spyOn(ConcurrencyQueue.prototype, 'remove');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -248,7 +295,12 @@ describe('ConcurrencyControlService', () => {
|
||||||
*/
|
*/
|
||||||
config.set('executions.concurrency.productionLimit', 1);
|
config.set('executions.concurrency.productionLimit', 1);
|
||||||
|
|
||||||
const service = new ConcurrencyControlService(logger, executionRepository, telemetry);
|
const service = new ConcurrencyControlService(
|
||||||
|
logger,
|
||||||
|
executionRepository,
|
||||||
|
telemetry,
|
||||||
|
eventRelay,
|
||||||
|
);
|
||||||
const removeSpy = jest.spyOn(ConcurrencyQueue.prototype, 'remove');
|
const removeSpy = jest.spyOn(ConcurrencyQueue.prototype, 'remove');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -271,7 +323,12 @@ describe('ConcurrencyControlService', () => {
|
||||||
*/
|
*/
|
||||||
config.set('executions.concurrency.productionLimit', 2);
|
config.set('executions.concurrency.productionLimit', 2);
|
||||||
|
|
||||||
const service = new ConcurrencyControlService(logger, executionRepository, telemetry);
|
const service = new ConcurrencyControlService(
|
||||||
|
logger,
|
||||||
|
executionRepository,
|
||||||
|
telemetry,
|
||||||
|
eventRelay,
|
||||||
|
);
|
||||||
|
|
||||||
jest
|
jest
|
||||||
.spyOn(ConcurrencyQueue.prototype, 'getAll')
|
.spyOn(ConcurrencyQueue.prototype, 'getAll')
|
||||||
|
@ -310,7 +367,12 @@ describe('ConcurrencyControlService', () => {
|
||||||
*/
|
*/
|
||||||
config.set('executions.concurrency.productionLimit', -1);
|
config.set('executions.concurrency.productionLimit', -1);
|
||||||
|
|
||||||
const service = new ConcurrencyControlService(logger, executionRepository, telemetry);
|
const service = new ConcurrencyControlService(
|
||||||
|
logger,
|
||||||
|
executionRepository,
|
||||||
|
telemetry,
|
||||||
|
eventRelay,
|
||||||
|
);
|
||||||
const enqueueSpy = jest.spyOn(ConcurrencyQueue.prototype, 'enqueue');
|
const enqueueSpy = jest.spyOn(ConcurrencyQueue.prototype, 'enqueue');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -333,7 +395,12 @@ describe('ConcurrencyControlService', () => {
|
||||||
*/
|
*/
|
||||||
config.set('executions.concurrency.productionLimit', -1);
|
config.set('executions.concurrency.productionLimit', -1);
|
||||||
|
|
||||||
const service = new ConcurrencyControlService(logger, executionRepository, telemetry);
|
const service = new ConcurrencyControlService(
|
||||||
|
logger,
|
||||||
|
executionRepository,
|
||||||
|
telemetry,
|
||||||
|
eventRelay,
|
||||||
|
);
|
||||||
const dequeueSpy = jest.spyOn(ConcurrencyQueue.prototype, 'dequeue');
|
const dequeueSpy = jest.spyOn(ConcurrencyQueue.prototype, 'dequeue');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -355,7 +422,12 @@ describe('ConcurrencyControlService', () => {
|
||||||
*/
|
*/
|
||||||
config.set('executions.concurrency.productionLimit', -1);
|
config.set('executions.concurrency.productionLimit', -1);
|
||||||
|
|
||||||
const service = new ConcurrencyControlService(logger, executionRepository, telemetry);
|
const service = new ConcurrencyControlService(
|
||||||
|
logger,
|
||||||
|
executionRepository,
|
||||||
|
telemetry,
|
||||||
|
eventRelay,
|
||||||
|
);
|
||||||
const removeSpy = jest.spyOn(ConcurrencyQueue.prototype, 'remove');
|
const removeSpy = jest.spyOn(ConcurrencyQueue.prototype, 'remove');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -385,7 +457,12 @@ describe('ConcurrencyControlService', () => {
|
||||||
*/
|
*/
|
||||||
config.set('executions.concurrency.productionLimit', CLOUD_TEMP_PRODUCTION_LIMIT);
|
config.set('executions.concurrency.productionLimit', CLOUD_TEMP_PRODUCTION_LIMIT);
|
||||||
config.set('deployment.type', 'cloud');
|
config.set('deployment.type', 'cloud');
|
||||||
const service = new ConcurrencyControlService(logger, executionRepository, telemetry);
|
const service = new ConcurrencyControlService(
|
||||||
|
logger,
|
||||||
|
executionRepository,
|
||||||
|
telemetry,
|
||||||
|
eventRelay,
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Act
|
* Act
|
||||||
|
@ -410,7 +487,12 @@ describe('ConcurrencyControlService', () => {
|
||||||
*/
|
*/
|
||||||
config.set('executions.concurrency.productionLimit', CLOUD_TEMP_PRODUCTION_LIMIT);
|
config.set('executions.concurrency.productionLimit', CLOUD_TEMP_PRODUCTION_LIMIT);
|
||||||
config.set('deployment.type', 'cloud');
|
config.set('deployment.type', 'cloud');
|
||||||
const service = new ConcurrencyControlService(logger, executionRepository, telemetry);
|
const service = new ConcurrencyControlService(
|
||||||
|
logger,
|
||||||
|
executionRepository,
|
||||||
|
telemetry,
|
||||||
|
eventRelay,
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Act
|
* Act
|
||||||
|
@ -437,7 +519,12 @@ describe('ConcurrencyControlService', () => {
|
||||||
*/
|
*/
|
||||||
config.set('executions.concurrency.productionLimit', CLOUD_TEMP_PRODUCTION_LIMIT);
|
config.set('executions.concurrency.productionLimit', CLOUD_TEMP_PRODUCTION_LIMIT);
|
||||||
config.set('deployment.type', 'cloud');
|
config.set('deployment.type', 'cloud');
|
||||||
const service = new ConcurrencyControlService(logger, executionRepository, telemetry);
|
const service = new ConcurrencyControlService(
|
||||||
|
logger,
|
||||||
|
executionRepository,
|
||||||
|
telemetry,
|
||||||
|
eventRelay,
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Act
|
* Act
|
||||||
|
|
|
@ -8,13 +8,14 @@ import { ExecutionRepository } from '@/databases/repositories/execution.reposito
|
||||||
import type { WorkflowExecuteMode as ExecutionMode } from 'n8n-workflow';
|
import type { WorkflowExecuteMode as ExecutionMode } from 'n8n-workflow';
|
||||||
import type { IExecutingWorkflowData } from '@/Interfaces';
|
import type { IExecutingWorkflowData } from '@/Interfaces';
|
||||||
import { Telemetry } from '@/telemetry';
|
import { Telemetry } from '@/telemetry';
|
||||||
|
import { EventRelay } from '@/eventbus/event-relay.service';
|
||||||
|
|
||||||
export const CLOUD_TEMP_PRODUCTION_LIMIT = 999;
|
export const CLOUD_TEMP_PRODUCTION_LIMIT = 999;
|
||||||
export const CLOUD_TEMP_REPORTABLE_THRESHOLDS = [5, 10, 20, 50, 100, 200];
|
export const CLOUD_TEMP_REPORTABLE_THRESHOLDS = [5, 10, 20, 50, 100, 200];
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class ConcurrencyControlService {
|
export class ConcurrencyControlService {
|
||||||
private readonly isEnabled: boolean;
|
private isEnabled: boolean;
|
||||||
|
|
||||||
private readonly productionLimit: number;
|
private readonly productionLimit: number;
|
||||||
|
|
||||||
|
@ -28,6 +29,7 @@ export class ConcurrencyControlService {
|
||||||
private readonly logger: Logger,
|
private readonly logger: Logger,
|
||||||
private readonly executionRepository: ExecutionRepository,
|
private readonly executionRepository: ExecutionRepository,
|
||||||
private readonly telemetry: Telemetry,
|
private readonly telemetry: Telemetry,
|
||||||
|
private readonly eventRelay: EventRelay,
|
||||||
) {
|
) {
|
||||||
this.productionLimit = config.getEnv('executions.concurrency.productionLimit');
|
this.productionLimit = config.getEnv('executions.concurrency.productionLimit');
|
||||||
|
|
||||||
|
@ -61,6 +63,7 @@ export class ConcurrencyControlService {
|
||||||
|
|
||||||
this.productionQueue.on('execution-throttled', ({ executionId }: { executionId: string }) => {
|
this.productionQueue.on('execution-throttled', ({ executionId }: { executionId: string }) => {
|
||||||
this.log('Execution throttled', { executionId });
|
this.log('Execution throttled', { executionId });
|
||||||
|
this.eventRelay.emit('execution-throttled', { executionId });
|
||||||
});
|
});
|
||||||
|
|
||||||
this.productionQueue.on('execution-released', async (executionId: string) => {
|
this.productionQueue.on('execution-released', async (executionId: string) => {
|
||||||
|
@ -130,6 +133,10 @@ export class ConcurrencyControlService {
|
||||||
this.logger.info('Canceled enqueued executions with response promises', { executionIds });
|
this.logger.info('Canceled enqueued executions with response promises', { executionIds });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
disable() {
|
||||||
|
this.isEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
// private
|
// private
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
|
|
@ -49,7 +49,7 @@ export class ConcurrencyQueue extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
getAll() {
|
getAll() {
|
||||||
return new Set(...this.queue.map((item) => item.executionId));
|
return new Set(this.queue.map((item) => item.executionId));
|
||||||
}
|
}
|
||||||
|
|
||||||
private resolveNext() {
|
private resolveNext() {
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { AbstractEventMessage, isEventMessageOptionsWithType } from './AbstractEventMessage';
|
||||||
|
import type { JsonObject } from 'n8n-workflow';
|
||||||
|
import { EventMessageTypeNames } from 'n8n-workflow';
|
||||||
|
import type { AbstractEventMessageOptions } from './AbstractEventMessageOptions';
|
||||||
|
import type { AbstractEventPayload } from './AbstractEventPayload';
|
||||||
|
import type { EventNamesExecutionType } from '.';
|
||||||
|
|
||||||
|
export interface EventPayloadExecution extends AbstractEventPayload {
|
||||||
|
executionId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EventMessageExecutionOptions extends AbstractEventMessageOptions {
|
||||||
|
eventName: EventNamesExecutionType;
|
||||||
|
|
||||||
|
payload?: EventPayloadExecution;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EventMessageExecution extends AbstractEventMessage {
|
||||||
|
readonly __type = EventMessageTypeNames.execution;
|
||||||
|
|
||||||
|
eventName: EventNamesExecutionType;
|
||||||
|
|
||||||
|
payload: EventPayloadExecution;
|
||||||
|
|
||||||
|
constructor(options: EventMessageExecutionOptions) {
|
||||||
|
super(options);
|
||||||
|
if (options.payload) this.setPayload(options.payload);
|
||||||
|
if (options.anonymize) {
|
||||||
|
this.anonymize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setPayload(payload: EventPayloadExecution): this {
|
||||||
|
this.payload = payload;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
deserialize(data: JsonObject): this {
|
||||||
|
if (isEventMessageOptionsWithType(data, this.__type)) {
|
||||||
|
this.setOptionsOrDefault(data);
|
||||||
|
if (data.payload) this.setPayload(data.payload as EventPayloadExecution);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import type { EventMessageAiNode } from './EventMessageAiNode';
|
import type { EventMessageAiNode } from './EventMessageAiNode';
|
||||||
import type { EventMessageAudit } from './EventMessageAudit';
|
import type { EventMessageAudit } from './EventMessageAudit';
|
||||||
|
import type { EventMessageExecution } from './EventMessageExecution';
|
||||||
import type { EventMessageGeneric } from './EventMessageGeneric';
|
import type { EventMessageGeneric } from './EventMessageGeneric';
|
||||||
import type { EventMessageNode } from './EventMessageNode';
|
import type { EventMessageNode } from './EventMessageNode';
|
||||||
import type { EventMessageWorkflow } from './EventMessageWorkflow';
|
import type { EventMessageWorkflow } from './EventMessageWorkflow';
|
||||||
|
@ -13,6 +14,10 @@ export const eventNamesWorkflow = [
|
||||||
] as const;
|
] as const;
|
||||||
export const eventNamesGeneric = ['n8n.worker.started', 'n8n.worker.stopped'] as const;
|
export const eventNamesGeneric = ['n8n.worker.started', 'n8n.worker.stopped'] as const;
|
||||||
export const eventNamesNode = ['n8n.node.started', 'n8n.node.finished'] as const;
|
export const eventNamesNode = ['n8n.node.started', 'n8n.node.finished'] as const;
|
||||||
|
export const eventNamesExecution = [
|
||||||
|
'n8n.execution.throttled',
|
||||||
|
'n8n.execution.started-during-bootup',
|
||||||
|
] as const;
|
||||||
export const eventNamesAudit = [
|
export const eventNamesAudit = [
|
||||||
'n8n.audit.user.login.success',
|
'n8n.audit.user.login.success',
|
||||||
'n8n.audit.user.login.failed',
|
'n8n.audit.user.login.failed',
|
||||||
|
@ -42,12 +47,14 @@ export const eventNamesAudit = [
|
||||||
export type EventNamesWorkflowType = (typeof eventNamesWorkflow)[number];
|
export type EventNamesWorkflowType = (typeof eventNamesWorkflow)[number];
|
||||||
export type EventNamesAuditType = (typeof eventNamesAudit)[number];
|
export type EventNamesAuditType = (typeof eventNamesAudit)[number];
|
||||||
export type EventNamesNodeType = (typeof eventNamesNode)[number];
|
export type EventNamesNodeType = (typeof eventNamesNode)[number];
|
||||||
|
export type EventNamesExecutionType = (typeof eventNamesExecution)[number];
|
||||||
export type EventNamesGenericType = (typeof eventNamesGeneric)[number];
|
export type EventNamesGenericType = (typeof eventNamesGeneric)[number];
|
||||||
|
|
||||||
export type EventNamesTypes =
|
export type EventNamesTypes =
|
||||||
| EventNamesAuditType
|
| EventNamesAuditType
|
||||||
| EventNamesWorkflowType
|
| EventNamesWorkflowType
|
||||||
| EventNamesNodeType
|
| EventNamesNodeType
|
||||||
|
| EventNamesExecutionType
|
||||||
| EventNamesGenericType
|
| EventNamesGenericType
|
||||||
| EventNamesAiNodesType
|
| EventNamesAiNodesType
|
||||||
| 'n8n.destination.test';
|
| 'n8n.destination.test';
|
||||||
|
@ -65,4 +72,5 @@ export type EventMessageTypes =
|
||||||
| EventMessageWorkflow
|
| EventMessageWorkflow
|
||||||
| EventMessageAudit
|
| EventMessageAudit
|
||||||
| EventMessageNode
|
| EventMessageNode
|
||||||
|
| EventMessageExecution
|
||||||
| EventMessageAiNode;
|
| EventMessageAiNode;
|
||||||
|
|
|
@ -35,6 +35,8 @@ import {
|
||||||
type EventMessageAiNodeOptions,
|
type EventMessageAiNodeOptions,
|
||||||
} from '../EventMessageClasses/EventMessageAiNode';
|
} from '../EventMessageClasses/EventMessageAiNode';
|
||||||
import { License } from '@/License';
|
import { License } from '@/License';
|
||||||
|
import type { EventMessageExecutionOptions } from '../EventMessageClasses/EventMessageExecution';
|
||||||
|
import { EventMessageExecution } from '../EventMessageClasses/EventMessageExecution';
|
||||||
|
|
||||||
export type EventMessageReturnMode = 'sent' | 'unsent' | 'all' | 'unfinished';
|
export type EventMessageReturnMode = 'sent' | 'unsent' | 'all' | 'unfinished';
|
||||||
|
|
||||||
|
@ -397,4 +399,8 @@ export class MessageEventBus extends EventEmitter {
|
||||||
async sendAiNodeEvent(options: EventMessageAiNodeOptions) {
|
async sendAiNodeEvent(options: EventMessageAiNodeOptions) {
|
||||||
await this.send(new EventMessageAiNode(options));
|
await this.send(new EventMessageAiNode(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async sendExecutionEvent(options: EventMessageExecutionOptions) {
|
||||||
|
await this.send(new EventMessageExecution(options));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -222,6 +222,8 @@ export class MessageEventBusLogWriter {
|
||||||
case 'n8n.workflow.success':
|
case 'n8n.workflow.success':
|
||||||
case 'n8n.workflow.failed':
|
case 'n8n.workflow.failed':
|
||||||
case 'n8n.workflow.crashed':
|
case 'n8n.workflow.crashed':
|
||||||
|
case 'n8n.execution.throttled':
|
||||||
|
case 'n8n.execution.started-during-bootup':
|
||||||
delete results.unfinishedExecutions[executionId];
|
delete results.unfinishedExecutions[executionId];
|
||||||
break;
|
break;
|
||||||
case 'n8n.node.started':
|
case 'n8n.node.started':
|
||||||
|
|
|
@ -51,6 +51,10 @@ export class AuditEventRelay {
|
||||||
);
|
);
|
||||||
this.eventRelay.on('community-package-updated', (event) => this.communityPackageUpdated(event));
|
this.eventRelay.on('community-package-updated', (event) => this.communityPackageUpdated(event));
|
||||||
this.eventRelay.on('community-package-deleted', (event) => this.communityPackageDeleted(event));
|
this.eventRelay.on('community-package-deleted', (event) => this.communityPackageDeleted(event));
|
||||||
|
this.eventRelay.on('execution-throttled', (event) => this.executionThrottled(event));
|
||||||
|
this.eventRelay.on('execution-started-during-bootup', (event) =>
|
||||||
|
this.executionStartedDuringBootup(event),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -339,4 +343,22 @@ export class AuditEventRelay {
|
||||||
payload: { ...user, ...rest },
|
payload: { ...user, ...rest },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execution
|
||||||
|
*/
|
||||||
|
|
||||||
|
private executionThrottled({ executionId }: Event['execution-throttled']) {
|
||||||
|
void this.eventBus.sendExecutionEvent({
|
||||||
|
eventName: 'n8n.execution.throttled',
|
||||||
|
payload: { executionId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private executionStartedDuringBootup({ executionId }: Event['execution-started-during-bootup']) {
|
||||||
|
void this.eventBus.sendExecutionEvent({
|
||||||
|
eventName: 'n8n.execution.started-during-bootup',
|
||||||
|
payload: { executionId },
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,4 +182,12 @@ export type Event = {
|
||||||
packageAuthor?: string;
|
packageAuthor?: string;
|
||||||
packageAuthorEmail?: string;
|
packageAuthorEmail?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
'execution-throttled': {
|
||||||
|
executionId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
'execution-started-during-bootup': {
|
||||||
|
executionId: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,6 +11,7 @@ export const enum EventMessageTypeNames {
|
||||||
confirm = '$$EventMessageConfirm',
|
confirm = '$$EventMessageConfirm',
|
||||||
workflow = '$$EventMessageWorkflow',
|
workflow = '$$EventMessageWorkflow',
|
||||||
node = '$$EventMessageNode',
|
node = '$$EventMessageNode',
|
||||||
|
execution = '$$EventMessageExecution',
|
||||||
aiNode = '$$EventMessageAiNode',
|
aiNode = '$$EventMessageAiNode',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue