@autofleet/rabbit 3.3.0-beta.0 → 3.3.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +18 -12
- package/dist/index.js +185 -107
- package/dist/lib/celery.d.ts +9 -0
- package/dist/lib/celery.js +54 -0
- package/dist/lib/consts.d.ts +5 -2
- package/dist/lib/consts.js +7 -3
- package/dist/lib/types.d.ts +10 -0
- package/dist/lib/utils.d.ts +2 -2
- package/package.json +5 -2
- package/src/index.ts +230 -132
- package/src/lib/celery.ts +89 -0
- package/src/lib/consts.ts +6 -2
- package/src/lib/types.ts +12 -0
- package/src/lib/utils.ts +6 -2
- package/coverage/clover.xml +0 -7
- package/coverage/coverage-final.json +0 -1
- package/coverage/lcov-report/base.css +0 -212
- package/coverage/lcov-report/index.html +0 -60
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -1
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -158
- package/coverage/lcov.info +0 -0
package/src/index.ts
CHANGED
|
@@ -19,8 +19,8 @@ import RabbitError from './lib/rabbitError';
|
|
|
19
19
|
import getRedisInstance, { RedisConfig } from './lib/redis';
|
|
20
20
|
import { assertExchangeFanout, rand, wrapSetImmediate } from './lib/utils';
|
|
21
21
|
import {
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
AUTOMATION_ID_HEADER,
|
|
23
|
+
ConnectionPurpose,
|
|
24
24
|
DEFAULT_LOCK_TIMEOUT,
|
|
25
25
|
DEFAULT_OPTIONS,
|
|
26
26
|
RETRY_HEADER,
|
|
@@ -35,14 +35,17 @@ import {
|
|
|
35
35
|
CustomMessageHeaders,
|
|
36
36
|
QueuesCache,
|
|
37
37
|
RedisLockType,
|
|
38
|
-
ExchangesCache,
|
|
38
|
+
ExchangesCache,
|
|
39
|
+
CONSUMER_DEFAULT_OPTIONS,
|
|
40
|
+
QueueSetupPromisesDictionary,
|
|
41
|
+
AssertExchangePromisesDictionary,
|
|
42
|
+
ConnectionData,
|
|
39
43
|
} from './lib/types';
|
|
40
44
|
|
|
41
45
|
// const debug = nodeDebug('af-rabbitmq')
|
|
42
46
|
const debug = logger.debug.bind(logger);
|
|
43
47
|
|
|
44
48
|
const PUBLISH_TIMEOUT = 1000 * 10;
|
|
45
|
-
|
|
46
49
|
export interface IAfRabbitMq {
|
|
47
50
|
ack: any;
|
|
48
51
|
nack: any;
|
|
@@ -76,21 +79,19 @@ export interface AfRabbitOptions {
|
|
|
76
79
|
dontRetryAssert?: boolean;
|
|
77
80
|
|
|
78
81
|
rabbitHost?: string;
|
|
79
|
-
|
|
80
|
-
serviceName: string;
|
|
81
|
-
|
|
82
|
-
podIp?: string;
|
|
83
82
|
}
|
|
84
83
|
|
|
85
84
|
type newChannelOpts = {
|
|
86
85
|
name?: string;
|
|
87
86
|
onClose?: null | ((args: any | null) => void);
|
|
88
87
|
options?: CreateChannelOpts | undefined;
|
|
88
|
+
connectionPurpose?: ConnectionPurpose,
|
|
89
89
|
};
|
|
90
90
|
|
|
91
91
|
type assertChannelOpts = {
|
|
92
92
|
channelName?: string;
|
|
93
93
|
force?: boolean;
|
|
94
|
+
connectionPurpose?: ConnectionPurpose;
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
type AfConsumer = {
|
|
@@ -102,7 +103,7 @@ type AfConsumer = {
|
|
|
102
103
|
const HEARTBEAT = '60';
|
|
103
104
|
|
|
104
105
|
class RabbitMq implements IAfRabbitMq {
|
|
105
|
-
static parseMsg(msg: any)
|
|
106
|
+
static parseMsg(msg: any): any {
|
|
106
107
|
let { content } = msg;
|
|
107
108
|
content = content.toString();
|
|
108
109
|
|
|
@@ -143,17 +144,18 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
143
144
|
|
|
144
145
|
RECONNECT_MSG = 'rabbit: connection disconnect - reconnecting';
|
|
145
146
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
blockReconnect: boolean | null | undefined
|
|
147
|
+
publishChannel: ChannelWrapper | null;
|
|
149
148
|
|
|
150
|
-
|
|
149
|
+
publishChannelSetupPromise: Promise<ChannelWrapper> | null;
|
|
151
150
|
|
|
152
|
-
|
|
151
|
+
blockReconnect: boolean | null | undefined;
|
|
153
152
|
|
|
154
|
-
|
|
153
|
+
connectionsMap: {
|
|
154
|
+
[ConnectionPurpose.Consume]: ConnectionData;
|
|
155
|
+
[ConnectionPurpose.Publish]: ConnectionData;
|
|
156
|
+
};
|
|
155
157
|
|
|
156
|
-
|
|
158
|
+
em: EventEmitter;
|
|
157
159
|
|
|
158
160
|
exchanges: ExchangesCache;
|
|
159
161
|
|
|
@@ -161,6 +163,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
161
163
|
|
|
162
164
|
queueSetupPromises: QueueSetupPromisesDictionary;
|
|
163
165
|
|
|
166
|
+
assertExchangePromises: AssertExchangePromisesDictionary;
|
|
167
|
+
|
|
164
168
|
options: AfRabbitOptions | undefined;
|
|
165
169
|
|
|
166
170
|
redisClient: any;
|
|
@@ -174,15 +178,28 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
174
178
|
|
|
175
179
|
constructor(options?: AfRabbitOptions, redisConfig?: RedisConfig) {
|
|
176
180
|
this.em = new EventEmitter();
|
|
177
|
-
this.
|
|
178
|
-
this.
|
|
179
|
-
this.
|
|
181
|
+
this.publishChannel = null;
|
|
182
|
+
this.publishChannelSetupPromise = null;
|
|
183
|
+
this.connectionsMap = {
|
|
184
|
+
[ConnectionPurpose.Consume]: {
|
|
185
|
+
connection: null,
|
|
186
|
+
creatingConnection: false,
|
|
187
|
+
connectionCreatedEventName: 'consumeConnectionCreated',
|
|
188
|
+
connectionFailedEventName: 'consumeConnectionFailed',
|
|
189
|
+
},
|
|
190
|
+
[ConnectionPurpose.Publish]: {
|
|
191
|
+
connection: null,
|
|
192
|
+
creatingConnection: false,
|
|
193
|
+
connectionCreatedEventName: 'publishConnectionCreated',
|
|
194
|
+
connectionFailedEventName: 'publishConnectionFailed',
|
|
195
|
+
},
|
|
196
|
+
};
|
|
180
197
|
this.exchanges = {};
|
|
181
198
|
this.queues = {};
|
|
182
199
|
this.queueSetupPromises = {};
|
|
200
|
+
this.assertExchangePromises = {};
|
|
183
201
|
this.consumers = [];
|
|
184
202
|
this.options = options;
|
|
185
|
-
this.podId = `${options?.serviceName}${options?.podIp ? `-${options.podIp}` : ''}`;
|
|
186
203
|
this.redisClient = redisConfig && getRedisInstance(redisConfig);
|
|
187
204
|
if (this.redisClient) {
|
|
188
205
|
this.redisLock = promisify(RedisLock(this.redisClient)) as RedisLockType;
|
|
@@ -213,7 +230,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
213
230
|
return false;
|
|
214
231
|
}
|
|
215
232
|
|
|
216
|
-
public ack = (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp = false, releaseLock = null) => async (userMsg: ConsumeMessage)
|
|
233
|
+
public ack = (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp = false, releaseLock = null) => async (userMsg: ConsumeMessage): Promise<any> => {
|
|
217
234
|
if (msg) {
|
|
218
235
|
debug('rabbit acking message', { deliveryTag: msg.fields.deliveryTag });
|
|
219
236
|
await channel.ack(msg);
|
|
@@ -239,8 +256,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
239
256
|
userMsg: ConsumeMessageOrNull,
|
|
240
257
|
{
|
|
241
258
|
skipRetry = false,
|
|
242
|
-
}: NackOptions = {
|
|
243
|
-
)
|
|
259
|
+
}: NackOptions = {},
|
|
260
|
+
): Promise<any> => {
|
|
244
261
|
await this.unlockRedisIfNeeded(releaseLock);
|
|
245
262
|
if (channel && msg) {
|
|
246
263
|
if (
|
|
@@ -274,30 +291,36 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
274
291
|
}
|
|
275
292
|
}
|
|
276
293
|
|
|
277
|
-
async getConnection() {
|
|
278
|
-
return new Promise<AmqpConnectionManager>(async (resolve, reject) => {
|
|
294
|
+
async getConnection(connectionPurpose: ConnectionPurpose) {
|
|
295
|
+
return new Promise<AmqpConnectionManager | undefined | null>(async (resolve, reject) => {
|
|
296
|
+
const {
|
|
297
|
+
connection,
|
|
298
|
+
creatingConnection,
|
|
299
|
+
connectionCreatedEventName,
|
|
300
|
+
connectionFailedEventName,
|
|
301
|
+
} = this.connectionsMap[connectionPurpose];
|
|
279
302
|
if (this.blockReconnect) {
|
|
280
303
|
debug('rabbit: block reconnect');
|
|
281
304
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
282
305
|
// @ts-ignore
|
|
283
306
|
return resolve();
|
|
284
307
|
}
|
|
285
|
-
if (
|
|
286
|
-
if (this.options?.disableReconnect ||
|
|
308
|
+
if (connection !== null) {
|
|
309
|
+
if (this.options?.disableReconnect || connection?.isConnected()) {
|
|
287
310
|
debug('rabbit: connection - is connected');
|
|
288
311
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
289
312
|
// @ts-ignore
|
|
290
|
-
return resolve(
|
|
313
|
+
return resolve(connection);
|
|
291
314
|
}
|
|
292
315
|
debug('rabbit: connection - reconnecting');
|
|
293
316
|
}
|
|
294
|
-
if (
|
|
317
|
+
if (creatingConnection) {
|
|
295
318
|
debug('rabbit: creating connection emi');
|
|
296
|
-
this.em.once(
|
|
297
|
-
this.em.once(
|
|
319
|
+
this.em.once(connectionCreatedEventName, resolve);
|
|
320
|
+
this.em.once(connectionFailedEventName, reject);
|
|
298
321
|
return;
|
|
299
322
|
}
|
|
300
|
-
this.creatingConnection = true;
|
|
323
|
+
this.connectionsMap[connectionPurpose].creatingConnection = true;
|
|
301
324
|
let isResolved = false;
|
|
302
325
|
|
|
303
326
|
// It is import to use it as a function and not as a variable
|
|
@@ -314,31 +337,33 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
314
337
|
};
|
|
315
338
|
|
|
316
339
|
const defaultUrls = findServers();
|
|
317
|
-
const
|
|
340
|
+
const newConnection: AmqpConnectionManager = await connect(defaultUrls, {
|
|
318
341
|
findServers,
|
|
319
342
|
});
|
|
320
343
|
|
|
321
|
-
this.connection =
|
|
322
|
-
|
|
344
|
+
this.connectionsMap[connectionPurpose].connection = newConnection;
|
|
345
|
+
logger.info(`rabbit: created new connection ${connectionPurpose}`);
|
|
346
|
+
|
|
347
|
+
newConnection.on('error', (err) => {
|
|
323
348
|
logger.error('rabbit: connection error', { err });
|
|
324
349
|
if (!isResolved) {
|
|
325
350
|
isResolved = true;
|
|
326
351
|
reject(err);
|
|
327
|
-
this.em.emit(
|
|
352
|
+
this.em.emit(connectionFailedEventName, err);
|
|
328
353
|
}
|
|
329
354
|
});
|
|
330
355
|
|
|
331
|
-
|
|
356
|
+
newConnection.on('connectFailed', (err) => {
|
|
332
357
|
this.consumersTags = [];
|
|
333
358
|
logger.error('rabbit: connection connectFailed', { err });
|
|
334
359
|
if (!isResolved) {
|
|
335
360
|
isResolved = true;
|
|
336
361
|
reject(err);
|
|
337
|
-
this.em.emit(
|
|
362
|
+
this.em.emit(connectionFailedEventName, err);
|
|
338
363
|
}
|
|
339
364
|
});
|
|
340
365
|
|
|
341
|
-
|
|
366
|
+
newConnection.on('disconnect', ({ err }) => {
|
|
342
367
|
this.consumersTags = [];
|
|
343
368
|
debug('rabbit: connection closed');
|
|
344
369
|
if (this.options?.disableReconnect) {
|
|
@@ -348,25 +373,29 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
348
373
|
logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
|
|
349
374
|
}
|
|
350
375
|
});
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
this.
|
|
354
|
-
this.em.emit(CONNECTION_CREATED_CONST, connection);
|
|
376
|
+
newConnection.once('connect', async () => {
|
|
377
|
+
this.connectionsMap[connectionPurpose].creatingConnection = false;
|
|
378
|
+
this.em.emit(connectionCreatedEventName, newConnection);
|
|
355
379
|
isResolved = true;
|
|
356
|
-
resolve(
|
|
380
|
+
resolve(newConnection);
|
|
357
381
|
});
|
|
358
382
|
});
|
|
359
383
|
}
|
|
360
384
|
|
|
361
|
-
async getNewChannel({
|
|
362
|
-
|
|
385
|
+
async getNewChannel({
|
|
386
|
+
name = rand().toString(), onClose = null, options = {}, connectionPurpose = ConnectionPurpose.Consume,
|
|
387
|
+
}: newChannelOpts): Promise<ChannelWrapper> {
|
|
388
|
+
let connection!: AmqpConnectionManager | undefined | null;
|
|
363
389
|
try {
|
|
364
|
-
connection = await this.getConnection();
|
|
390
|
+
connection = await this.getConnection(connectionPurpose);
|
|
365
391
|
} catch (e) {
|
|
366
392
|
logger.error(`rabbit: error on get connection for new channel ${name} `, { e });
|
|
367
393
|
throw e;
|
|
368
394
|
}
|
|
369
|
-
|
|
395
|
+
if (!connection) {
|
|
396
|
+
throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
|
|
397
|
+
}
|
|
398
|
+
const channel = connection.createChannel({ ...options });
|
|
370
399
|
once(channel, 'close').then((args) => {
|
|
371
400
|
logger.error(`rabbit: channel ${name} closed`);
|
|
372
401
|
onClose?.(args);
|
|
@@ -381,48 +410,62 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
381
410
|
}
|
|
382
411
|
}
|
|
383
412
|
|
|
384
|
-
async assertChannel({ force = false }
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
413
|
+
async assertChannel({ force = false, connectionPurpose = ConnectionPurpose.Consume }: assertChannelOpts): Promise<ChannelWrapper> {
|
|
414
|
+
debug('rabbit: start assert channel', { connectionPurpose, publishPromise: this.publishChannelSetupPromise, channel: this.publishChannel });
|
|
415
|
+
if (!this.publishChannelSetupPromise) {
|
|
416
|
+
this.publishChannelSetupPromise = new Promise<ChannelWrapper>(async (resolve, reject) => {
|
|
417
|
+
if (this.publishChannel && !force) {
|
|
418
|
+
return resolve(this.publishChannel);
|
|
419
|
+
}
|
|
389
420
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
421
|
+
try {
|
|
422
|
+
const channel = await this.getNewChannel({ connectionPurpose });
|
|
423
|
+
debug('rabbit: new channel got', { connectionPurpose, channel: this.publishChannel });
|
|
424
|
+
channel.on('error', (err) => {
|
|
425
|
+
logger.error('rabbit: channel error', { err });
|
|
426
|
+
});
|
|
427
|
+
if (connectionPurpose === ConnectionPurpose.Publish) {
|
|
428
|
+
this.publishChannel = channel;
|
|
429
|
+
}
|
|
430
|
+
resolve(channel);
|
|
431
|
+
} catch (e) {
|
|
432
|
+
reject(e);
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
return this.publishChannelSetupPromise;
|
|
401
437
|
}
|
|
402
438
|
|
|
403
|
-
async assertExchange(exchangeName: string, options
|
|
404
|
-
const channel: ChannelWrapper = await this.assertChannel();
|
|
439
|
+
async assertExchange(exchangeName: string, options: any = { connectionPurpose: ConnectionPurpose.Consume }) {
|
|
440
|
+
const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose: options.connectionPurpose });
|
|
405
441
|
if (this.exchanges[exchangeName]) {
|
|
442
|
+
delete this.assertExchangePromises[exchangeName];
|
|
406
443
|
return this.exchanges[exchangeName];
|
|
407
444
|
}
|
|
408
|
-
|
|
409
|
-
this.
|
|
410
|
-
|
|
445
|
+
|
|
446
|
+
if (this.assertExchangePromises[exchangeName]) {
|
|
447
|
+
return this.assertExchangePromises[exchangeName];
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
this.assertExchangePromises[exchangeName] = assertExchangeFanout(channel, exchangeName);
|
|
451
|
+
this.exchanges[exchangeName] = await this.assertExchangePromises[exchangeName];
|
|
452
|
+
return this.exchanges[exchangeName];
|
|
411
453
|
}
|
|
412
454
|
|
|
413
|
-
async getQueueLength(queue: string) {
|
|
455
|
+
async getQueueLength(queue: string, connectionPurpose: ConnectionPurpose = ConnectionPurpose.Consume): Promise<Replies.AssertQueue> {
|
|
414
456
|
RabbitMq.validateName('queue', queue);
|
|
415
|
-
const {
|
|
416
|
-
|
|
457
|
+
const { connection } = this.connectionsMap[connectionPurpose];
|
|
458
|
+
const { publishChannel } = this;
|
|
459
|
+
if (!publishChannel) {
|
|
417
460
|
throw new Error('channel is not defined');
|
|
418
461
|
}
|
|
419
|
-
debug('rabbit: getting queue length', { queue, connected:
|
|
420
|
-
return
|
|
462
|
+
debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
|
|
463
|
+
return publishChannel?.checkQueue(queue);
|
|
421
464
|
}
|
|
422
465
|
|
|
423
|
-
private async deleteQueue(queue: string) {
|
|
466
|
+
private async deleteQueue(queue: string, connectionPurpose: ConnectionPurpose) {
|
|
424
467
|
RabbitMq.validateName('queue', queue);
|
|
425
|
-
const channel: ChannelWrapper = await this.assertChannel();
|
|
468
|
+
const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
|
|
426
469
|
logger.info('rabbit: deleting queue', { queue });
|
|
427
470
|
const deleteQueueRes = await channel.deleteQueue(queue);
|
|
428
471
|
debug('queue deleted', deleteQueueRes);
|
|
@@ -430,40 +473,67 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
430
473
|
}
|
|
431
474
|
|
|
432
475
|
async bindQueue(queue: string, exchange: string) {
|
|
433
|
-
const channel: ChannelWrapper = await this.assertChannel();
|
|
476
|
+
const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose: ConnectionPurpose.Publish });
|
|
434
477
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.bindQueue(queue, exchange, ''));
|
|
435
478
|
return channel.bindQueue(queue, exchange, '');
|
|
436
479
|
}
|
|
437
480
|
|
|
438
481
|
async setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
|
|
439
482
|
let queue: Replies.AssertQueue;
|
|
483
|
+
const connectionPurpose = ConnectionPurpose.Publish;
|
|
484
|
+
const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
|
|
485
|
+
const localeOptions = {
|
|
486
|
+
...options,
|
|
487
|
+
durable: true,
|
|
488
|
+
arguments: {
|
|
489
|
+
...options?.arguments,
|
|
490
|
+
'x-consumer-timeout': 1000 * 60 * 60 * 24,
|
|
491
|
+
'x-queue-type': shouldUseQuorum ? 'quorum' : 'classic',
|
|
492
|
+
},
|
|
493
|
+
};
|
|
440
494
|
try {
|
|
441
|
-
const channel: ChannelWrapper = await this.assertChannel();
|
|
495
|
+
const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
|
|
442
496
|
debug('assertQueue->channel.addSetup', { queueName });
|
|
443
|
-
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName,
|
|
497
|
+
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
444
498
|
debug('assertQueue->channel.assertQueue', { queueName });
|
|
445
|
-
queue = await channel.assertQueue(queueName,
|
|
499
|
+
queue = await channel.assertQueue(queueName, localeOptions);
|
|
446
500
|
} catch (e) {
|
|
447
501
|
logger.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
448
502
|
if (!this.options?.dontRetryAssert) {
|
|
449
503
|
debug('retrying assertQueue', { queueName });
|
|
450
|
-
const channel = await this.assertChannel({ force: true });
|
|
451
|
-
await this.deleteQueue(queueName);
|
|
504
|
+
const channel = await this.assertChannel({ force: true, connectionPurpose });
|
|
505
|
+
await this.deleteQueue(queueName, connectionPurpose);
|
|
452
506
|
|
|
453
507
|
debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
454
|
-
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName,
|
|
508
|
+
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
455
509
|
debug('retrying assertQueue->channel.assertQueue', { queueName });
|
|
456
|
-
queue = await channel.assertQueue(queueName,
|
|
510
|
+
queue = await channel.assertQueue(queueName, localeOptions);
|
|
457
511
|
} else {
|
|
458
512
|
throw e;
|
|
459
513
|
}
|
|
460
514
|
}
|
|
461
515
|
|
|
462
|
-
this.queues[queueName] =
|
|
516
|
+
this.queues[queueName] = queue;
|
|
463
517
|
return queue;
|
|
464
518
|
}
|
|
465
519
|
|
|
520
|
+
static shouldUseQuorum(queueName: string): boolean {
|
|
521
|
+
const envQuorumQueuesWhitelist = process.env.QUORUM_QUEUES_WHITELIST;
|
|
522
|
+
|
|
523
|
+
if (envQuorumQueuesWhitelist === '*') {
|
|
524
|
+
return true;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if (envQuorumQueuesWhitelist) {
|
|
528
|
+
const whitelist = envQuorumQueuesWhitelist.split(',');
|
|
529
|
+
return whitelist.includes(queueName);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
return false;
|
|
533
|
+
}
|
|
534
|
+
|
|
466
535
|
async assertQueue(queueName: string, options?: Options.AssertQueue) {
|
|
536
|
+
debug('rabbit: start assert queue', { queueName });
|
|
467
537
|
RabbitMq.validateName('queue', queueName);
|
|
468
538
|
if (this.queues[queueName]) {
|
|
469
539
|
delete this.queueSetupPromises[queueName];
|
|
@@ -475,11 +545,12 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
475
545
|
}
|
|
476
546
|
|
|
477
547
|
this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
|
|
548
|
+
debug('rabbit: done assert queue', { queueName });
|
|
478
549
|
return this.queueSetupPromises[queueName];
|
|
479
550
|
}
|
|
480
551
|
|
|
481
552
|
private saveConsumer(queue: string, callback: CallbackFunction, options: ConsumeOptions | undefined) {
|
|
482
|
-
const isConsumerExist
|
|
553
|
+
const isConsumerExist: boolean = this.consumers.some((consumer) => consumer.queue === queue);
|
|
483
554
|
if (!isConsumerExist) {
|
|
484
555
|
logger.info(`rabbit: consumer: ${queue} saved in consumer array`);
|
|
485
556
|
this.consumers.push({
|
|
@@ -528,10 +599,10 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
528
599
|
}
|
|
529
600
|
logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
530
601
|
}
|
|
531
|
-
const channel = await this.getNewChannel({
|
|
602
|
+
const channel = await this.getNewChannel({ connectionPurpose: ConnectionPurpose.Consume });
|
|
532
603
|
return channel.addSetup(async (confirmChannel: ConfirmChannel) => {
|
|
533
|
-
await
|
|
534
|
-
await confirmChannel.prefetch(limit,
|
|
604
|
+
const q = await this.assertQueue(queue, optionsWithDefaults);
|
|
605
|
+
await confirmChannel.prefetch(limit, false);
|
|
535
606
|
const { consumerTag } = await confirmChannel.consume(
|
|
536
607
|
queue,
|
|
537
608
|
async (msg: ConsumeMessageOrNull) => {
|
|
@@ -541,6 +612,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
541
612
|
|
|
542
613
|
const traceId = msg.properties.headers[TRACING_HEADER];
|
|
543
614
|
const userId = msg.properties.headers[USER_TRACING_HEADER];
|
|
615
|
+
const automationId = msg.properties.headers[AUTOMATION_ID_HEADER];
|
|
544
616
|
const parsedMessage = RabbitMq.parseMsg(msg);
|
|
545
617
|
const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
|
|
546
618
|
const trace = newTrace(traceTypes.RABBIT);
|
|
@@ -566,7 +638,10 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
566
638
|
}
|
|
567
639
|
|
|
568
640
|
if (auditContext) {
|
|
569
|
-
await auditContext(queue
|
|
641
|
+
await auditContext(queue, {
|
|
642
|
+
userId,
|
|
643
|
+
automationId,
|
|
644
|
+
});
|
|
570
645
|
}
|
|
571
646
|
const shouldConsume = await this.shouldConsumeMessageByTimestamp(parsedMessage);
|
|
572
647
|
if (!shouldConsume) {
|
|
@@ -614,19 +689,19 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
614
689
|
});
|
|
615
690
|
}
|
|
616
691
|
|
|
617
|
-
async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions)
|
|
692
|
+
async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
618
693
|
const optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options };
|
|
619
694
|
RabbitMq.validateName('exchange', exchange);
|
|
620
695
|
RabbitMq.validateName('queue', queue);
|
|
621
696
|
const { limit, deadMessageTtl } = optionsWithDefaults;
|
|
622
697
|
await this.saveConsumer(queue, callback, options);
|
|
623
|
-
const channel: ChannelWrapper = await this.getNewChannel({ name:
|
|
698
|
+
const channel: ChannelWrapper = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}` });
|
|
624
699
|
|
|
625
700
|
return channel.addSetup(async (c: ConfirmChannel) => {
|
|
626
701
|
const assertExchange = await assertExchangeFanout(c, exchange);
|
|
627
702
|
await c.assertQueue(queue);
|
|
628
703
|
this.exchanges[exchange] = assertExchange;
|
|
629
|
-
await c.prefetch(limit,
|
|
704
|
+
await c.prefetch(limit, false);
|
|
630
705
|
return Promise.all([
|
|
631
706
|
c.bindQueue(queue, exchange, ''),
|
|
632
707
|
this.consume(
|
|
@@ -638,11 +713,12 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
638
713
|
});
|
|
639
714
|
}
|
|
640
715
|
|
|
641
|
-
async publish(exchange: string, content: any, customHeaders?: any)
|
|
716
|
+
async publish(exchange: string, content: any, customHeaders?: any): Promise<boolean> {
|
|
717
|
+
debug('rabbit: start publish msg');
|
|
642
718
|
return wrapSetImmediate(async () => {
|
|
643
719
|
RabbitMq.validateName('exchange', exchange);
|
|
644
|
-
const channel: ChannelWrapper = await this.assertChannel();
|
|
645
|
-
await this.assertExchange(exchange);
|
|
720
|
+
const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose: ConnectionPurpose.Publish });
|
|
721
|
+
await this.assertExchange(exchange, { connectionPurpose: ConnectionPurpose.Publish });
|
|
646
722
|
await channel.publish(exchange, '',
|
|
647
723
|
Buffer.from(JSON.stringify(content)),
|
|
648
724
|
RabbitMq.getPublishOptions(customHeaders));
|
|
@@ -654,51 +730,69 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
654
730
|
content: any,
|
|
655
731
|
options?: any,
|
|
656
732
|
customHeaders?: any,
|
|
657
|
-
isBlocking?: boolean,
|
|
658
733
|
): Promise<boolean | undefined> {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
const res = await this.channel?.sendToQueue(queue,
|
|
665
|
-
Buffer.from(JSON.stringify(content)),
|
|
666
|
-
RabbitMq.getPublishOptions(customHeaders));
|
|
667
|
-
debug(`rabbit: sending to queue ${queue}`, { res });
|
|
668
|
-
return res;
|
|
669
|
-
} catch (e) {
|
|
670
|
-
logger.error(`rabbit: failed to send to queue ${queue}`, { e });
|
|
671
|
-
throw e;
|
|
672
|
-
}
|
|
673
|
-
};
|
|
674
|
-
if (isBlocking) {
|
|
675
|
-
return callback();
|
|
734
|
+
try {
|
|
735
|
+
await this.assertChannel({ connectionPurpose: ConnectionPurpose.Publish });
|
|
736
|
+
} catch (e) {
|
|
737
|
+
logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
|
|
738
|
+
throw e;
|
|
676
739
|
}
|
|
677
|
-
return wrapSetImmediate(callback);
|
|
678
|
-
}
|
|
679
740
|
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
logger.error(
|
|
685
|
-
|
|
741
|
+
try {
|
|
742
|
+
RabbitMq.validateName('queue', queue);
|
|
743
|
+
await this.assertQueue(queue, options);
|
|
744
|
+
} catch (e) {
|
|
745
|
+
logger.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
|
|
746
|
+
throw e;
|
|
686
747
|
}
|
|
687
|
-
|
|
748
|
+
|
|
688
749
|
try {
|
|
689
|
-
await
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
750
|
+
const res = await this.publishChannel?.sendToQueue(queue,
|
|
751
|
+
Buffer.from(JSON.stringify(content)),
|
|
752
|
+
RabbitMq.getPublishOptions(customHeaders));
|
|
753
|
+
debug(`rabbit: sending to queue ${queue}`, { res });
|
|
754
|
+
return res;
|
|
693
755
|
} catch (e) {
|
|
694
|
-
logger.error(
|
|
695
|
-
|
|
756
|
+
logger.error(`rabbit sendToQueue: failed to send to queue ${queue}`, { e });
|
|
757
|
+
throw e;
|
|
696
758
|
}
|
|
697
|
-
logger.info('rabbit: isConnected - true');
|
|
698
|
-
return true;
|
|
699
759
|
}
|
|
700
760
|
|
|
701
|
-
async
|
|
761
|
+
async isConnected(): Promise<boolean> {
|
|
762
|
+
debug('rabbit: start is connected');
|
|
763
|
+
const isEachConnectionConnected = await Promise.all(
|
|
764
|
+
Object.entries(this.connectionsMap).map(async ([connectionPurpose, connectionData]) => {
|
|
765
|
+
const { connection } = connectionData;
|
|
766
|
+
if (connection) {
|
|
767
|
+
const isConnected = connection.isConnected();
|
|
768
|
+
if (!isConnected) {
|
|
769
|
+
logger.error('rabbit: isConnected - false', { connectionPurpose });
|
|
770
|
+
return false;
|
|
771
|
+
}
|
|
772
|
+
logger.info('rabbit: isConnected - true', { connectionPurpose });
|
|
773
|
+
|
|
774
|
+
if (connectionPurpose === ConnectionPurpose.Publish) {
|
|
775
|
+
const channel: any = await this.assertChannel({ connectionPurpose: connectionPurpose as ConnectionPurpose });
|
|
776
|
+
try {
|
|
777
|
+
await Promise.all([
|
|
778
|
+
channel.waitForConnect(),
|
|
779
|
+
...this.consumers.map((c: AfConsumer) => channel.checkQueue(c.queue)),
|
|
780
|
+
]);
|
|
781
|
+
} catch (e) {
|
|
782
|
+
logger.error('rabbit: isConnected - false');
|
|
783
|
+
return false;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
} else {
|
|
787
|
+
logger.info('rabbit: connection hasnt initialized yet', { connectionPurpose });
|
|
788
|
+
}
|
|
789
|
+
return true;
|
|
790
|
+
}),
|
|
791
|
+
);
|
|
792
|
+
return isEachConnectionConnected.every((isConnected) => isConnected === true);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
async gracefulShutdown(signal: string): Promise<void> {
|
|
702
796
|
const tagsNumber = this.consumersTags.length;
|
|
703
797
|
logger.info(`rabbit: [gracefully-shutdown] received ${signal}! canceling #${tagsNumber} tags...`);
|
|
704
798
|
const cancelTagPromises = this.consumersTags.map(([channel, tag]) => channel.cancel(tag));
|
|
@@ -715,3 +809,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
715
809
|
}
|
|
716
810
|
|
|
717
811
|
export default RabbitMq;
|
|
812
|
+
|
|
813
|
+
export {
|
|
814
|
+
sendCeleryTaskViaHttp,
|
|
815
|
+
} from './lib/celery';
|