mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
feat(RabbitMQ Trigger Node): Make message acknowledgement and parallel processing configurable (#3385)
* feat(RabbitMQ Trigger Node): Make message acknowledgement and concurrent processing configurable * ⚡ Make sure that messages do not get executed multiple times * 👕 Fix lint issue * 🐛 Fix issue that for manual executions in "own" mode messages got know acknowledged * ⚡ Increment count now that console.log got removed * ⚡ Improvements * ⚡ Fix default value * ⚡ Improve display name
This commit is contained in:
@@ -4,9 +4,15 @@ import {
|
||||
ITriggerFunctions,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
const amqplib = require('amqplib');
|
||||
import * as amqplib from 'amqplib';
|
||||
|
||||
export async function rabbitmqConnect(this: IExecuteFunctions | ITriggerFunctions, options: IDataObject): Promise<any> { // tslint:disable-line:no-any
|
||||
declare module 'amqplib' {
|
||||
interface Channel {
|
||||
connection: amqplib.Connection;
|
||||
}
|
||||
}
|
||||
|
||||
export async function rabbitmqConnect(this: IExecuteFunctions | ITriggerFunctions, options: IDataObject): Promise<amqplib.Channel> {
|
||||
const credentials = await this.getCredentials('rabbitmq');
|
||||
|
||||
const credentialKeys = [
|
||||
@@ -44,7 +50,7 @@ export async function rabbitmqConnect(this: IExecuteFunctions | ITriggerFunction
|
||||
reject(error);
|
||||
});
|
||||
|
||||
const channel = await connection.createChannel().catch(console.warn);
|
||||
const channel = await connection.createChannel().catch(console.warn) as amqplib.Channel;
|
||||
|
||||
if (options.arguments && ((options.arguments as IDataObject).argument! as IDataObject[]).length) {
|
||||
const additionalArguments: IDataObject = {};
|
||||
@@ -54,7 +60,6 @@ export async function rabbitmqConnect(this: IExecuteFunctions | ITriggerFunction
|
||||
options.arguments = additionalArguments;
|
||||
}
|
||||
|
||||
|
||||
resolve(channel);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
@@ -62,7 +67,7 @@ export async function rabbitmqConnect(this: IExecuteFunctions | ITriggerFunction
|
||||
});
|
||||
}
|
||||
|
||||
export async function rabbitmqConnectQueue(this: IExecuteFunctions | ITriggerFunctions, queue: string, options: IDataObject): Promise<any> { // tslint:disable-line:no-any
|
||||
export async function rabbitmqConnectQueue(this: IExecuteFunctions | ITriggerFunctions, queue: string, options: IDataObject): Promise<amqplib.Channel> {
|
||||
const channel = await rabbitmqConnect.call(this, options);
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
@@ -75,7 +80,7 @@ export async function rabbitmqConnectQueue(this: IExecuteFunctions | ITriggerFun
|
||||
});
|
||||
}
|
||||
|
||||
export async function rabbitmqConnectExchange(this: IExecuteFunctions | ITriggerFunctions, exchange: string, type: string, options: IDataObject): Promise<any> { // tslint:disable-line:no-any
|
||||
export async function rabbitmqConnectExchange(this: IExecuteFunctions | ITriggerFunctions, exchange: string, type: string, options: IDataObject): Promise<amqplib.Channel> {
|
||||
const channel = await rabbitmqConnect.call(this, options);
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
@@ -87,3 +92,53 @@ export async function rabbitmqConnectExchange(this: IExecuteFunctions | ITrigger
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export class MessageTracker {
|
||||
messages: number[] = [];
|
||||
isClosing = false;
|
||||
|
||||
received(message: amqplib.ConsumeMessage) {
|
||||
this.messages.push(message.fields.deliveryTag);
|
||||
}
|
||||
|
||||
answered(message: amqplib.ConsumeMessage) {
|
||||
if (this.messages.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const index = this.messages.findIndex(value => value !== message.fields.deliveryTag);
|
||||
this.messages.splice(index);
|
||||
}
|
||||
|
||||
unansweredMessages() {
|
||||
return this.messages.length;
|
||||
}
|
||||
|
||||
async closeChannel(channel: amqplib.Channel, consumerTag: string) {
|
||||
if (this.isClosing) {
|
||||
return;
|
||||
}
|
||||
this.isClosing = true;
|
||||
|
||||
// Do not accept any new messages
|
||||
await channel.cancel(consumerTag);
|
||||
|
||||
let count = 0;
|
||||
let unansweredMessages = this.unansweredMessages();
|
||||
|
||||
// Give currently executing messages max. 5 minutes to finish before
|
||||
// the channel gets closed. If we would not do that, it would not be possible
|
||||
// to acknowledge messages anymore for which the executions were already running
|
||||
// when for example a new version of the workflow got saved. That would lead to
|
||||
// them getting delivered and processed again.
|
||||
while (unansweredMessages !== 0 && count++ <= 300) {
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, 1000);
|
||||
});
|
||||
unansweredMessages = this.unansweredMessages();
|
||||
}
|
||||
|
||||
await channel.close();
|
||||
await channel.connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user