@autofleet/rabbit 3.3.0-beta.0 → 3.3.0-beta.10
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/.nvmrc +1 -1
- package/dist/index.d.ts +32 -5
- package/dist/index.js +523 -76
- package/dist/lib/celery.d.ts +9 -0
- package/dist/lib/celery.js +54 -0
- package/dist/lib/consts.d.ts +1 -0
- package/dist/lib/consts.js +2 -1
- package/dist/lib/types.d.ts +4 -0
- package/dist/lib/utils.d.ts +2 -2
- package/package.json +5 -2
- package/src/index.ts +619 -86
- package/src/lib/celery.ts +89 -0
- package/src/lib/consts.ts +1 -0
- package/src/lib/types.ts +6 -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,6 +19,7 @@ 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
|
+
AUTOMATION_ID_HEADER,
|
|
22
23
|
CONNECTION_CREATED_CONST,
|
|
23
24
|
CONNECTION_FAILED_CONST,
|
|
24
25
|
DEFAULT_LOCK_TIMEOUT,
|
|
@@ -35,7 +36,10 @@ import {
|
|
|
35
36
|
CustomMessageHeaders,
|
|
36
37
|
QueuesCache,
|
|
37
38
|
RedisLockType,
|
|
38
|
-
ExchangesCache,
|
|
39
|
+
ExchangesCache,
|
|
40
|
+
CONSUMER_DEFAULT_OPTIONS,
|
|
41
|
+
QueueSetupPromisesDictionary,
|
|
42
|
+
AssertExchangePromisesDictionary,
|
|
39
43
|
} from './lib/types';
|
|
40
44
|
|
|
41
45
|
// const debug = nodeDebug('af-rabbitmq')
|
|
@@ -76,10 +80,6 @@ export interface AfRabbitOptions {
|
|
|
76
80
|
dontRetryAssert?: boolean;
|
|
77
81
|
|
|
78
82
|
rabbitHost?: string;
|
|
79
|
-
|
|
80
|
-
serviceName: string;
|
|
81
|
-
|
|
82
|
-
podIp?: string;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
type newChannelOpts = {
|
|
@@ -145,14 +145,14 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
145
145
|
|
|
146
146
|
channel: ChannelWrapper | null;
|
|
147
147
|
|
|
148
|
+
publishChannelSetupPromise: Promise<ChannelWrapper> | null;
|
|
149
|
+
|
|
148
150
|
blockReconnect: boolean | null | undefined
|
|
149
151
|
|
|
150
152
|
connection: AmqpConnectionManager | null | undefined
|
|
151
153
|
|
|
152
154
|
em: EventEmitter;
|
|
153
155
|
|
|
154
|
-
podId: string;
|
|
155
|
-
|
|
156
156
|
creatingConnection: boolean;
|
|
157
157
|
|
|
158
158
|
exchanges: ExchangesCache;
|
|
@@ -161,6 +161,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
161
161
|
|
|
162
162
|
queueSetupPromises: QueueSetupPromisesDictionary;
|
|
163
163
|
|
|
164
|
+
assertExchangePromises: AssertExchangePromisesDictionary;
|
|
165
|
+
|
|
164
166
|
options: AfRabbitOptions | undefined;
|
|
165
167
|
|
|
166
168
|
redisClient: any;
|
|
@@ -172,17 +174,48 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
172
174
|
|
|
173
175
|
private consumers: Array<AfConsumer> = [];
|
|
174
176
|
|
|
175
|
-
|
|
177
|
+
private doesVHostExist = false;
|
|
178
|
+
|
|
179
|
+
private vhost = 'quorum-vhost';
|
|
180
|
+
|
|
181
|
+
// TODO:[QUORUM-PHASE-3] Delete the old properties that we use for the old consumers and publishers
|
|
182
|
+
oldChannel: ChannelWrapper | null;
|
|
183
|
+
|
|
184
|
+
oldPublishChannelSetupPromise: Promise<ChannelWrapper> | null;
|
|
185
|
+
|
|
186
|
+
oldBlockReconnect: boolean | null | undefined
|
|
187
|
+
|
|
188
|
+
oldConnection: AmqpConnectionManager | null | undefined
|
|
189
|
+
|
|
190
|
+
oldEm: EventEmitter;
|
|
191
|
+
|
|
192
|
+
oldCreatingConnection: boolean;
|
|
193
|
+
|
|
194
|
+
oldExchanges: ExchangesCache;
|
|
195
|
+
|
|
196
|
+
oldQueues: QueuesCache;
|
|
197
|
+
|
|
198
|
+
oldQueueSetupPromises: QueueSetupPromisesDictionary;
|
|
199
|
+
|
|
200
|
+
oldAssertExchangePromises: AssertExchangePromisesDictionary;
|
|
201
|
+
|
|
202
|
+
oldConsumersTags: Array<[ConfirmChannel, string]>;
|
|
203
|
+
|
|
204
|
+
private oldConsumers: Array<AfConsumer> = [];
|
|
205
|
+
|
|
206
|
+
constructor(options: AfRabbitOptions = {}, redisConfig?: RedisConfig) {
|
|
176
207
|
this.em = new EventEmitter();
|
|
177
208
|
this.channel = null;
|
|
209
|
+
this.publishChannelSetupPromise = null;
|
|
178
210
|
this.connection = null;
|
|
179
211
|
this.creatingConnection = false;
|
|
180
212
|
this.exchanges = {};
|
|
181
213
|
this.queues = {};
|
|
182
214
|
this.queueSetupPromises = {};
|
|
215
|
+
this.assertExchangePromises = {};
|
|
183
216
|
this.consumers = [];
|
|
184
217
|
this.options = options;
|
|
185
|
-
|
|
218
|
+
|
|
186
219
|
this.redisClient = redisConfig && getRedisInstance(redisConfig);
|
|
187
220
|
if (this.redisClient) {
|
|
188
221
|
this.redisLock = promisify(RedisLock(this.redisClient)) as RedisLockType;
|
|
@@ -197,8 +230,74 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
197
230
|
await this.gracefulShutdown('SIGINT');
|
|
198
231
|
});
|
|
199
232
|
}
|
|
233
|
+
|
|
234
|
+
// TODO: [QUORUM-PHASE-3] Delete the old properties that we use for the old consumers and publishers
|
|
235
|
+
this.oldEm = new EventEmitter();
|
|
236
|
+
this.oldChannel = null;
|
|
237
|
+
this.oldPublishChannelSetupPromise = null;
|
|
238
|
+
this.oldConnection = null;
|
|
239
|
+
this.oldCreatingConnection = false;
|
|
240
|
+
this.oldExchanges = {};
|
|
241
|
+
this.oldQueues = {};
|
|
242
|
+
this.oldQueueSetupPromises = {};
|
|
243
|
+
this.oldAssertExchangePromises = {};
|
|
244
|
+
this.oldConsumers = [];
|
|
245
|
+
this.oldConsumersTags = [];
|
|
200
246
|
}
|
|
201
247
|
|
|
248
|
+
private assertVHost = async () => {
|
|
249
|
+
if (this.doesVHostExist) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const username = process.env.RABBITMQ_USERNAME || 'guest';
|
|
254
|
+
const password = process.env.RABBITMQ_PASSWORD || 'guest';
|
|
255
|
+
const credentials = Buffer.from(`${username}:${password}`).toString('base64');
|
|
256
|
+
const headers = {
|
|
257
|
+
Authorization: `Basic ${credentials}`,
|
|
258
|
+
'Content-Type': 'application/json',
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const rabbitHost = `http://${(this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost').split(':')[0]}:15672`;
|
|
262
|
+
|
|
263
|
+
const url = `${rabbitHost}/api/vhosts/${encodeURIComponent(this.vhost)}`;
|
|
264
|
+
|
|
265
|
+
try {
|
|
266
|
+
const response = await fetch(url, {
|
|
267
|
+
method: 'GET',
|
|
268
|
+
headers,
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
if (response.status === 200) {
|
|
272
|
+
this.doesVHostExist = true;
|
|
273
|
+
logger.info('Vhost exists', { vhost: this.vhost });
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (response.status !== 404) {
|
|
278
|
+
logger.error('Failed to check vhost', { response });
|
|
279
|
+
throw new RabbitError('Failed to check vhost');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const createResponse = await fetch(url, {
|
|
283
|
+
method: 'PUT',
|
|
284
|
+
headers,
|
|
285
|
+
body: JSON.stringify({ default_queue_type: 'quorum' }),
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
if (!createResponse.ok) {
|
|
289
|
+
logger.error('Failed to create vhost', { response: createResponse });
|
|
290
|
+
throw new RabbitError('Failed to create vhost');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
this.doesVHostExist = true;
|
|
294
|
+
logger.info('Vhost created', { vhost: this.vhost });
|
|
295
|
+
} catch (error) {
|
|
296
|
+
logger.error('Failed to check or create vhost', { error });
|
|
297
|
+
throw error;
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
|
|
202
301
|
private shouldConsumeMessageByTimestamp = async (msg: ConsumeMessageOrNull) => {
|
|
203
302
|
if (msg) {
|
|
204
303
|
const { properties: { headers } } = msg;
|
|
@@ -246,13 +345,13 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
246
345
|
if (
|
|
247
346
|
!skipRetry
|
|
248
347
|
&& (
|
|
249
|
-
!msg.properties.headers[RETRY_HEADER]
|
|
250
|
-
|| parseInt(msg.properties.headers[RETRY_HEADER], 10) < options.retries
|
|
348
|
+
!msg.properties.headers?.[RETRY_HEADER]
|
|
349
|
+
|| parseInt(msg.properties.headers?.[RETRY_HEADER], 10) < options.retries
|
|
251
350
|
)
|
|
252
351
|
) {
|
|
253
352
|
await this.sendToQueue(queue, RabbitMq.parseMsg(msg).content, options, {
|
|
254
353
|
...msg.properties.headers,
|
|
255
|
-
[RETRY_HEADER]: msg.properties.headers[RETRY_HEADER]
|
|
354
|
+
[RETRY_HEADER]: msg.properties.headers?.[RETRY_HEADER]
|
|
256
355
|
? msg.properties.headers[RETRY_HEADER] + 1
|
|
257
356
|
: 1,
|
|
258
357
|
});
|
|
@@ -260,7 +359,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
260
359
|
const deadQueue = `${queue}-dead`;
|
|
261
360
|
await this.sendToQueue(deadQueue, RabbitMq.parseMsg(msg).content, deadQueueOptions, {
|
|
262
361
|
...msg.properties.headers,
|
|
263
|
-
[RETRY_HEADER]: msg.properties.headers[RETRY_HEADER]
|
|
362
|
+
[RETRY_HEADER]: msg.properties.headers?.[RETRY_HEADER]
|
|
264
363
|
? msg.properties.headers[RETRY_HEADER] + 1
|
|
265
364
|
: 1,
|
|
266
365
|
});
|
|
@@ -309,8 +408,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
309
408
|
const host = this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost';
|
|
310
409
|
|
|
311
410
|
debug('rabbit: creating connection', { host, userName, HEARTBEAT });
|
|
312
|
-
|
|
313
|
-
return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
|
|
411
|
+
return [`amqp://${userName}:${password}@${host}/${this.vhost}?heartbeat=${HEARTBEAT}`];
|
|
314
412
|
};
|
|
315
413
|
|
|
316
414
|
const defaultUrls = findServers();
|
|
@@ -330,7 +428,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
330
428
|
|
|
331
429
|
this.connection.on('connectFailed', (err) => {
|
|
332
430
|
this.consumersTags = [];
|
|
333
|
-
logger.error('rabbit: connection connectFailed', { err });
|
|
431
|
+
logger.error('rabbit: connection connectFailed', { err, advice: 'Check if the vhost exist', vhost: this.vhost });
|
|
334
432
|
if (!isResolved) {
|
|
335
433
|
isResolved = true;
|
|
336
434
|
reject(err);
|
|
@@ -339,6 +437,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
339
437
|
});
|
|
340
438
|
|
|
341
439
|
this.connection.on('disconnect', ({ err }) => {
|
|
440
|
+
// this.channel = null;
|
|
342
441
|
this.consumersTags = [];
|
|
343
442
|
debug('rabbit: connection closed');
|
|
344
443
|
if (this.options?.disableReconnect) {
|
|
@@ -348,6 +447,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
348
447
|
logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
|
|
349
448
|
}
|
|
350
449
|
});
|
|
450
|
+
|
|
351
451
|
this.connection.once('connect', async () => {
|
|
352
452
|
debug('rabbit: connection established');
|
|
353
453
|
this.creatingConnection = false;
|
|
@@ -366,7 +466,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
366
466
|
logger.error(`rabbit: error on get connection for new channel ${name} `, { e });
|
|
367
467
|
throw e;
|
|
368
468
|
}
|
|
369
|
-
const channel = connection.createChannel({ ...options
|
|
469
|
+
const channel = connection.createChannel({ ...options });
|
|
370
470
|
once(channel, 'close').then((args) => {
|
|
371
471
|
logger.error(`rabbit: channel ${name} closed`);
|
|
372
472
|
onClose?.(args);
|
|
@@ -382,39 +482,50 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
382
482
|
}
|
|
383
483
|
|
|
384
484
|
async assertChannel({ force = false } : assertChannelOpts = {}): Promise<ChannelWrapper> {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
485
|
+
if (!this.publishChannelSetupPromise) {
|
|
486
|
+
this.publishChannelSetupPromise = new Promise<ChannelWrapper>(async (resolve, reject) => {
|
|
487
|
+
if (this.channel && !force) {
|
|
488
|
+
return resolve(this.channel);
|
|
489
|
+
}
|
|
389
490
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
491
|
+
try {
|
|
492
|
+
const channel = await this.getNewChannel({});
|
|
493
|
+
channel.on('error', (err) => {
|
|
494
|
+
logger.error('rabbit: channel error', { err });
|
|
495
|
+
});
|
|
496
|
+
this.channel = channel;
|
|
497
|
+
resolve(channel);
|
|
498
|
+
} catch (e) {
|
|
499
|
+
reject(e);
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
return this.publishChannelSetupPromise;
|
|
401
504
|
}
|
|
402
505
|
|
|
403
506
|
async assertExchange(exchangeName: string, options?: any) {
|
|
404
507
|
const channel: ChannelWrapper = await this.assertChannel();
|
|
508
|
+
|
|
405
509
|
if (this.exchanges[exchangeName]) {
|
|
510
|
+
delete this.assertExchangePromises[exchangeName];
|
|
406
511
|
return this.exchanges[exchangeName];
|
|
407
512
|
}
|
|
408
|
-
|
|
409
|
-
this.
|
|
410
|
-
|
|
513
|
+
|
|
514
|
+
if (this.assertExchangePromises[exchangeName]) {
|
|
515
|
+
return this.assertExchangePromises[exchangeName];
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
this.assertExchangePromises[exchangeName] = assertExchangeFanout(channel, exchangeName);
|
|
519
|
+
this.exchanges[exchangeName] = await this.assertExchangePromises[exchangeName];
|
|
520
|
+
return this.exchanges[exchangeName];
|
|
411
521
|
}
|
|
412
522
|
|
|
523
|
+
// TODO: [QUORUM-PHASE-2] Change the implementation to the new one
|
|
413
524
|
async getQueueLength(queue: string) {
|
|
414
525
|
RabbitMq.validateName('queue', queue);
|
|
415
|
-
const { channel } = this;
|
|
526
|
+
const { oldChannel: channel } = this;
|
|
416
527
|
if (!channel) {
|
|
417
|
-
throw new
|
|
528
|
+
throw new RabbitError('channel is not defined');
|
|
418
529
|
}
|
|
419
530
|
debug('rabbit: getting queue length', { queue, connected: this.connection?.isConnected() });
|
|
420
531
|
return channel?.checkQueue(queue);
|
|
@@ -429,20 +540,32 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
429
540
|
return deleteQueueRes;
|
|
430
541
|
}
|
|
431
542
|
|
|
543
|
+
// TODO: [QUORUM-PHASE-2] Change the implementation to the new one
|
|
432
544
|
async bindQueue(queue: string, exchange: string) {
|
|
433
|
-
const channel: ChannelWrapper = await this.
|
|
545
|
+
const channel: ChannelWrapper = await this.assertChannelOld();
|
|
434
546
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.bindQueue(queue, exchange, ''));
|
|
435
547
|
return channel.bindQueue(queue, exchange, '');
|
|
436
548
|
}
|
|
437
549
|
|
|
438
550
|
async setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
|
|
439
551
|
let queue: Replies.AssertQueue;
|
|
552
|
+
const localeOptions = {
|
|
553
|
+
...options,
|
|
554
|
+
durable: true,
|
|
555
|
+
arguments: {
|
|
556
|
+
...options?.arguments,
|
|
557
|
+
'x-consumer-timeout': 1000 * 60 * 60 * 24,
|
|
558
|
+
'x-queue-type': 'quorum',
|
|
559
|
+
},
|
|
560
|
+
};
|
|
440
561
|
try {
|
|
441
562
|
const channel: ChannelWrapper = await this.assertChannel();
|
|
442
563
|
debug('assertQueue->channel.addSetup', { queueName });
|
|
443
|
-
await channel.addSetup((setupChannel: ConfirmChannel) =>
|
|
564
|
+
await channel.addSetup(async (setupChannel: ConfirmChannel) => {
|
|
565
|
+
await setupChannel.assertQueue(queueName, localeOptions);
|
|
566
|
+
});
|
|
444
567
|
debug('assertQueue->channel.assertQueue', { queueName });
|
|
445
|
-
queue = await channel.assertQueue(queueName,
|
|
568
|
+
queue = await channel.assertQueue(queueName, localeOptions);
|
|
446
569
|
} catch (e) {
|
|
447
570
|
logger.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
448
571
|
if (!this.options?.dontRetryAssert) {
|
|
@@ -451,19 +574,34 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
451
574
|
await this.deleteQueue(queueName);
|
|
452
575
|
|
|
453
576
|
debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
454
|
-
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName,
|
|
577
|
+
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
455
578
|
debug('retrying assertQueue->channel.assertQueue', { queueName });
|
|
456
|
-
queue = await channel.assertQueue(queueName,
|
|
579
|
+
queue = await channel.assertQueue(queueName, localeOptions);
|
|
457
580
|
} else {
|
|
458
581
|
throw e;
|
|
459
582
|
}
|
|
460
583
|
}
|
|
461
|
-
|
|
462
|
-
this.queues[queueName] = queueName;
|
|
584
|
+
this.queues[queueName] = queue;
|
|
463
585
|
return queue;
|
|
464
586
|
}
|
|
465
587
|
|
|
466
|
-
|
|
588
|
+
// TODO: [QUORUM-PHASE-3] Can be deleted after deleting the old consumers and publishers because all the queues are created us quorum queues
|
|
589
|
+
static shouldUseQuorum(queueName: string): boolean {
|
|
590
|
+
const envQuorumQueuesWhitelist = process.env.QUORUM_QUEUES_WHITELIST;
|
|
591
|
+
|
|
592
|
+
if (envQuorumQueuesWhitelist === '*') {
|
|
593
|
+
return true;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
if (envQuorumQueuesWhitelist) {
|
|
597
|
+
const whitelist = envQuorumQueuesWhitelist.split(',');
|
|
598
|
+
return whitelist.includes(queueName);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
return false;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
async assertQueue(queueName: string, options?: Options.AssertQueue): Promise<any> {
|
|
467
605
|
RabbitMq.validateName('queue', queueName);
|
|
468
606
|
if (this.queues[queueName]) {
|
|
469
607
|
delete this.queueSetupPromises[queueName];
|
|
@@ -490,10 +628,27 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
490
628
|
}
|
|
491
629
|
}
|
|
492
630
|
|
|
631
|
+
// Used by the microservices to consume messages from the queue
|
|
493
632
|
async consume(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
633
|
+
// TODO: [QUORUM-PHASE-3] Use only the implementation of consumeNew and delete consumeNew and consumeOld
|
|
634
|
+
if (options?.isQuorumQueue !== false) {
|
|
635
|
+
await this.assertVHost();
|
|
636
|
+
await this.consumeNew(queue, callback, options);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
await this.consumeOld(queue, callback, options);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// TODO: [QUORUM-PHASE-3] Delete consumeNew we do not use it anymore
|
|
643
|
+
async consumeNew(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
494
644
|
await this.consumeFromRabbit(queue, callback, options);
|
|
495
645
|
}
|
|
496
646
|
|
|
647
|
+
// TODO: [QUORUM-PHASE-3] Delete consumeOld we do not use it anymore
|
|
648
|
+
async consumeOld(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
649
|
+
await this.consumeFromRabbitOld(queue, callback, options);
|
|
650
|
+
}
|
|
651
|
+
|
|
497
652
|
private async lockRedisIfNeeded(msg: any, options: any) {
|
|
498
653
|
const { properties: { headers } } = msg;
|
|
499
654
|
const timestamp = headers?.creationTimestamp;
|
|
@@ -524,14 +679,15 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
524
679
|
} = optionsWithDefaults;
|
|
525
680
|
if (useConsumeWithLock) {
|
|
526
681
|
if (!this.redisLock) {
|
|
527
|
-
throw new
|
|
682
|
+
throw new RabbitError('Usage of consumeWithLock requires RedisInstance');
|
|
528
683
|
}
|
|
529
684
|
logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
530
685
|
}
|
|
531
|
-
const channel = await this.getNewChannel({
|
|
686
|
+
const channel = await this.getNewChannel({});
|
|
532
687
|
return channel.addSetup(async (confirmChannel: ConfirmChannel) => {
|
|
533
|
-
|
|
534
|
-
await
|
|
688
|
+
logger.info(`rabbit: channel.addSetup before ${queue} assertQueueOld`);
|
|
689
|
+
const q = await this.assertQueue(queue, optionsWithDefaults);
|
|
690
|
+
await confirmChannel.prefetch(limit, false);
|
|
535
691
|
const { consumerTag } = await confirmChannel.consume(
|
|
536
692
|
queue,
|
|
537
693
|
async (msg: ConsumeMessageOrNull) => {
|
|
@@ -539,8 +695,9 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
539
695
|
return null;
|
|
540
696
|
}
|
|
541
697
|
|
|
542
|
-
const traceId = msg.properties.headers[TRACING_HEADER];
|
|
543
|
-
const userId = msg.properties.headers[USER_TRACING_HEADER];
|
|
698
|
+
const traceId = msg.properties.headers?.[TRACING_HEADER];
|
|
699
|
+
const userId = msg.properties.headers?.[USER_TRACING_HEADER];
|
|
700
|
+
const automationId = msg.properties.headers?.[AUTOMATION_ID_HEADER];
|
|
544
701
|
const parsedMessage = RabbitMq.parseMsg(msg);
|
|
545
702
|
const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
|
|
546
703
|
const trace = newTrace(traceTypes.RABBIT);
|
|
@@ -566,7 +723,10 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
566
723
|
}
|
|
567
724
|
|
|
568
725
|
if (auditContext) {
|
|
569
|
-
await auditContext(queue
|
|
726
|
+
await auditContext(queue, {
|
|
727
|
+
userId,
|
|
728
|
+
automationId,
|
|
729
|
+
});
|
|
570
730
|
}
|
|
571
731
|
const shouldConsume = await this.shouldConsumeMessageByTimestamp(parsedMessage);
|
|
572
732
|
if (!shouldConsume) {
|
|
@@ -614,22 +774,45 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
614
774
|
});
|
|
615
775
|
}
|
|
616
776
|
|
|
777
|
+
// Used by the microservices to consume messages from the exchange
|
|
617
778
|
async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions) : Promise<any> {
|
|
618
779
|
const optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options };
|
|
619
780
|
RabbitMq.validateName('exchange', exchange);
|
|
620
781
|
RabbitMq.validateName('queue', queue);
|
|
621
782
|
const { limit, deadMessageTtl } = optionsWithDefaults;
|
|
622
|
-
|
|
623
|
-
|
|
783
|
+
// TODO: [QUORUM-PHASE-3] Delete the if statement after all the queues are created as quorum queues
|
|
784
|
+
if (options?.isQuorumQueue !== false) {
|
|
785
|
+
await this.assertVHost();
|
|
786
|
+
await this.saveConsumer(queue, callback, options);
|
|
787
|
+
const channel: ChannelWrapper = await this.getNewChannel({ name: `consume-exchange-${exchange}-queue-${queue}` });
|
|
788
|
+
|
|
789
|
+
await channel.addSetup(async (c: ConfirmChannel) => {
|
|
790
|
+
const assertExchange = await assertExchangeFanout(c, exchange);
|
|
791
|
+
await c.assertQueue(queue);
|
|
792
|
+
this.exchanges[exchange] = assertExchange;
|
|
793
|
+
await c.prefetch(limit, false);
|
|
794
|
+
return Promise.all([
|
|
795
|
+
c.bindQueue(queue, exchange, ''),
|
|
796
|
+
this.consumeNew(
|
|
797
|
+
queue,
|
|
798
|
+
callback,
|
|
799
|
+
options,
|
|
800
|
+
),
|
|
801
|
+
]);
|
|
802
|
+
});
|
|
803
|
+
}
|
|
624
804
|
|
|
625
|
-
|
|
805
|
+
// TODO: [QUORUM-PHASE-3] Delete the old implementation
|
|
806
|
+
await this.saveConsumerOld(queue, callback, options);
|
|
807
|
+
const channelOld: ChannelWrapper = await this.getNewChannelOld({ name: `consume-exchange-${exchange}-queue-${queue}-old` });
|
|
808
|
+
await channelOld.addSetup(async (c: ConfirmChannel) => {
|
|
626
809
|
const assertExchange = await assertExchangeFanout(c, exchange);
|
|
627
|
-
await c.assertQueue(queue);
|
|
628
|
-
this.
|
|
629
|
-
await c.prefetch(limit,
|
|
810
|
+
// await c.assertQueue(queue);
|
|
811
|
+
this.oldExchanges[exchange] = assertExchange;
|
|
812
|
+
await c.prefetch(limit, false);
|
|
630
813
|
return Promise.all([
|
|
631
|
-
c.bindQueue(queue, exchange, ''),
|
|
632
|
-
this.
|
|
814
|
+
// c.bindQueue(queue, exchange, ''),
|
|
815
|
+
this.consumeOld(
|
|
633
816
|
queue,
|
|
634
817
|
callback,
|
|
635
818
|
options,
|
|
@@ -638,57 +821,68 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
638
821
|
});
|
|
639
822
|
}
|
|
640
823
|
|
|
824
|
+
// Used by the microservices to publish messages to the exchange
|
|
825
|
+
// TODO: [QUORUM-PHASE-2] Change the implementation to the new one
|
|
641
826
|
async publish(exchange: string, content: any, customHeaders?: any) : Promise<boolean> {
|
|
642
827
|
return wrapSetImmediate(async () => {
|
|
643
828
|
RabbitMq.validateName('exchange', exchange);
|
|
644
|
-
const channel: ChannelWrapper = await this.
|
|
645
|
-
await this.
|
|
829
|
+
const channel: ChannelWrapper = await this.assertChannelOld();
|
|
830
|
+
await this.assertExchangeOld(exchange);
|
|
646
831
|
await channel.publish(exchange, '',
|
|
647
832
|
Buffer.from(JSON.stringify(content)),
|
|
648
833
|
RabbitMq.getPublishOptions(customHeaders));
|
|
649
834
|
});
|
|
650
835
|
}
|
|
651
836
|
|
|
837
|
+
// Used by the microservices to send messages to the queue
|
|
838
|
+
// TODO: [QUORUM-PHASE-2] Change the implementation to the new one
|
|
652
839
|
async sendToQueue(
|
|
653
840
|
queue: string,
|
|
654
841
|
content: any,
|
|
655
842
|
options?: any,
|
|
656
843
|
customHeaders?: any,
|
|
657
|
-
isBlocking?: boolean,
|
|
658
844
|
): Promise<boolean | undefined> {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
845
|
+
try {
|
|
846
|
+
await this.assertChannelOld();
|
|
847
|
+
} catch (e) {
|
|
848
|
+
logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
|
|
849
|
+
throw e;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
try {
|
|
853
|
+
RabbitMq.validateName('queue', queue);
|
|
854
|
+
await this.assertQueueOld(queue, options);
|
|
855
|
+
} catch (e) {
|
|
856
|
+
logger.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
|
|
857
|
+
throw e;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
try {
|
|
861
|
+
const res = await this.oldChannel?.sendToQueue(queue,
|
|
862
|
+
Buffer.from(JSON.stringify(content)),
|
|
863
|
+
RabbitMq.getPublishOptions(customHeaders));
|
|
864
|
+
debug(`rabbit: sending to queue ${queue}`, { res });
|
|
865
|
+
return res;
|
|
866
|
+
} catch (e) {
|
|
867
|
+
const isConnected = await this.isConnected();
|
|
868
|
+
logger.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
|
|
869
|
+
throw e;
|
|
676
870
|
}
|
|
677
|
-
return wrapSetImmediate(callback);
|
|
678
871
|
}
|
|
679
872
|
|
|
873
|
+
// TODO: [QUORUM-PHASE-2] After changing the publish from old to new change from the old to the new implementation
|
|
680
874
|
async isConnected() : Promise<boolean> {
|
|
681
|
-
const connection = await this.
|
|
875
|
+
const connection = await this.getConnectionOld();
|
|
682
876
|
const isConnected = connection.isConnected();
|
|
683
877
|
if (!isConnected) {
|
|
684
878
|
logger.error('rabbit: isConnected - false');
|
|
685
879
|
return false;
|
|
686
880
|
}
|
|
687
|
-
const channel: any = await this.
|
|
881
|
+
const channel: any = await this.assertChannelOld();
|
|
688
882
|
try {
|
|
689
883
|
await Promise.all([
|
|
690
884
|
channel.waitForConnect(),
|
|
691
|
-
...this.
|
|
885
|
+
...this.oldConsumers.map((c: AfConsumer) => channel.checkQueue(c.queue)),
|
|
692
886
|
]);
|
|
693
887
|
} catch (e) {
|
|
694
888
|
logger.error('rabbit: isConnected - false');
|
|
@@ -699,12 +893,15 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
699
893
|
}
|
|
700
894
|
|
|
701
895
|
async gracefulShutdown(signal: string) : Promise<void> {
|
|
702
|
-
|
|
896
|
+
// TODO: [QUORUM-PHASE-2] After changing the publish from old to new change from the old to the new implementation
|
|
897
|
+
const tagsNumber = this.consumersTags.length + this.oldConsumersTags.length;
|
|
703
898
|
logger.info(`rabbit: [gracefully-shutdown] received ${signal}! canceling #${tagsNumber} tags...`);
|
|
704
899
|
const cancelTagPromises = this.consumersTags.map(([channel, tag]) => channel.cancel(tag));
|
|
900
|
+
const cancelTagPromisesOld = this.oldConsumersTags.map(([channel, tag]) => channel.cancel(tag));
|
|
705
901
|
// Clean the array to avoid race
|
|
706
902
|
this.consumersTags = [];
|
|
707
|
-
|
|
903
|
+
this.oldConsumersTags = [];
|
|
904
|
+
const results = await Promise.allSettled([...cancelTagPromises, ...cancelTagPromisesOld]);
|
|
708
905
|
const rejected = results.filter((p) => p.status === 'rejected');
|
|
709
906
|
if (rejected.length > 0) {
|
|
710
907
|
logger.warn(`rabbit: [gracefully-shutdown] #${rejected.length}/${tagsNumber} tags failed to cancel: ${rejected}`);
|
|
@@ -712,6 +909,342 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
712
909
|
logger.info('rabbit: [gracefully-shutdown] all tags successfully canceled.');
|
|
713
910
|
}
|
|
714
911
|
}
|
|
912
|
+
|
|
913
|
+
private async consumeFromRabbitOld(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
914
|
+
const optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options };
|
|
915
|
+
RabbitMq.validateName('queue', queue);
|
|
916
|
+
this.saveConsumerOld(queue, callback, options);
|
|
917
|
+
const uniqueId = randomUUID();
|
|
918
|
+
const {
|
|
919
|
+
limit, deadMessageTtl, useConsumeWithLock, lockTimeout, auditContext, enableRabbitTrace,
|
|
920
|
+
} = optionsWithDefaults;
|
|
921
|
+
if (useConsumeWithLock) {
|
|
922
|
+
if (!this.redisLock) {
|
|
923
|
+
throw new RabbitError('Usage of consumeWithLock requires RedisInstance');
|
|
924
|
+
}
|
|
925
|
+
logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
926
|
+
}
|
|
927
|
+
const channel = await this.getNewChannelOld({});
|
|
928
|
+
return channel.addSetup(async (confirmChannel: ConfirmChannel) => {
|
|
929
|
+
const q = await this.assertQueueOld(queue, optionsWithDefaults);
|
|
930
|
+
await confirmChannel.prefetch(limit, false);
|
|
931
|
+
const { consumerTag } = await confirmChannel.consume(
|
|
932
|
+
queue,
|
|
933
|
+
async (msg: ConsumeMessageOrNull) => {
|
|
934
|
+
if (!msg) {
|
|
935
|
+
return null;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
const traceId = msg.properties.headers?.[TRACING_HEADER];
|
|
939
|
+
const userId = msg.properties.headers?.[USER_TRACING_HEADER];
|
|
940
|
+
const automationId = msg.properties.headers?.[AUTOMATION_ID_HEADER];
|
|
941
|
+
const parsedMessage = RabbitMq.parseMsg(msg);
|
|
942
|
+
const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
|
|
943
|
+
const trace = newTrace(traceTypes.RABBIT);
|
|
944
|
+
// setting also outbreak trace as part of legacy code
|
|
945
|
+
const outbreakTrace = outbreak.newTrace(traceTypes.RABBIT);
|
|
946
|
+
// enableRabbitTrace is a flag to protect from different flows that doesn't work with permission
|
|
947
|
+
// and we don't want to fail the flow because of it
|
|
948
|
+
if (userId && enableRabbitTrace) {
|
|
949
|
+
try {
|
|
950
|
+
await Promise.all([
|
|
951
|
+
createOrSetRabbitTrace(trace, userId),
|
|
952
|
+
createOrSetRabbitTrace(outbreakTrace, userId),
|
|
953
|
+
]);
|
|
954
|
+
} catch (e) {
|
|
955
|
+
logger.error('rabbit: failed to setRabbitTrace', { userId, e });
|
|
956
|
+
return this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
if (traceId) {
|
|
961
|
+
(trace as any)?.context?.set(TRACING_HEADER, traceId);
|
|
962
|
+
(outbreakTrace as any)?.context.set(TRACING_HEADER, traceId);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
if (auditContext) {
|
|
966
|
+
await auditContext(queue, {
|
|
967
|
+
userId,
|
|
968
|
+
automationId,
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
const shouldConsume = await this.shouldConsumeMessageByTimestamp(parsedMessage);
|
|
972
|
+
if (!shouldConsume) {
|
|
973
|
+
await this.unlockRedisIfNeeded(releaseLock);
|
|
974
|
+
return this.ack(confirmChannel, msg)(msg);
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
let messageAcked = false;
|
|
978
|
+
// setting the localAck function to be used in the callback
|
|
979
|
+
|
|
980
|
+
const localAck = async () => {
|
|
981
|
+
if (messageAcked) {
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
messageAcked = true;
|
|
985
|
+
return this.ack(confirmChannel, msg, true, releaseLock)(msg);
|
|
986
|
+
};
|
|
987
|
+
|
|
988
|
+
const localNack = async (_: ConsumeMessageOrNull, nackOptions: NackOptions = {}) => {
|
|
989
|
+
if (messageAcked) {
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
debug('rabbit localNack', { messageAcked, uniqueId, deliveryTag: msg.fields.deliveryTag });
|
|
993
|
+
messageAcked = true;
|
|
994
|
+
return this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg, nackOptions);
|
|
995
|
+
};
|
|
996
|
+
|
|
997
|
+
try {
|
|
998
|
+
await callback(
|
|
999
|
+
parsedMessage,
|
|
1000
|
+
localAck,
|
|
1001
|
+
localNack,
|
|
1002
|
+
);
|
|
1003
|
+
} catch (e) {
|
|
1004
|
+
await localNack(msg);
|
|
1005
|
+
}
|
|
1006
|
+
}, CONSUMER_DEFAULT_OPTIONS,
|
|
1007
|
+
);
|
|
1008
|
+
if (!consumerTag) {
|
|
1009
|
+
logger.error(`rabbit: failed to consume from queue ${queue}`);
|
|
1010
|
+
} else {
|
|
1011
|
+
logger.info(`rabbit: adding tag ${consumerTag} to the array.`);
|
|
1012
|
+
this.oldConsumersTags.push([confirmChannel, consumerTag]);
|
|
1013
|
+
}
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
// TODO: [QUORUM-PHASE-3] Delete all the function under this line (getNewChannelOld, getConnectionOld, assertQueueOld, setupQueueOld, saveConsumerOld)
|
|
1018
|
+
async getNewChannelOld({ name = rand().toString(), onClose = null, options = {} }: newChannelOpts = {}) {
|
|
1019
|
+
let connection!: AmqpConnectionManager;
|
|
1020
|
+
try {
|
|
1021
|
+
connection = await this.getConnectionOld();
|
|
1022
|
+
} catch (e) {
|
|
1023
|
+
logger.error(`rabbit: error on get connection for new channel ${name} `, { e });
|
|
1024
|
+
throw e;
|
|
1025
|
+
}
|
|
1026
|
+
const channel = connection.createChannel({ ...options });
|
|
1027
|
+
once(channel, 'close').then((args) => {
|
|
1028
|
+
logger.error(`rabbit: channel ${name} closed`);
|
|
1029
|
+
onClose?.(args);
|
|
1030
|
+
});
|
|
1031
|
+
try {
|
|
1032
|
+
await once(channel, 'connect');
|
|
1033
|
+
debug(`rabbit: channel ${name} CONNECTED`);
|
|
1034
|
+
return channel;
|
|
1035
|
+
} catch (err) {
|
|
1036
|
+
logger.error(`rabbit: channel error ${name} error`, { err });
|
|
1037
|
+
throw err;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
async getConnectionOld() {
|
|
1042
|
+
return new Promise<AmqpConnectionManager>(async (resolve, reject) => {
|
|
1043
|
+
if (this.oldBlockReconnect) {
|
|
1044
|
+
debug('rabbit: block reconnect');
|
|
1045
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1046
|
+
// @ts-ignore
|
|
1047
|
+
return resolve();
|
|
1048
|
+
}
|
|
1049
|
+
if (this.oldConnection !== null) {
|
|
1050
|
+
if (this.options?.disableReconnect || this.oldConnection?.isConnected()) {
|
|
1051
|
+
debug('rabbit: connection - is connected');
|
|
1052
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1053
|
+
// @ts-ignore
|
|
1054
|
+
return resolve(this.oldConnection);
|
|
1055
|
+
}
|
|
1056
|
+
debug('rabbit: connection - reconnecting');
|
|
1057
|
+
}
|
|
1058
|
+
if (this.oldCreatingConnection) {
|
|
1059
|
+
debug('rabbit: creating connection emi');
|
|
1060
|
+
this.oldEm.once(CONNECTION_CREATED_CONST, resolve);
|
|
1061
|
+
this.oldEm.once(CONNECTION_FAILED_CONST, reject);
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
this.oldCreatingConnection = true;
|
|
1065
|
+
let isResolved = false;
|
|
1066
|
+
|
|
1067
|
+
// It is import to use it as a function and not as a variable
|
|
1068
|
+
// because of k8s changes the env variables
|
|
1069
|
+
// and we want to use the new values
|
|
1070
|
+
const findServers = () => {
|
|
1071
|
+
const userName = process.env.RABBITMQ_USERNAME || 'guest';
|
|
1072
|
+
const password = process.env.RABBITMQ_PASSWORD || 'guest';
|
|
1073
|
+
const host = this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost';
|
|
1074
|
+
|
|
1075
|
+
debug('rabbit: creating connection', { host, userName, HEARTBEAT });
|
|
1076
|
+
|
|
1077
|
+
return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
|
|
1078
|
+
};
|
|
1079
|
+
|
|
1080
|
+
const defaultUrls = findServers();
|
|
1081
|
+
const connection: AmqpConnectionManager = await connect(defaultUrls, {
|
|
1082
|
+
findServers,
|
|
1083
|
+
});
|
|
1084
|
+
|
|
1085
|
+
this.oldConnection = connection;
|
|
1086
|
+
this.oldConnection.on('error', (err) => {
|
|
1087
|
+
logger.error('rabbit: connection error', { err });
|
|
1088
|
+
if (!isResolved) {
|
|
1089
|
+
isResolved = true;
|
|
1090
|
+
reject(err);
|
|
1091
|
+
this.oldEm.emit(CONNECTION_FAILED_CONST, err);
|
|
1092
|
+
}
|
|
1093
|
+
});
|
|
1094
|
+
|
|
1095
|
+
this.oldConnection.on('connectFailed', (err) => {
|
|
1096
|
+
this.oldConsumersTags = [];
|
|
1097
|
+
logger.error('rabbit: connection connectFailed', { err });
|
|
1098
|
+
if (!isResolved) {
|
|
1099
|
+
isResolved = true;
|
|
1100
|
+
reject(err);
|
|
1101
|
+
this.oldEm.emit(CONNECTION_FAILED_CONST, err);
|
|
1102
|
+
}
|
|
1103
|
+
});
|
|
1104
|
+
|
|
1105
|
+
this.oldConnection.on('disconnect', ({ err }) => {
|
|
1106
|
+
this.oldConsumersTags = [];
|
|
1107
|
+
debug('rabbit: connection closed');
|
|
1108
|
+
if (this.options?.disableReconnect) {
|
|
1109
|
+
logger.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
|
|
1110
|
+
this.oldBlockReconnect = true;
|
|
1111
|
+
} else {
|
|
1112
|
+
logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
|
|
1113
|
+
}
|
|
1114
|
+
});
|
|
1115
|
+
|
|
1116
|
+
this.oldConnection.once('connect', async () => {
|
|
1117
|
+
debug('rabbit: connection established');
|
|
1118
|
+
this.oldCreatingConnection = false;
|
|
1119
|
+
this.oldEm.emit(CONNECTION_CREATED_CONST, connection);
|
|
1120
|
+
isResolved = true;
|
|
1121
|
+
resolve(connection);
|
|
1122
|
+
});
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
private saveConsumerOld(queue: string, callback: CallbackFunction, options: ConsumeOptions | undefined) {
|
|
1127
|
+
const isConsumerExist :boolean = this.oldConsumers.some((consumer) => consumer.queue === queue);
|
|
1128
|
+
if (!isConsumerExist) {
|
|
1129
|
+
logger.info(`rabbit: consumer: ${queue} saved in consumer array`);
|
|
1130
|
+
this.oldConsumers.push({
|
|
1131
|
+
queue,
|
|
1132
|
+
callback,
|
|
1133
|
+
options,
|
|
1134
|
+
});
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
async assertQueueOld(queueName: string, options?: Options.AssertQueue): Promise<any> {
|
|
1139
|
+
RabbitMq.validateName('queue', queueName);
|
|
1140
|
+
if (this.oldQueues[queueName]) {
|
|
1141
|
+
delete this.oldQueueSetupPromises[queueName];
|
|
1142
|
+
return this.oldQueues[queueName];
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
if (this.oldQueueSetupPromises[queueName]) {
|
|
1146
|
+
return this.oldQueueSetupPromises[queueName];
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
this.oldQueueSetupPromises[queueName] = this.setupQueueOld(queueName, options);
|
|
1150
|
+
return this.oldQueueSetupPromises[queueName];
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
async setupQueueOld(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
|
|
1154
|
+
// let queue: Replies.AssertQueue;
|
|
1155
|
+
const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
|
|
1156
|
+
const localeOptions = {
|
|
1157
|
+
...options,
|
|
1158
|
+
durable: true,
|
|
1159
|
+
arguments: {
|
|
1160
|
+
...options?.arguments,
|
|
1161
|
+
'x-consumer-timeout': 1000 * 60 * 60 * 24,
|
|
1162
|
+
'x-queue-type': shouldUseQuorum ? 'quorum' : 'classic',
|
|
1163
|
+
},
|
|
1164
|
+
};
|
|
1165
|
+
try {
|
|
1166
|
+
const channel: ChannelWrapper = await this.assertChannelOld();
|
|
1167
|
+
debug('assertQueue->channel.addSetup', { queueName });
|
|
1168
|
+
// await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
1169
|
+
debug('assertQueue->channel.assertQueue', { queueName });
|
|
1170
|
+
// queue = await channel.assertQueue(queueName, localeOptions);
|
|
1171
|
+
} catch (e) {
|
|
1172
|
+
logger.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
1173
|
+
if (!this.options?.dontRetryAssert) {
|
|
1174
|
+
debug('retrying assertQueue', { queueName });
|
|
1175
|
+
const channel = await this.assertChannelOld({ force: true });
|
|
1176
|
+
await this.deleteQueueOld(queueName);
|
|
1177
|
+
|
|
1178
|
+
debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
1179
|
+
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
1180
|
+
debug('retrying assertQueue->channel.assertQueue', { queueName });
|
|
1181
|
+
// queue = await channel.assertQueue(queueName, localeOptions);
|
|
1182
|
+
} else {
|
|
1183
|
+
throw e;
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
const queue = {
|
|
1188
|
+
queue: 'bla bla queue',
|
|
1189
|
+
messageCount: 0,
|
|
1190
|
+
consumerCount: 2,
|
|
1191
|
+
};
|
|
1192
|
+
|
|
1193
|
+
this.oldQueues[queueName] = queue;
|
|
1194
|
+
return queue;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
async assertChannelOld({ force = false } : assertChannelOpts = {}): Promise<ChannelWrapper> {
|
|
1198
|
+
if (!this.oldPublishChannelSetupPromise) {
|
|
1199
|
+
this.oldPublishChannelSetupPromise = new Promise<ChannelWrapper>(async (resolve, reject) => {
|
|
1200
|
+
if (this.oldChannel && !force) {
|
|
1201
|
+
return resolve(this.oldChannel);
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
try {
|
|
1205
|
+
const channel = await this.getNewChannelOld({});
|
|
1206
|
+
channel.on('error', (err) => {
|
|
1207
|
+
logger.error('rabbit: channel error', { err });
|
|
1208
|
+
});
|
|
1209
|
+
this.oldChannel = channel;
|
|
1210
|
+
resolve(channel);
|
|
1211
|
+
} catch (e) {
|
|
1212
|
+
reject(e);
|
|
1213
|
+
}
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
return this.oldPublishChannelSetupPromise;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
private async deleteQueueOld(queue: string) {
|
|
1220
|
+
RabbitMq.validateName('queue', queue);
|
|
1221
|
+
const channel: ChannelWrapper = await this.assertChannelOld();
|
|
1222
|
+
logger.info('rabbit: deleting queue', { queue });
|
|
1223
|
+
const deleteQueueRes = await channel.deleteQueue(queue);
|
|
1224
|
+
debug('queue deleted', deleteQueueRes);
|
|
1225
|
+
return deleteQueueRes;
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
async assertExchangeOld(exchangeName: string, options?: any) {
|
|
1229
|
+
const channel: ChannelWrapper = await this.assertChannelOld();
|
|
1230
|
+
|
|
1231
|
+
if (this.oldExchanges[exchangeName]) {
|
|
1232
|
+
delete this.oldAssertExchangePromises[exchangeName];
|
|
1233
|
+
return this.oldExchanges[exchangeName];
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
if (this.oldAssertExchangePromises[exchangeName]) {
|
|
1237
|
+
return this.oldAssertExchangePromises[exchangeName];
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
this.oldAssertExchangePromises[exchangeName] = assertExchangeFanout(channel, exchangeName);
|
|
1241
|
+
this.oldExchanges[exchangeName] = await this.oldAssertExchangePromises[exchangeName];
|
|
1242
|
+
return this.oldExchanges[exchangeName];
|
|
1243
|
+
}
|
|
715
1244
|
}
|
|
716
1245
|
|
|
717
1246
|
export default RabbitMq;
|
|
1247
|
+
|
|
1248
|
+
export {
|
|
1249
|
+
sendCeleryTaskViaHttp,
|
|
1250
|
+
} from './lib/celery';
|