@autofleet/rabbit 3.3.0-beta.10 → 3.3.0-beta.2

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/src/index.ts CHANGED
@@ -20,8 +20,7 @@ import getRedisInstance, { RedisConfig } from './lib/redis';
20
20
  import { assertExchangeFanout, rand, wrapSetImmediate } from './lib/utils';
21
21
  import {
22
22
  AUTOMATION_ID_HEADER,
23
- CONNECTION_CREATED_CONST,
24
- CONNECTION_FAILED_CONST,
23
+ ConnectionPurpose,
25
24
  DEFAULT_LOCK_TIMEOUT,
26
25
  DEFAULT_OPTIONS,
27
26
  RETRY_HEADER,
@@ -40,13 +39,13 @@ import {
40
39
  CONSUMER_DEFAULT_OPTIONS,
41
40
  QueueSetupPromisesDictionary,
42
41
  AssertExchangePromisesDictionary,
42
+ ConnectionData,
43
43
  } from './lib/types';
44
44
 
45
45
  // const debug = nodeDebug('af-rabbitmq')
46
46
  const debug = logger.debug.bind(logger);
47
47
 
48
48
  const PUBLISH_TIMEOUT = 1000 * 10;
49
-
50
49
  export interface IAfRabbitMq {
51
50
  ack: any;
52
51
  nack: any;
@@ -86,11 +85,13 @@ 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) : any {
106
+ static parseMsg(msg: any): any {
106
107
  let { content } = msg;
107
108
  content = content.toString();
108
109
 
@@ -143,18 +144,19 @@ class RabbitMq implements IAfRabbitMq {
143
144
 
144
145
  RECONNECT_MSG = 'rabbit: connection disconnect - reconnecting';
145
146
 
146
- channel: ChannelWrapper | null;
147
+ publishChannel: ChannelWrapper | null;
147
148
 
148
149
  publishChannelSetupPromise: Promise<ChannelWrapper> | null;
149
150
 
150
- blockReconnect: boolean | null | undefined
151
+ blockReconnect: boolean | null | undefined;
151
152
 
152
- connection: AmqpConnectionManager | null | undefined
153
+ connectionsMap: {
154
+ [ConnectionPurpose.Consume]: ConnectionData;
155
+ [ConnectionPurpose.Publish]: ConnectionData;
156
+ };
153
157
 
154
158
  em: EventEmitter;
155
159
 
156
- creatingConnection: boolean;
157
-
158
160
  exchanges: ExchangesCache;
159
161
 
160
162
  queues: QueuesCache;
@@ -174,48 +176,30 @@ class RabbitMq implements IAfRabbitMq {
174
176
 
175
177
  private consumers: Array<AfConsumer> = [];
176
178
 
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) {
179
+ constructor(options?: AfRabbitOptions, redisConfig?: RedisConfig) {
207
180
  this.em = new EventEmitter();
208
- this.channel = null;
181
+ this.publishChannel = null;
209
182
  this.publishChannelSetupPromise = null;
210
- this.connection = null;
211
- this.creatingConnection = false;
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
+ };
212
197
  this.exchanges = {};
213
198
  this.queues = {};
214
199
  this.queueSetupPromises = {};
215
200
  this.assertExchangePromises = {};
216
201
  this.consumers = [];
217
202
  this.options = options;
218
-
219
203
  this.redisClient = redisConfig && getRedisInstance(redisConfig);
220
204
  if (this.redisClient) {
221
205
  this.redisLock = promisify(RedisLock(this.redisClient)) as RedisLockType;
@@ -230,74 +214,8 @@ class RabbitMq implements IAfRabbitMq {
230
214
  await this.gracefulShutdown('SIGINT');
231
215
  });
232
216
  }
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 = [];
246
217
  }
247
218
 
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
-
301
219
  private shouldConsumeMessageByTimestamp = async (msg: ConsumeMessageOrNull) => {
302
220
  if (msg) {
303
221
  const { properties: { headers } } = msg;
@@ -312,7 +230,7 @@ class RabbitMq implements IAfRabbitMq {
312
230
  return false;
313
231
  }
314
232
 
315
- public ack = (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp = false, releaseLock = null) => async (userMsg: ConsumeMessage) : Promise<any> => {
233
+ public ack = (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp = false, releaseLock = null) => async (userMsg: ConsumeMessage): Promise<any> => {
316
234
  if (msg) {
317
235
  debug('rabbit acking message', { deliveryTag: msg.fields.deliveryTag });
318
236
  await channel.ack(msg);
@@ -338,20 +256,20 @@ class RabbitMq implements IAfRabbitMq {
338
256
  userMsg: ConsumeMessageOrNull,
339
257
  {
340
258
  skipRetry = false,
341
- }: NackOptions = { },
342
- ) : Promise<any> => {
259
+ }: NackOptions = {},
260
+ ): Promise<any> => {
343
261
  await this.unlockRedisIfNeeded(releaseLock);
344
262
  if (channel && msg) {
345
263
  if (
346
264
  !skipRetry
347
265
  && (
348
- !msg.properties.headers?.[RETRY_HEADER]
349
- || parseInt(msg.properties.headers?.[RETRY_HEADER], 10) < options.retries
266
+ !msg.properties.headers[RETRY_HEADER]
267
+ || parseInt(msg.properties.headers[RETRY_HEADER], 10) < options.retries
350
268
  )
351
269
  ) {
352
270
  await this.sendToQueue(queue, RabbitMq.parseMsg(msg).content, options, {
353
271
  ...msg.properties.headers,
354
- [RETRY_HEADER]: msg.properties.headers?.[RETRY_HEADER]
272
+ [RETRY_HEADER]: msg.properties.headers[RETRY_HEADER]
355
273
  ? msg.properties.headers[RETRY_HEADER] + 1
356
274
  : 1,
357
275
  });
@@ -359,7 +277,7 @@ class RabbitMq implements IAfRabbitMq {
359
277
  const deadQueue = `${queue}-dead`;
360
278
  await this.sendToQueue(deadQueue, RabbitMq.parseMsg(msg).content, deadQueueOptions, {
361
279
  ...msg.properties.headers,
362
- [RETRY_HEADER]: msg.properties.headers?.[RETRY_HEADER]
280
+ [RETRY_HEADER]: msg.properties.headers[RETRY_HEADER]
363
281
  ? msg.properties.headers[RETRY_HEADER] + 1
364
282
  : 1,
365
283
  });
@@ -373,30 +291,36 @@ class RabbitMq implements IAfRabbitMq {
373
291
  }
374
292
  }
375
293
 
376
- async getConnection() {
377
- 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];
378
302
  if (this.blockReconnect) {
379
303
  debug('rabbit: block reconnect');
380
304
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
381
305
  // @ts-ignore
382
306
  return resolve();
383
307
  }
384
- if (this.connection !== null) {
385
- if (this.options?.disableReconnect || this.connection?.isConnected()) {
308
+ if (connection !== null) {
309
+ if (this.options?.disableReconnect || connection?.isConnected()) {
386
310
  debug('rabbit: connection - is connected');
387
311
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
388
312
  // @ts-ignore
389
- return resolve(this.connection);
313
+ return resolve(connection);
390
314
  }
391
315
  debug('rabbit: connection - reconnecting');
392
316
  }
393
- if (this.creatingConnection) {
317
+ if (creatingConnection) {
394
318
  debug('rabbit: creating connection emi');
395
- this.em.once(CONNECTION_CREATED_CONST, resolve);
396
- this.em.once(CONNECTION_FAILED_CONST, reject);
319
+ this.em.once(connectionCreatedEventName, resolve);
320
+ this.em.once(connectionFailedEventName, reject);
397
321
  return;
398
322
  }
399
- this.creatingConnection = true;
323
+ this.connectionsMap[connectionPurpose].creatingConnection = true;
400
324
  let isResolved = false;
401
325
 
402
326
  // It is import to use it as a function and not as a variable
@@ -408,36 +332,38 @@ class RabbitMq implements IAfRabbitMq {
408
332
  const host = this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost';
409
333
 
410
334
  debug('rabbit: creating connection', { host, userName, HEARTBEAT });
411
- return [`amqp://${userName}:${password}@${host}/${this.vhost}?heartbeat=${HEARTBEAT}`];
335
+
336
+ return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
412
337
  };
413
338
 
414
339
  const defaultUrls = findServers();
415
- const connection: AmqpConnectionManager = await connect(defaultUrls, {
340
+ const newConnection: AmqpConnectionManager = await connect(defaultUrls, {
416
341
  findServers,
417
342
  });
418
343
 
419
- this.connection = connection;
420
- this.connection.on('error', (err) => {
344
+ this.connectionsMap[connectionPurpose].connection = newConnection;
345
+ logger.info(`rabbit: created new connection ${connectionPurpose}`);
346
+
347
+ newConnection.on('error', (err) => {
421
348
  logger.error('rabbit: connection error', { err });
422
349
  if (!isResolved) {
423
350
  isResolved = true;
424
351
  reject(err);
425
- this.em.emit(CONNECTION_FAILED_CONST, err);
352
+ this.em.emit(connectionFailedEventName, err);
426
353
  }
427
354
  });
428
355
 
429
- this.connection.on('connectFailed', (err) => {
356
+ newConnection.on('connectFailed', (err) => {
430
357
  this.consumersTags = [];
431
- logger.error('rabbit: connection connectFailed', { err, advice: 'Check if the vhost exist', vhost: this.vhost });
358
+ logger.error('rabbit: connection connectFailed', { err });
432
359
  if (!isResolved) {
433
360
  isResolved = true;
434
361
  reject(err);
435
- this.em.emit(CONNECTION_FAILED_CONST, err);
362
+ this.em.emit(connectionFailedEventName, err);
436
363
  }
437
364
  });
438
365
 
439
- this.connection.on('disconnect', ({ err }) => {
440
- // this.channel = null;
366
+ newConnection.on('disconnect', ({ err }) => {
441
367
  this.consumersTags = [];
442
368
  debug('rabbit: connection closed');
443
369
  if (this.options?.disableReconnect) {
@@ -447,25 +373,28 @@ class RabbitMq implements IAfRabbitMq {
447
373
  logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
448
374
  }
449
375
  });
450
-
451
- this.connection.once('connect', async () => {
452
- debug('rabbit: connection established');
453
- this.creatingConnection = false;
454
- this.em.emit(CONNECTION_CREATED_CONST, connection);
376
+ newConnection.once('connect', async () => {
377
+ this.connectionsMap[connectionPurpose].creatingConnection = false;
378
+ this.em.emit(connectionCreatedEventName, newConnection);
455
379
  isResolved = true;
456
- resolve(connection);
380
+ resolve(newConnection);
457
381
  });
458
382
  });
459
383
  }
460
384
 
461
- async getNewChannel({ name = rand().toString(), onClose = null, options = {} }: newChannelOpts = {}) {
462
- let connection!: AmqpConnectionManager;
385
+ async getNewChannel({
386
+ name = rand().toString(), onClose = null, options = {}, connectionPurpose = ConnectionPurpose.Consume,
387
+ }: newChannelOpts): Promise<ChannelWrapper> {
388
+ let connection!: AmqpConnectionManager | undefined | null;
463
389
  try {
464
- connection = await this.getConnection();
390
+ connection = await this.getConnection(connectionPurpose);
465
391
  } catch (e) {
466
392
  logger.error(`rabbit: error on get connection for new channel ${name} `, { e });
467
393
  throw e;
468
394
  }
395
+ if (!connection) {
396
+ throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
397
+ }
469
398
  const channel = connection.createChannel({ ...options });
470
399
  once(channel, 'close').then((args) => {
471
400
  logger.error(`rabbit: channel ${name} closed`);
@@ -481,19 +410,23 @@ class RabbitMq implements IAfRabbitMq {
481
410
  }
482
411
  }
483
412
 
484
- async assertChannel({ force = false } : assertChannelOpts = {}): Promise<ChannelWrapper> {
413
+ async assertChannel({ force = false, connectionPurpose = ConnectionPurpose.Consume }: assertChannelOpts): Promise<ChannelWrapper> {
414
+ debug('rabbit: start assert channel', { connectionPurpose, publishPromise: this.publishChannelSetupPromise, channel: this.publishChannel });
485
415
  if (!this.publishChannelSetupPromise) {
486
416
  this.publishChannelSetupPromise = new Promise<ChannelWrapper>(async (resolve, reject) => {
487
- if (this.channel && !force) {
488
- return resolve(this.channel);
417
+ if (this.publishChannel && !force) {
418
+ return resolve(this.publishChannel);
489
419
  }
490
420
 
491
421
  try {
492
- const channel = await this.getNewChannel({});
422
+ const channel = await this.getNewChannel({ connectionPurpose });
423
+ debug('rabbit: new channel got', { connectionPurpose, channel: this.publishChannel });
493
424
  channel.on('error', (err) => {
494
425
  logger.error('rabbit: channel error', { err });
495
426
  });
496
- this.channel = channel;
427
+ if (connectionPurpose === ConnectionPurpose.Publish) {
428
+ this.publishChannel = channel;
429
+ }
497
430
  resolve(channel);
498
431
  } catch (e) {
499
432
  reject(e);
@@ -503,9 +436,8 @@ class RabbitMq implements IAfRabbitMq {
503
436
  return this.publishChannelSetupPromise;
504
437
  }
505
438
 
506
- async assertExchange(exchangeName: string, options?: any) {
507
- const channel: ChannelWrapper = await this.assertChannel();
508
-
439
+ async assertExchange(exchangeName: string, options: any = { connectionPurpose: ConnectionPurpose.Consume }) {
440
+ const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose: options.connectionPurpose });
509
441
  if (this.exchanges[exchangeName]) {
510
442
  delete this.assertExchangePromises[exchangeName];
511
443
  return this.exchanges[exchangeName];
@@ -520,58 +452,57 @@ class RabbitMq implements IAfRabbitMq {
520
452
  return this.exchanges[exchangeName];
521
453
  }
522
454
 
523
- // TODO: [QUORUM-PHASE-2] Change the implementation to the new one
524
- async getQueueLength(queue: string) {
455
+ async getQueueLength(queue: string, connectionPurpose: ConnectionPurpose = ConnectionPurpose.Consume): Promise<Replies.AssertQueue> {
525
456
  RabbitMq.validateName('queue', queue);
526
- const { oldChannel: channel } = this;
527
- if (!channel) {
528
- throw new RabbitError('channel is not defined');
457
+ const { connection } = this.connectionsMap[connectionPurpose];
458
+ const { publishChannel } = this;
459
+ if (!publishChannel) {
460
+ throw new Error('channel is not defined');
529
461
  }
530
- debug('rabbit: getting queue length', { queue, connected: this.connection?.isConnected() });
531
- return channel?.checkQueue(queue);
462
+ debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
463
+ return publishChannel?.checkQueue(queue);
532
464
  }
533
465
 
534
- private async deleteQueue(queue: string) {
466
+ private async deleteQueue(queue: string, connectionPurpose: ConnectionPurpose) {
535
467
  RabbitMq.validateName('queue', queue);
536
- const channel: ChannelWrapper = await this.assertChannel();
468
+ const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
537
469
  logger.info('rabbit: deleting queue', { queue });
538
470
  const deleteQueueRes = await channel.deleteQueue(queue);
539
471
  debug('queue deleted', deleteQueueRes);
540
472
  return deleteQueueRes;
541
473
  }
542
474
 
543
- // TODO: [QUORUM-PHASE-2] Change the implementation to the new one
544
475
  async bindQueue(queue: string, exchange: string) {
545
- const channel: ChannelWrapper = await this.assertChannelOld();
476
+ const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose: ConnectionPurpose.Publish });
546
477
  await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.bindQueue(queue, exchange, ''));
547
478
  return channel.bindQueue(queue, exchange, '');
548
479
  }
549
480
 
550
481
  async setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
551
482
  let queue: Replies.AssertQueue;
483
+ const connectionPurpose = ConnectionPurpose.Publish;
484
+ const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
552
485
  const localeOptions = {
553
486
  ...options,
554
487
  durable: true,
555
488
  arguments: {
556
489
  ...options?.arguments,
557
490
  'x-consumer-timeout': 1000 * 60 * 60 * 24,
558
- 'x-queue-type': 'quorum',
491
+ 'x-queue-type': shouldUseQuorum ? 'quorum' : 'classic',
559
492
  },
560
493
  };
561
494
  try {
562
- const channel: ChannelWrapper = await this.assertChannel();
495
+ const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
563
496
  debug('assertQueue->channel.addSetup', { queueName });
564
- await channel.addSetup(async (setupChannel: ConfirmChannel) => {
565
- await setupChannel.assertQueue(queueName, localeOptions);
566
- });
497
+ await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
567
498
  debug('assertQueue->channel.assertQueue', { queueName });
568
499
  queue = await channel.assertQueue(queueName, localeOptions);
569
500
  } catch (e) {
570
501
  logger.error('rabbit: assertQueue error', { queueName, options, error: e });
571
502
  if (!this.options?.dontRetryAssert) {
572
503
  debug('retrying assertQueue', { queueName });
573
- const channel = await this.assertChannel({ force: true });
574
- await this.deleteQueue(queueName);
504
+ const channel = await this.assertChannel({ force: true, connectionPurpose });
505
+ await this.deleteQueue(queueName, connectionPurpose);
575
506
 
576
507
  debug('retrying assertQueue->channel.addSetup', { queueName });
577
508
  await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
@@ -581,11 +512,11 @@ class RabbitMq implements IAfRabbitMq {
581
512
  throw e;
582
513
  }
583
514
  }
515
+
584
516
  this.queues[queueName] = queue;
585
517
  return queue;
586
518
  }
587
519
 
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
520
  static shouldUseQuorum(queueName: string): boolean {
590
521
  const envQuorumQueuesWhitelist = process.env.QUORUM_QUEUES_WHITELIST;
591
522
 
@@ -601,7 +532,8 @@ class RabbitMq implements IAfRabbitMq {
601
532
  return false;
602
533
  }
603
534
 
604
- async assertQueue(queueName: string, options?: Options.AssertQueue): Promise<any> {
535
+ async assertQueue(queueName: string, options?: Options.AssertQueue) {
536
+ debug('rabbit: start assert queue', { queueName });
605
537
  RabbitMq.validateName('queue', queueName);
606
538
  if (this.queues[queueName]) {
607
539
  delete this.queueSetupPromises[queueName];
@@ -613,11 +545,12 @@ class RabbitMq implements IAfRabbitMq {
613
545
  }
614
546
 
615
547
  this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
548
+ debug('rabbit: done assert queue', { queueName });
616
549
  return this.queueSetupPromises[queueName];
617
550
  }
618
551
 
619
552
  private saveConsumer(queue: string, callback: CallbackFunction, options: ConsumeOptions | undefined) {
620
- const isConsumerExist :boolean = this.consumers.some((consumer) => consumer.queue === queue);
553
+ const isConsumerExist: boolean = this.consumers.some((consumer) => consumer.queue === queue);
621
554
  if (!isConsumerExist) {
622
555
  logger.info(`rabbit: consumer: ${queue} saved in consumer array`);
623
556
  this.consumers.push({
@@ -628,27 +561,10 @@ class RabbitMq implements IAfRabbitMq {
628
561
  }
629
562
  }
630
563
 
631
- // Used by the microservices to consume messages from the queue
632
564
  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> {
644
565
  await this.consumeFromRabbit(queue, callback, options);
645
566
  }
646
567
 
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
-
652
568
  private async lockRedisIfNeeded(msg: any, options: any) {
653
569
  const { properties: { headers } } = msg;
654
570
  const timestamp = headers?.creationTimestamp;
@@ -679,13 +595,12 @@ class RabbitMq implements IAfRabbitMq {
679
595
  } = optionsWithDefaults;
680
596
  if (useConsumeWithLock) {
681
597
  if (!this.redisLock) {
682
- throw new RabbitError('Usage of consumeWithLock requires RedisInstance');
598
+ throw new Error('Usage of consumeWithLock requires RedisInstance');
683
599
  }
684
600
  logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
685
601
  }
686
- const channel = await this.getNewChannel({});
602
+ const channel = await this.getNewChannel({ connectionPurpose: ConnectionPurpose.Consume });
687
603
  return channel.addSetup(async (confirmChannel: ConfirmChannel) => {
688
- logger.info(`rabbit: channel.addSetup before ${queue} assertQueueOld`);
689
604
  const q = await this.assertQueue(queue, optionsWithDefaults);
690
605
  await confirmChannel.prefetch(limit, false);
691
606
  const { consumerTag } = await confirmChannel.consume(
@@ -695,9 +610,9 @@ class RabbitMq implements IAfRabbitMq {
695
610
  return null;
696
611
  }
697
612
 
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];
613
+ const traceId = msg.properties.headers[TRACING_HEADER];
614
+ const userId = msg.properties.headers[USER_TRACING_HEADER];
615
+ const automationId = msg.properties.headers[AUTOMATION_ID_HEADER];
701
616
  const parsedMessage = RabbitMq.parseMsg(msg);
702
617
  const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
703
618
  const trace = newTrace(traceTypes.RABBIT);
@@ -774,45 +689,22 @@ class RabbitMq implements IAfRabbitMq {
774
689
  });
775
690
  }
776
691
 
777
- // Used by the microservices to consume messages from the exchange
778
- async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions) : Promise<any> {
692
+ async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
779
693
  const optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options };
780
694
  RabbitMq.validateName('exchange', exchange);
781
695
  RabbitMq.validateName('queue', queue);
782
696
  const { limit, deadMessageTtl } = optionsWithDefaults;
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
- }
697
+ await this.saveConsumer(queue, callback, options);
698
+ const channel: ChannelWrapper = await this.getNewChannel({ name: `consume-exchange-${exchange}-queue-${queue}` });
804
699
 
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) => {
700
+ return channel.addSetup(async (c: ConfirmChannel) => {
809
701
  const assertExchange = await assertExchangeFanout(c, exchange);
810
- // await c.assertQueue(queue);
811
- this.oldExchanges[exchange] = assertExchange;
702
+ await c.assertQueue(queue);
703
+ this.exchanges[exchange] = assertExchange;
812
704
  await c.prefetch(limit, false);
813
705
  return Promise.all([
814
- // c.bindQueue(queue, exchange, ''),
815
- this.consumeOld(
706
+ c.bindQueue(queue, exchange, ''),
707
+ this.consume(
816
708
  queue,
817
709
  callback,
818
710
  options,
@@ -821,21 +713,18 @@ class RabbitMq implements IAfRabbitMq {
821
713
  });
822
714
  }
823
715
 
824
- // Used by the microservices to publish messages to the exchange
825
- // TODO: [QUORUM-PHASE-2] Change the implementation to the new one
826
- async publish(exchange: string, content: any, customHeaders?: any) : Promise<boolean> {
716
+ async publish(exchange: string, content: any, customHeaders?: any): Promise<boolean> {
717
+ debug('rabbit: start publish msg');
827
718
  return wrapSetImmediate(async () => {
828
719
  RabbitMq.validateName('exchange', exchange);
829
- const channel: ChannelWrapper = await this.assertChannelOld();
830
- await this.assertExchangeOld(exchange);
720
+ const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose: ConnectionPurpose.Publish });
721
+ await this.assertExchange(exchange, { connectionPurpose: ConnectionPurpose.Publish });
831
722
  await channel.publish(exchange, '',
832
723
  Buffer.from(JSON.stringify(content)),
833
724
  RabbitMq.getPublishOptions(customHeaders));
834
725
  });
835
726
  }
836
727
 
837
- // Used by the microservices to send messages to the queue
838
- // TODO: [QUORUM-PHASE-2] Change the implementation to the new one
839
728
  async sendToQueue(
840
729
  queue: string,
841
730
  content: any,
@@ -843,7 +732,7 @@ class RabbitMq implements IAfRabbitMq {
843
732
  customHeaders?: any,
844
733
  ): Promise<boolean | undefined> {
845
734
  try {
846
- await this.assertChannelOld();
735
+ await this.assertChannel({ connectionPurpose: ConnectionPurpose.Publish });
847
736
  } catch (e) {
848
737
  logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
849
738
  throw e;
@@ -851,57 +740,62 @@ class RabbitMq implements IAfRabbitMq {
851
740
 
852
741
  try {
853
742
  RabbitMq.validateName('queue', queue);
854
- await this.assertQueueOld(queue, options);
743
+ await this.assertQueue(queue, options);
855
744
  } catch (e) {
856
745
  logger.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
857
746
  throw e;
858
747
  }
859
748
 
860
749
  try {
861
- const res = await this.oldChannel?.sendToQueue(queue,
750
+ const res = await this.publishChannel?.sendToQueue(queue,
862
751
  Buffer.from(JSON.stringify(content)),
863
752
  RabbitMq.getPublishOptions(customHeaders));
864
753
  debug(`rabbit: sending to queue ${queue}`, { res });
865
754
  return res;
866
755
  } catch (e) {
867
- const isConnected = await this.isConnected();
868
- logger.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
756
+ logger.error(`rabbit sendToQueue: failed to send to queue ${queue}`, { e });
869
757
  throw e;
870
758
  }
871
759
  }
872
760
 
873
- // TODO: [QUORUM-PHASE-2] After changing the publish from old to new change from the old to the new implementation
874
- async isConnected() : Promise<boolean> {
875
- const connection = await this.getConnectionOld();
876
- const isConnected = connection.isConnected();
877
- if (!isConnected) {
878
- logger.error('rabbit: isConnected - false');
879
- return false;
880
- }
881
- const channel: any = await this.assertChannelOld();
882
- try {
883
- await Promise.all([
884
- channel.waitForConnect(),
885
- ...this.oldConsumers.map((c: AfConsumer) => channel.checkQueue(c.queue)),
886
- ]);
887
- } catch (e) {
888
- logger.error('rabbit: isConnected - false');
889
- return false;
890
- }
891
- logger.info('rabbit: isConnected - true');
892
- return true;
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
+ const isConnected = connection?.isConnected();
767
+ if (!isConnected) {
768
+ logger.error('rabbit: isConnected - false', { connectionPurpose });
769
+ return false;
770
+ }
771
+ logger.info('rabbit: isConnected - true', { connectionPurpose });
772
+
773
+ if (connectionPurpose === ConnectionPurpose.Publish) {
774
+ const channel: any = await this.assertChannel({ connectionPurpose: connectionPurpose as ConnectionPurpose });
775
+ try {
776
+ await Promise.all([
777
+ channel.waitForConnect(),
778
+ ...this.consumers.map((c: AfConsumer) => channel.checkQueue(c.queue)),
779
+ ]);
780
+ } catch (e) {
781
+ logger.error('rabbit: isConnected - false');
782
+ return false;
783
+ }
784
+ }
785
+
786
+ return true;
787
+ }),
788
+ );
789
+ return isEachConnectionConnected.every((isConnected) => isConnected === true);
893
790
  }
894
791
 
895
- async gracefulShutdown(signal: string) : Promise<void> {
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;
792
+ async gracefulShutdown(signal: string): Promise<void> {
793
+ const tagsNumber = this.consumersTags.length;
898
794
  logger.info(`rabbit: [gracefully-shutdown] received ${signal}! canceling #${tagsNumber} tags...`);
899
795
  const cancelTagPromises = this.consumersTags.map(([channel, tag]) => channel.cancel(tag));
900
- const cancelTagPromisesOld = this.oldConsumersTags.map(([channel, tag]) => channel.cancel(tag));
901
796
  // Clean the array to avoid race
902
797
  this.consumersTags = [];
903
- this.oldConsumersTags = [];
904
- const results = await Promise.allSettled([...cancelTagPromises, ...cancelTagPromisesOld]);
798
+ const results = await Promise.allSettled(cancelTagPromises);
905
799
  const rejected = results.filter((p) => p.status === 'rejected');
906
800
  if (rejected.length > 0) {
907
801
  logger.warn(`rabbit: [gracefully-shutdown] #${rejected.length}/${tagsNumber} tags failed to cancel: ${rejected}`);
@@ -909,338 +803,6 @@ class RabbitMq implements IAfRabbitMq {
909
803
  logger.info('rabbit: [gracefully-shutdown] all tags successfully canceled.');
910
804
  }
911
805
  }
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
- }
1244
806
  }
1245
807
 
1246
808
  export default RabbitMq;