@autofleet/rabbit 4.0.0-beta.0 → 4.0.0

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.
Files changed (55) hide show
  1. package/dist/index.d.ts +24 -17
  2. package/dist/index.js +303 -180
  3. package/dist/index.js.map +1 -0
  4. package/dist/lib/celery.js +1 -0
  5. package/dist/lib/celery.js.map +1 -0
  6. package/dist/lib/consts.d.ts +0 -2
  7. package/dist/lib/consts.js +2 -3
  8. package/dist/lib/consts.js.map +1 -0
  9. package/dist/lib/rabbitError.js +1 -0
  10. package/dist/lib/rabbitError.js.map +1 -0
  11. package/dist/lib/redis.js +1 -0
  12. package/dist/lib/redis.js.map +1 -0
  13. package/dist/lib/types.d.ts +8 -0
  14. package/dist/lib/types.js +1 -0
  15. package/dist/lib/types.js.map +1 -0
  16. package/dist/lib/utils.js +1 -0
  17. package/dist/lib/utils.js.map +1 -0
  18. package/dist/logger.js +1 -0
  19. package/dist/logger.js.map +1 -0
  20. package/dist/{mock.d.ts → mock/index.d.ts} +1 -1
  21. package/dist/{mock.js → mock/index.js} +2 -1
  22. package/dist/mock/index.js.map +1 -0
  23. package/dist/mock/vitest.d.ts +13 -0
  24. package/dist/mock/vitest.js +18 -0
  25. package/dist/mock/vitest.js.map +1 -0
  26. package/package.json +22 -16
  27. package/src/index.ts +359 -203
  28. package/src/lib/consts.ts +0 -2
  29. package/src/lib/types.ts +10 -0
  30. package/src/{mock.ts → mock/index.ts} +2 -2
  31. package/src/mock/vitest.ts +24 -0
  32. package/tsconfig.build.json +5 -0
  33. package/tsconfig.json +2 -2
  34. package/vitest.config.ts +17 -0
  35. package/coverage/clover.xml +0 -669
  36. package/coverage/coverage-final.json +0 -9
  37. package/coverage/lcov-report/base.css +0 -224
  38. package/coverage/lcov-report/block-navigation.js +0 -87
  39. package/coverage/lcov-report/favicon.png +0 -0
  40. package/coverage/lcov-report/index.html +0 -131
  41. package/coverage/lcov-report/prettify.css +0 -1
  42. package/coverage/lcov-report/prettify.js +0 -2
  43. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  44. package/coverage/lcov-report/sorter.js +0 -196
  45. package/coverage/lcov-report/src/index.html +0 -131
  46. package/coverage/lcov-report/src/index.ts.html +0 -3826
  47. package/coverage/lcov-report/src/lib/celery.ts.html +0 -352
  48. package/coverage/lcov-report/src/lib/consts.ts.html +0 -142
  49. package/coverage/lcov-report/src/lib/index.html +0 -191
  50. package/coverage/lcov-report/src/lib/rabbitError.ts.html +0 -103
  51. package/coverage/lcov-report/src/lib/redis.ts.html +0 -133
  52. package/coverage/lcov-report/src/lib/types.ts.html +0 -268
  53. package/coverage/lcov-report/src/lib/utils.ts.html +0 -142
  54. package/coverage/lcov-report/src/logger.ts.html +0 -100
  55. package/coverage/lcov.info +0 -1076
package/src/index.ts CHANGED
@@ -1,5 +1,4 @@
1
- /* eslint-disable no-empty,consistent-return,no-async-promise-executor,@typescript-eslint/no-unused-vars */
2
-
1
+ /* eslint-disable no-empty,consistent-return,no-async-promise-executor,@typescript-eslint/no-unused-vars,no-param-reassign */
3
2
  import { EventEmitter, once } from 'events';
4
3
  import { promisify } from 'util';
5
4
  import moment from 'moment';
@@ -14,15 +13,12 @@ import {
14
13
  getCurrentPayload, newTrace, traceTypes, createOrSetRabbitTrace, outbreak,
15
14
  } from '@autofleet/zehut';
16
15
  import { randomUUID } from 'node:crypto';
17
- import axios from 'axios';
18
16
  import logger from './logger';
19
17
  import RabbitError from './lib/rabbitError';
20
18
  import getRedisInstance, { RedisConfig } from './lib/redis';
21
19
  import { assertExchangeFanout, rand, wrapSetImmediate } from './lib/utils';
22
20
  import {
23
21
  AUTOMATION_ID_HEADER,
24
- CONNECTION_CREATED_CONST,
25
- CONNECTION_FAILED_CONST,
26
22
  DEFAULT_LOCK_TIMEOUT,
27
23
  DEFAULT_OPTIONS,
28
24
  RETRY_HEADER,
@@ -41,6 +37,7 @@ import {
41
37
  CONSUMER_DEFAULT_OPTIONS,
42
38
  QueueSetupPromisesDictionary,
43
39
  AssertExchangePromisesDictionary,
40
+ ConnectionData,
44
41
  } from './lib/types';
45
42
 
46
43
  // const debug = nodeDebug('af-rabbitmq')
@@ -48,6 +45,11 @@ const debug = logger.debug.bind(logger);
48
45
 
49
46
  const PUBLISH_TIMEOUT = 1000 * 10;
50
47
 
48
+ // TODO: [QUORUM-PHASE-3] Delete this env var
49
+ const {
50
+ DISABLE_QUORUM_QUEUES,
51
+ } = process.env;
52
+
51
53
  export interface IAfRabbitMq {
52
54
  ack: any;
53
55
  nack: any;
@@ -89,11 +91,13 @@ type newChannelOpts = {
89
91
  name?: string;
90
92
  onClose?: null | ((args: any | null) => void);
91
93
  options?: CreateChannelOpts | undefined;
94
+ connection: ConnectionData;
92
95
  };
93
96
 
94
97
  type assertChannelOpts = {
95
98
  channelName?: string;
96
99
  force?: boolean;
100
+ connection: ConnectionData;
97
101
  }
98
102
 
99
103
  type AfConsumer = {
@@ -146,13 +150,15 @@ class RabbitMq implements IAfRabbitMq {
146
150
 
147
151
  RECONNECT_MSG = 'rabbit: connection disconnect - reconnecting';
148
152
 
149
- channel: ChannelWrapper | null;
153
+ publishChannel: ChannelWrapper | null;
150
154
 
151
155
  publishChannelSetupPromise: Promise<ChannelWrapper> | null;
152
156
 
153
157
  blockReconnect: boolean | null | undefined
154
158
 
155
- connection: AmqpConnectionManager | null | undefined
159
+ publishConnection: ConnectionData
160
+
161
+ consumeConnection: ConnectionData
156
162
 
157
163
  em: EventEmitter;
158
164
 
@@ -166,7 +172,7 @@ class RabbitMq implements IAfRabbitMq {
166
172
 
167
173
  assertExchangePromises: AssertExchangePromisesDictionary;
168
174
 
169
- options: AfRabbitOptions | undefined;
175
+ options: AfRabbitOptions;
170
176
 
171
177
  redisClient: any;
172
178
 
@@ -177,17 +183,20 @@ class RabbitMq implements IAfRabbitMq {
177
183
 
178
184
  private consumers: Array<AfConsumer> = [];
179
185
 
180
- private isVHostExist = false;
186
+ private doesVHostExist = false;
181
187
 
182
- // TODO: DELETE OLD PROPERTIES
188
+ private vhost = 'quorum-vhost';
183
189
 
184
- oldChannel: ChannelWrapper | null;
190
+ // TODO:[QUORUM-PHASE-3] Delete the old properties that we use for the old consumers and publishers
191
+ oldPublishChannel: ChannelWrapper | null;
185
192
 
186
193
  oldPublishChannelSetupPromise: Promise<ChannelWrapper> | null;
187
194
 
188
195
  oldBlockReconnect: boolean | null | undefined
189
196
 
190
- oldConnection: AmqpConnectionManager | null | undefined
197
+ oldPublishConnection: ConnectionData
198
+
199
+ oldConsumeConnection: ConnectionData
191
200
 
192
201
  oldEm: EventEmitter;
193
202
 
@@ -207,9 +216,22 @@ class RabbitMq implements IAfRabbitMq {
207
216
 
208
217
  constructor(options: AfRabbitOptions = {}, redisConfig?: RedisConfig) {
209
218
  this.em = new EventEmitter();
210
- this.channel = null;
219
+ this.publishChannel = null;
211
220
  this.publishChannelSetupPromise = null;
212
- this.connection = null;
221
+ this.publishConnection = {
222
+ amqpConnection: null,
223
+ creatingConnection: false,
224
+ connectionCreatedEventName: 'publishConnectionCreated',
225
+ connectionFailedEventName: 'publishConnectionFailed',
226
+ blockReconnect: false,
227
+ };
228
+ this.consumeConnection = {
229
+ amqpConnection: null,
230
+ creatingConnection: false,
231
+ connectionCreatedEventName: 'consumeConnectionCreated',
232
+ connectionFailedEventName: 'consumeConnectionFailed',
233
+ blockReconnect: false,
234
+ };
213
235
  this.creatingConnection = false;
214
236
  this.exchanges = {};
215
237
  this.queues = {};
@@ -218,10 +240,6 @@ class RabbitMq implements IAfRabbitMq {
218
240
  this.consumers = [];
219
241
  this.options = options;
220
242
 
221
- if (!options?.vhost) {
222
- this.options.vhost = process.env.VHOST_NAME || 'quorum-vhost';
223
- }
224
-
225
243
  this.redisClient = redisConfig && getRedisInstance(redisConfig);
226
244
  if (this.redisClient) {
227
245
  this.redisLock = promisify(RedisLock(this.redisClient)) as RedisLockType;
@@ -237,11 +255,24 @@ class RabbitMq implements IAfRabbitMq {
237
255
  });
238
256
  }
239
257
 
240
- // TODO: DELETE OLD PROPERTIES
258
+ // TODO: [QUORUM-PHASE-3] Delete the old properties that we use for the old consumers and publishers
241
259
  this.oldEm = new EventEmitter();
242
- this.oldChannel = null;
260
+ this.oldPublishChannel = null;
243
261
  this.oldPublishChannelSetupPromise = null;
244
- this.oldConnection = null;
262
+ this.oldPublishConnection = {
263
+ amqpConnection: null,
264
+ creatingConnection: false,
265
+ connectionCreatedEventName: 'oldPublishConnectionCreated',
266
+ connectionFailedEventName: 'oldPublishConnectionFailed',
267
+ blockReconnect: false,
268
+ };
269
+ this.oldConsumeConnection = {
270
+ amqpConnection: null,
271
+ creatingConnection: false,
272
+ connectionCreatedEventName: 'oldConsumeConnectionCreated',
273
+ connectionFailedEventName: 'oldConsumeConnectionFailed',
274
+ blockReconnect: false,
275
+ };
245
276
  this.oldCreatingConnection = false;
246
277
  this.oldExchanges = {};
247
278
  this.oldQueues = {};
@@ -252,57 +283,57 @@ class RabbitMq implements IAfRabbitMq {
252
283
  }
253
284
 
254
285
  private assertVHost = async () => {
255
- if (this.isVHostExist) {
286
+ if (this.doesVHostExist) {
256
287
  return;
257
288
  }
258
289
 
259
- if (!this.options?.vhost) {
260
- throw new Error('vhost is not defined');
261
- }
262
-
263
- const { vhost } = this.options;
264
- const auth = {
265
- auth: {
266
- username: process.env.RABBITMQ_USERNAME || 'guest',
267
- password: process.env.RABBITMQ_PASSWORD || 'guest',
268
- },
290
+ const username = process.env.RABBITMQ_USERNAME || 'guest';
291
+ const password = process.env.RABBITMQ_PASSWORD || 'guest';
292
+ const credentials = Buffer.from(`${username}:${password}`).toString('base64');
293
+ const headers = {
294
+ Authorization: `Basic ${credentials}`,
295
+ 'Content-Type': 'application/json',
269
296
  };
270
- const rabbitHost = `http://${(this.options.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost').split(':')[0]}:15672`;
297
+
298
+ const rabbitHost = `http://${(this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost').split(':')[0]}:15672`;
299
+
300
+ const url = `${rabbitHost}/api/vhosts/${encodeURIComponent(this.vhost)}`;
271
301
 
272
302
  try {
273
- const response = await axios.get(`${rabbitHost}/api/vhosts/${encodeURIComponent(vhost)}`, auth);
303
+ const response = await fetch(url, {
304
+ method: 'GET',
305
+ headers,
306
+ });
274
307
 
275
- if (response.status !== 200) {
276
- throw new Error('Failed to check vhost');
308
+ if (response.status === 200) {
309
+ this.doesVHostExist = true;
310
+ logger.info('Vhost exists', { vhost: this.vhost });
311
+ return;
277
312
  }
278
313
 
279
- this.isVHostExist = true;
280
- logger.info('Vhost exists', { vhost });
281
- } catch (error) {
282
- // If the vhost does not exist, create it
283
- if (error instanceof axios.AxiosError && error.response?.status === 404) {
284
- try {
285
- await axios.put(
286
- `${rabbitHost}/api/vhosts/${encodeURIComponent(vhost)}`,
287
- {
288
- default_queue_type: 'quorum',
289
- },
290
- auth,
291
- );
292
-
293
- this.isVHostExist = true;
294
- logger.info('Vhost created', { vhost: this.options?.vhost });
295
- return;
296
- } catch (createError) {
297
- logger.error('Failed to create vhost', { error: createError });
298
- throw createError;
299
- }
314
+ if (response.status !== 404) {
315
+ logger.error('Failed to check vhost', { response });
316
+ throw new RabbitError('Failed to check vhost');
317
+ }
318
+
319
+ const createResponse = await fetch(url, {
320
+ method: 'PUT',
321
+ headers,
322
+ body: JSON.stringify({ default_queue_type: 'quorum' }),
323
+ });
324
+
325
+ if (!createResponse.ok) {
326
+ logger.error('Failed to create vhost', { response: createResponse });
327
+ throw new RabbitError('Failed to create vhost');
300
328
  }
301
329
 
302
- logger.error('Failed to check vhost', { error });
330
+ this.doesVHostExist = true;
331
+ logger.info('Vhost created', { vhost: this.vhost });
332
+ } catch (error) {
333
+ logger.error('Failed to check or create vhost', { error });
303
334
  throw error;
304
335
  }
305
- }
336
+ };
306
337
 
307
338
  private shouldConsumeMessageByTimestamp = async (msg: ConsumeMessageOrNull) => {
308
339
  if (msg) {
@@ -351,13 +382,13 @@ class RabbitMq implements IAfRabbitMq {
351
382
  if (
352
383
  !skipRetry
353
384
  && (
354
- !msg.properties.headers[RETRY_HEADER]
385
+ !msg.properties.headers?.[RETRY_HEADER]
355
386
  || parseInt(msg.properties.headers[RETRY_HEADER], 10) < options.retries
356
387
  )
357
388
  ) {
358
389
  await this.sendToQueue(queue, RabbitMq.parseMsg(msg).content, options, {
359
390
  ...msg.properties.headers,
360
- [RETRY_HEADER]: msg.properties.headers[RETRY_HEADER]
391
+ [RETRY_HEADER]: msg.properties.headers?.[RETRY_HEADER]
361
392
  ? msg.properties.headers[RETRY_HEADER] + 1
362
393
  : 1,
363
394
  });
@@ -365,7 +396,7 @@ class RabbitMq implements IAfRabbitMq {
365
396
  const deadQueue = `${queue}-dead`;
366
397
  await this.sendToQueue(deadQueue, RabbitMq.parseMsg(msg).content, deadQueueOptions, {
367
398
  ...msg.properties.headers,
368
- [RETRY_HEADER]: msg.properties.headers[RETRY_HEADER]
399
+ [RETRY_HEADER]: msg.properties.headers?.[RETRY_HEADER]
369
400
  ? msg.properties.headers[RETRY_HEADER] + 1
370
401
  : 1,
371
402
  });
@@ -379,30 +410,38 @@ class RabbitMq implements IAfRabbitMq {
379
410
  }
380
411
  }
381
412
 
382
- async getConnection() {
413
+ async getConnection(connection: ConnectionData) {
383
414
  return new Promise<AmqpConnectionManager>(async (resolve, reject) => {
384
- if (this.blockReconnect) {
415
+ const {
416
+ amqpConnection: connectionLocal,
417
+ creatingConnection,
418
+ connectionCreatedEventName,
419
+ connectionFailedEventName,
420
+ blockReconnect,
421
+ } = connection;
422
+
423
+ if (blockReconnect) {
385
424
  debug('rabbit: block reconnect');
386
425
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
387
426
  // @ts-ignore
388
427
  return resolve();
389
428
  }
390
- if (this.connection !== null) {
391
- if (this.options?.disableReconnect || this.connection?.isConnected()) {
429
+ if (connectionLocal !== null) {
430
+ if (this.options?.disableReconnect || connectionLocal?.isConnected()) {
392
431
  debug('rabbit: connection - is connected');
393
432
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
394
433
  // @ts-ignore
395
- return resolve(this.connection);
434
+ return resolve(connectionLocal);
396
435
  }
397
436
  debug('rabbit: connection - reconnecting');
398
437
  }
399
- if (this.creatingConnection) {
438
+ if (creatingConnection) {
400
439
  debug('rabbit: creating connection emi');
401
- this.em.once(CONNECTION_CREATED_CONST, resolve);
402
- this.em.once(CONNECTION_FAILED_CONST, reject);
440
+ this.em.once(connectionCreatedEventName, resolve);
441
+ this.em.once(connectionFailedEventName, reject);
403
442
  return;
404
443
  }
405
- this.creatingConnection = true;
444
+ connection.creatingConnection = true;
406
445
  let isResolved = false;
407
446
 
408
447
  // It is import to use it as a function and not as a variable
@@ -414,65 +453,72 @@ class RabbitMq implements IAfRabbitMq {
414
453
  const host = this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost';
415
454
 
416
455
  debug('rabbit: creating connection', { host, userName, HEARTBEAT });
417
- return [`amqp://${userName}:${password}@${host}/${this.options?.vhost}?heartbeat=${HEARTBEAT}`];
456
+ return [`amqp://${userName}:${password}@${host}/${this.vhost}?heartbeat=${HEARTBEAT}`];
418
457
  };
419
458
 
420
459
  const defaultUrls = findServers();
421
- const connection: AmqpConnectionManager = await connect(defaultUrls, {
460
+ const newConnection: AmqpConnectionManager = await connect(defaultUrls, {
422
461
  findServers,
423
462
  });
424
463
 
425
- this.connection = connection;
426
- this.connection.on('error', (err) => {
464
+ connection.amqpConnection = newConnection;
465
+ newConnection.on('error', (err) => {
427
466
  logger.error('rabbit: connection error', { err });
428
467
  if (!isResolved) {
429
468
  isResolved = true;
430
469
  reject(err);
431
- this.em.emit(CONNECTION_FAILED_CONST, err);
470
+ this.em.emit(connectionFailedEventName, err);
432
471
  }
433
472
  });
434
473
 
435
- this.connection.on('connectFailed', (err) => {
474
+ newConnection.on('connectFailed', (err) => {
436
475
  this.consumersTags = [];
437
- logger.error('rabbit: connection connectFailed', { err, advice: 'Check if the vhost exist', vhost: this.options?.vhost });
476
+ if (typeof err.url === 'string') {
477
+ err.url = this.maskURL(err.url);
478
+ }
479
+ logger.error('rabbit: connection connectFailed', { err, advice: 'Check if the vhost exist', vhost: this.vhost });
438
480
  if (!isResolved) {
439
481
  isResolved = true;
440
482
  reject(err);
441
- this.em.emit(CONNECTION_FAILED_CONST, err);
483
+ this.em.emit(connectionFailedEventName, err);
442
484
  }
443
485
  });
444
486
 
445
- this.connection.on('disconnect', ({ err }) => {
446
- // this.channel = null;
487
+ newConnection.on('disconnect', ({ err }) => {
447
488
  this.consumersTags = [];
448
489
  debug('rabbit: connection closed');
449
490
  if (this.options?.disableReconnect) {
450
491
  logger.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
451
- this.blockReconnect = true;
492
+ connection.blockReconnect = true;
452
493
  } else {
453
494
  logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
454
495
  }
455
496
  });
456
497
 
457
- this.connection.once('connect', async () => {
498
+ newConnection.once('connect', async () => {
458
499
  debug('rabbit: connection established');
459
- this.creatingConnection = false;
460
- this.em.emit(CONNECTION_CREATED_CONST, connection);
500
+ connection.creatingConnection = false;
501
+ this.em.emit(connectionCreatedEventName, newConnection);
461
502
  isResolved = true;
462
- resolve(connection);
503
+ resolve(newConnection);
463
504
  });
464
505
  });
465
506
  }
466
507
 
467
- async getNewChannel({ name = rand().toString(), onClose = null, options = {} }: newChannelOpts = {}) {
468
- let connection!: AmqpConnectionManager;
508
+ async getNewChannel({
509
+ name = rand().toString(),
510
+ onClose = null,
511
+ options = {},
512
+ connection,
513
+ }: newChannelOpts) {
514
+ let localConnection!: AmqpConnectionManager;
469
515
  try {
470
- connection = await this.getConnection();
516
+ localConnection = await this.getConnection(connection);
471
517
  } catch (e) {
472
518
  logger.error(`rabbit: error on get connection for new channel ${name} `, { e });
473
519
  throw e;
474
520
  }
475
- const channel = connection.createChannel({ ...options });
521
+ const channel = localConnection?.createChannel({ ...options });
476
522
  once(channel, 'close').then((args) => {
477
523
  logger.error(`rabbit: channel ${name} closed`);
478
524
  onClose?.(args);
@@ -487,19 +533,21 @@ class RabbitMq implements IAfRabbitMq {
487
533
  }
488
534
  }
489
535
 
490
- async assertChannel({ force = false } : assertChannelOpts = {}): Promise<ChannelWrapper> {
536
+ async assertChannel({ force = false, connection }: assertChannelOpts): Promise<ChannelWrapper> {
491
537
  if (!this.publishChannelSetupPromise) {
492
538
  this.publishChannelSetupPromise = new Promise<ChannelWrapper>(async (resolve, reject) => {
493
- if (this.channel && !force) {
494
- return resolve(this.channel);
539
+ if (this.publishChannel && !force) {
540
+ return resolve(this.publishChannel);
495
541
  }
496
542
 
497
543
  try {
498
- const channel = await this.getNewChannel({});
544
+ const channel = await this.getNewChannel({ connection });
499
545
  channel.on('error', (err) => {
500
546
  logger.error('rabbit: channel error', { err });
501
547
  });
502
- this.channel = channel;
548
+ if (this.publishConnection === connection) {
549
+ this.publishChannel = channel;
550
+ }
503
551
  resolve(channel);
504
552
  } catch (e) {
505
553
  reject(e);
@@ -509,8 +557,8 @@ class RabbitMq implements IAfRabbitMq {
509
557
  return this.publishChannelSetupPromise;
510
558
  }
511
559
 
512
- async assertExchange(exchangeName: string, options?: any) {
513
- const channel: ChannelWrapper = await this.assertChannel();
560
+ async assertExchange(exchangeName: string, connection: ConnectionData) {
561
+ const channel: ChannelWrapper = await this.assertChannel({ connection });
514
562
 
515
563
  if (this.exchanges[exchangeName]) {
516
564
  delete this.assertExchangePromises[exchangeName];
@@ -526,35 +574,34 @@ class RabbitMq implements IAfRabbitMq {
526
574
  return this.exchanges[exchangeName];
527
575
  }
528
576
 
529
- // TODO: Change the implementation to the new one
530
577
  async getQueueLength(queue: string) {
531
578
  RabbitMq.validateName('queue', queue);
532
- const { oldChannel: channel } = this;
579
+ const { publishChannel: channel } = this;
533
580
  if (!channel) {
534
- throw new Error('channel is not defined');
581
+ throw new RabbitError('channel is not defined');
535
582
  }
536
- debug('rabbit: getting queue length', { queue, connected: this.connection?.isConnected() });
583
+ debug('rabbit: getting queue length', { queue, connected: this.publishConnection.amqpConnection?.isConnected() });
537
584
  return channel?.checkQueue(queue);
538
585
  }
539
586
 
540
- private async deleteQueue(queue: string) {
587
+ private async deleteQueue(queue: string, connection: ConnectionData) {
541
588
  RabbitMq.validateName('queue', queue);
542
- const channel: ChannelWrapper = await this.assertChannel();
589
+ const channel: ChannelWrapper = await this.assertChannel({ connection });
543
590
  logger.info('rabbit: deleting queue', { queue });
544
591
  const deleteQueueRes = await channel.deleteQueue(queue);
545
592
  debug('queue deleted', deleteQueueRes);
546
593
  return deleteQueueRes;
547
594
  }
548
595
 
549
- // TODO: Change the implementation to the new one
550
596
  async bindQueue(queue: string, exchange: string) {
551
- const channel: ChannelWrapper = await this.assertChannelOld();
597
+ const channel: ChannelWrapper = await this.assertChannel({ connection: this.publishConnection });
552
598
  await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.bindQueue(queue, exchange, ''));
553
599
  return channel.bindQueue(queue, exchange, '');
554
600
  }
555
601
 
556
602
  async setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
557
603
  let queue: Replies.AssertQueue;
604
+ const connection = this.publishConnection;
558
605
  const localeOptions = {
559
606
  ...options,
560
607
  durable: true,
@@ -565,7 +612,7 @@ class RabbitMq implements IAfRabbitMq {
565
612
  },
566
613
  };
567
614
  try {
568
- const channel: ChannelWrapper = await this.assertChannel();
615
+ const channel: ChannelWrapper = await this.assertChannel({ connection });
569
616
  debug('assertQueue->channel.addSetup', { queueName });
570
617
  await channel.addSetup(async (setupChannel: ConfirmChannel) => {
571
618
  await setupChannel.assertQueue(queueName, localeOptions);
@@ -576,8 +623,8 @@ class RabbitMq implements IAfRabbitMq {
576
623
  logger.error('rabbit: assertQueue error', { queueName, options, error: e });
577
624
  if (!this.options?.dontRetryAssert) {
578
625
  debug('retrying assertQueue', { queueName });
579
- const channel = await this.assertChannel({ force: true });
580
- await this.deleteQueue(queueName);
626
+ const channel = await this.assertChannel({ force: true, connection });
627
+ await this.deleteQueue(queueName, connection);
581
628
 
582
629
  debug('retrying assertQueue->channel.addSetup', { queueName });
583
630
  await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
@@ -592,7 +639,7 @@ class RabbitMq implements IAfRabbitMq {
592
639
  return queue;
593
640
  }
594
641
 
595
- // TODO: Can be deleted after deleting the old consumers
642
+ // TODO: [QUORUM-PHASE-3] Can be deleted after deleting the old consumers and publishers because all the queues are created us quorum queues
596
643
  static shouldUseQuorum(queueName: string): boolean {
597
644
  const envQuorumQueuesWhitelist = process.env.QUORUM_QUEUES_WHITELIST;
598
645
 
@@ -637,8 +684,8 @@ class RabbitMq implements IAfRabbitMq {
637
684
 
638
685
  // Used by the microservices to consume messages from the queue
639
686
  async consume(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
640
- // TODO: USE THE IMPLEMENTATION OF THE NEW CONSUME AND DELETE THE NEW AND OLD
641
- if (options?.isQuorumQueue !== false) {
687
+ // TODO: [QUORUM-PHASE-3] Use only the implementation of consumeNew and delete consumeNew and consumeOld
688
+ if (options?.isQuorumQueue !== false && DISABLE_QUORUM_QUEUES !== 'true') {
642
689
  await this.assertVHost();
643
690
  await this.consumeNew(queue, callback, options);
644
691
  }
@@ -646,12 +693,12 @@ class RabbitMq implements IAfRabbitMq {
646
693
  await this.consumeOld(queue, callback, options);
647
694
  }
648
695
 
649
- // TODO: DELETE
696
+ // TODO: [QUORUM-PHASE-3] Delete consumeNew we do not use it anymore
650
697
  async consumeNew(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
651
698
  await this.consumeFromRabbit(queue, callback, options);
652
699
  }
653
700
 
654
- // TODO: DELETE
701
+ // TODO: [QUORUM-PHASE-3] Delete consumeOld we do not use it anymore
655
702
  async consumeOld(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
656
703
  await this.consumeFromRabbitOld(queue, callback, options);
657
704
  }
@@ -686,11 +733,11 @@ class RabbitMq implements IAfRabbitMq {
686
733
  } = optionsWithDefaults;
687
734
  if (useConsumeWithLock) {
688
735
  if (!this.redisLock) {
689
- throw new Error('Usage of consumeWithLock requires RedisInstance');
736
+ throw new RabbitError('Usage of consumeWithLock requires RedisInstance');
690
737
  }
691
738
  logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
692
739
  }
693
- const channel = await this.getNewChannel({});
740
+ const channel = await this.getNewChannel({ connection: this.consumeConnection });
694
741
  return channel.addSetup(async (confirmChannel: ConfirmChannel) => {
695
742
  const q = await this.assertQueue(queue, optionsWithDefaults);
696
743
  await confirmChannel.prefetch(limit, false);
@@ -701,9 +748,9 @@ class RabbitMq implements IAfRabbitMq {
701
748
  return null;
702
749
  }
703
750
 
704
- const traceId = msg.properties.headers[TRACING_HEADER];
705
- const userId = msg.properties.headers[USER_TRACING_HEADER];
706
- const automationId = msg.properties.headers[AUTOMATION_ID_HEADER];
751
+ const traceId = msg.properties.headers![TRACING_HEADER];
752
+ const userId = msg.properties.headers![USER_TRACING_HEADER];
753
+ const automationId = msg.properties.headers![AUTOMATION_ID_HEADER];
707
754
  const parsedMessage = RabbitMq.parseMsg(msg);
708
755
  const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
709
756
  const trace = newTrace(traceTypes.RABBIT);
@@ -786,10 +833,14 @@ class RabbitMq implements IAfRabbitMq {
786
833
  RabbitMq.validateName('exchange', exchange);
787
834
  RabbitMq.validateName('queue', queue);
788
835
  const { limit, deadMessageTtl } = optionsWithDefaults;
789
- if (options?.isQuorumQueue !== false) {
836
+ // TODO: [QUORUM-PHASE-3] Delete the if statement after all the queues are created as quorum queues
837
+ if (options?.isQuorumQueue !== false && DISABLE_QUORUM_QUEUES !== 'true') {
790
838
  await this.assertVHost();
791
839
  await this.saveConsumer(queue, callback, options);
792
- const channel: ChannelWrapper = await this.getNewChannel({ name: `consume-exchange-${exchange}-queue-${queue}` });
840
+ const channel: ChannelWrapper = await this.getNewChannel({
841
+ name: `consume-exchange-${exchange}-queue-${queue}`,
842
+ connection: this.consumeConnection,
843
+ });
793
844
 
794
845
  await channel.addSetup(async (c: ConfirmChannel) => {
795
846
  const assertExchange = await assertExchangeFanout(c, exchange);
@@ -806,10 +857,12 @@ class RabbitMq implements IAfRabbitMq {
806
857
  ]);
807
858
  });
808
859
  }
809
-
810
- // TODO: DELETE OLD
860
+ // TODO: [QUORUM-PHASE-3] Delete the old implementation
811
861
  await this.saveConsumerOld(queue, callback, options);
812
- const channelOld: ChannelWrapper = await this.getNewChannelOld({ name: `consume-exchange-${exchange}-queue-${queue}-old` });
862
+ const channelOld: ChannelWrapper = await this.getNewChannelOld({
863
+ name: `consume-exchange-${exchange}-queue-${queue}-old`,
864
+ connection: this.oldConsumeConnection,
865
+ });
813
866
  await channelOld.addSetup(async (c: ConfirmChannel) => {
814
867
  const assertExchange = await assertExchangeFanout(c, exchange);
815
868
  await c.assertQueue(queue);
@@ -827,12 +880,25 @@ class RabbitMq implements IAfRabbitMq {
827
880
  }
828
881
 
829
882
  // Used by the microservices to publish messages to the exchange
830
- // TODO: Change the implementation to the new one
831
- async publish(exchange: string, content: any, customHeaders?: any) : Promise<boolean> {
883
+ // TODO: [QUORUM-PHASE-3] isQuorumQueue flag is not needed anymore because all the queues are created as quorum queues
884
+ async publish(exchange: string, content: any, customHeaders?: any, isQuorumQueue = true) : Promise<boolean> {
885
+ if (isQuorumQueue && DISABLE_QUORUM_QUEUES !== 'true') {
886
+ return wrapSetImmediate(async () => {
887
+ await this.assertVHost();
888
+ RabbitMq.validateName('exchange', exchange);
889
+ const channel: ChannelWrapper = await this.assertChannel({ connection: this.publishConnection });
890
+ await this.assertExchange(exchange, this.publishConnection);
891
+ await channel.publish(exchange, '',
892
+ Buffer.from(JSON.stringify(content)),
893
+ RabbitMq.getPublishOptions(customHeaders));
894
+ });
895
+ }
896
+
897
+ // TODO: [QUORUM-PHASE-3] Delete the old implementation
832
898
  return wrapSetImmediate(async () => {
833
899
  RabbitMq.validateName('exchange', exchange);
834
- const channel: ChannelWrapper = await this.assertChannelOld();
835
- await this.assertExchangeOld(exchange);
900
+ const channel: ChannelWrapper = await this.assertChannelOld({ connection: this.oldPublishConnection });
901
+ await this.assertExchangeOld(exchange, this.oldPublishConnection);
836
902
  await channel.publish(exchange, '',
837
903
  Buffer.from(JSON.stringify(content)),
838
904
  RabbitMq.getPublishOptions(customHeaders));
@@ -840,65 +906,97 @@ class RabbitMq implements IAfRabbitMq {
840
906
  }
841
907
 
842
908
  // Used by the microservices to send messages to the queue
843
- // TODO: Change the implementation to the new one
909
+ // TODO: [QUORUM-PHASE-3] isQuorumQueue flag is not needed anymore because all the queues are created as quorum queues
844
910
  async sendToQueue(
845
911
  queue: string,
846
912
  content: any,
847
913
  options?: any,
848
914
  customHeaders?: any,
915
+ isQuorumQueue = true,
849
916
  ): Promise<boolean | undefined> {
850
- try {
851
- await this.assertChannelOld();
852
- } catch (e) {
853
- logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
854
- throw e;
855
- }
917
+ if (isQuorumQueue && DISABLE_QUORUM_QUEUES !== 'true') {
918
+ try {
919
+ await this.assertVHost();
920
+ await this.assertChannel({ connection: this.publishConnection });
921
+ } catch (e) {
922
+ logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
923
+ throw e;
924
+ }
856
925
 
857
- try {
858
- RabbitMq.validateName('queue', queue);
859
- await this.assertQueueOld(queue, options);
860
- } catch (e) {
861
- logger.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
862
- throw e;
863
- }
926
+ try {
927
+ RabbitMq.validateName('queue', queue);
928
+ await this.assertQueue(queue, options);
929
+ } catch (e) {
930
+ logger.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
931
+ throw e;
932
+ }
864
933
 
865
- try {
866
- const res = await this.oldChannel?.sendToQueue(queue,
867
- Buffer.from(JSON.stringify(content)),
868
- RabbitMq.getPublishOptions(customHeaders));
869
- debug(`rabbit: sending to queue ${queue}`, { res });
870
- return res;
871
- } catch (e) {
872
- const isConnected = await this.isConnected();
873
- logger.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
874
- throw e;
934
+ try {
935
+ const res = await this.publishChannel?.sendToQueue(queue,
936
+ Buffer.from(JSON.stringify(content)),
937
+ RabbitMq.getPublishOptions(customHeaders));
938
+ debug(`rabbit: sending to queue ${queue}`, { res });
939
+ return res;
940
+ } catch (e) {
941
+ const isConnected = await this.isConnected();
942
+ logger.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
943
+ throw e;
944
+ }
945
+ } else {
946
+ try {
947
+ await this.assertChannelOld({ connection: this.oldPublishConnection });
948
+ } catch (e) {
949
+ logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
950
+ throw e;
951
+ }
952
+
953
+ try {
954
+ RabbitMq.validateName('queue', queue);
955
+ await this.assertQueueOld(queue, options);
956
+ } catch (e) {
957
+ logger.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
958
+ throw e;
959
+ }
960
+
961
+ try {
962
+ const res = await this.oldPublishChannel?.sendToQueue(queue,
963
+ Buffer.from(JSON.stringify(content)),
964
+ RabbitMq.getPublishOptions(customHeaders));
965
+ debug(`rabbit: sending to queue ${queue}`, { res });
966
+ return res;
967
+ } catch (e) {
968
+ const isConnected = await this.isConnectedOld();
969
+ logger.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
970
+ throw e;
971
+ }
875
972
  }
876
973
  }
877
974
 
878
- // TODO: After changing the publish to the new implementation change the from the old to the new
879
- async isConnected() : Promise<boolean> {
880
- const connection = await this.getConnectionOld();
881
- const isConnected = connection.isConnected();
975
+ async isConnected(): Promise<boolean> {
976
+ debug('rabbit: start old is connected');
977
+ const consumeConnection = await this.getConnection(this.consumeConnection);
978
+ const publishConnection = await this.getConnection(this.publishConnection);
979
+ const isConnected = consumeConnection.isConnected() && publishConnection.isConnected();
882
980
  if (!isConnected) {
883
- logger.error('rabbit: isConnected - false');
981
+ logger.error('rabbit: old isConnected - false');
884
982
  return false;
885
983
  }
886
- const channel: any = await this.assertChannelOld();
984
+ const channel: any = await this.assertChannel({ connection: this.publishConnection });
887
985
  try {
888
986
  await Promise.all([
889
987
  channel.waitForConnect(),
890
- ...this.oldConsumers.map((c: AfConsumer) => channel.checkQueue(c.queue)),
988
+ ...this.consumers.map((c: AfConsumer) => channel.checkQueue(c.queue)),
891
989
  ]);
892
990
  } catch (e) {
893
991
  logger.error('rabbit: isConnected - false');
894
992
  return false;
895
993
  }
896
- logger.info('rabbit: isConnected - true');
994
+ logger.debug('rabbit: isConnected - true');
897
995
  return true;
898
996
  }
899
997
 
900
998
  async gracefulShutdown(signal: string) : Promise<void> {
901
- // TODO: DELETE OLD
999
+ // TODO: [QUORUM-PHASE-3] Delete the old implementation they are not needed anymore
902
1000
  const tagsNumber = this.consumersTags.length + this.oldConsumersTags.length;
903
1001
  logger.info(`rabbit: [gracefully-shutdown] received ${signal}! canceling #${tagsNumber} tags...`);
904
1002
  const cancelTagPromises = this.consumersTags.map(([channel, tag]) => channel.cancel(tag));
@@ -925,11 +1023,11 @@ class RabbitMq implements IAfRabbitMq {
925
1023
  } = optionsWithDefaults;
926
1024
  if (useConsumeWithLock) {
927
1025
  if (!this.redisLock) {
928
- throw new Error('Usage of consumeWithLock requires RedisInstance');
1026
+ throw new RabbitError('Usage of consumeWithLock requires RedisInstance');
929
1027
  }
930
1028
  logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
931
1029
  }
932
- const channel = await this.getNewChannelOld({});
1030
+ const channel = await this.getNewChannelOld({ connection: this.oldConsumeConnection });
933
1031
  return channel.addSetup(async (confirmChannel: ConfirmChannel) => {
934
1032
  const q = await this.assertQueueOld(queue, optionsWithDefaults);
935
1033
  await confirmChannel.prefetch(limit, false);
@@ -940,9 +1038,9 @@ class RabbitMq implements IAfRabbitMq {
940
1038
  return null;
941
1039
  }
942
1040
 
943
- const traceId = msg.properties.headers[TRACING_HEADER];
944
- const userId = msg.properties.headers[USER_TRACING_HEADER];
945
- const automationId = msg.properties.headers[AUTOMATION_ID_HEADER];
1041
+ const traceId = msg.properties.headers![TRACING_HEADER];
1042
+ const userId = msg.properties.headers![USER_TRACING_HEADER];
1043
+ const automationId = msg.properties.headers![AUTOMATION_ID_HEADER];
946
1044
  const parsedMessage = RabbitMq.parseMsg(msg);
947
1045
  const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
948
1046
  const trace = newTrace(traceTypes.RABBIT);
@@ -1019,16 +1117,26 @@ class RabbitMq implements IAfRabbitMq {
1019
1117
  });
1020
1118
  }
1021
1119
 
1022
- // TODO: DELETE OLD PROPERTIES UNDER THIS LINE
1023
- async getNewChannelOld({ name = rand().toString(), onClose = null, options = {} }: newChannelOpts = {}) {
1024
- let connection!: AmqpConnectionManager;
1120
+ // TODO: [QUORUM-PHASE-3] Delete all the function under this line.
1121
+ async getNewChannelOld({
1122
+ name = rand().toString(),
1123
+ onClose = null,
1124
+ options = {},
1125
+ connection,
1126
+ }: newChannelOpts) {
1127
+ let localConnection!: AmqpConnectionManager;
1025
1128
  try {
1026
- connection = await this.getConnectionOld();
1129
+ localConnection = await this.getConnectionOld(connection);
1027
1130
  } catch (e) {
1028
1131
  logger.error(`rabbit: error on get connection for new channel ${name} `, { e });
1029
1132
  throw e;
1030
1133
  }
1031
- const channel = connection.createChannel({ ...options });
1134
+
1135
+ if (!localConnection) {
1136
+ throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
1137
+ }
1138
+
1139
+ const channel = localConnection?.createChannel({ ...options });
1032
1140
  once(channel, 'close').then((args) => {
1033
1141
  logger.error(`rabbit: channel ${name} closed`);
1034
1142
  onClose?.(args);
@@ -1043,30 +1151,38 @@ class RabbitMq implements IAfRabbitMq {
1043
1151
  }
1044
1152
  }
1045
1153
 
1046
- async getConnectionOld() {
1154
+ async getConnectionOld(connection: ConnectionData) {
1047
1155
  return new Promise<AmqpConnectionManager>(async (resolve, reject) => {
1048
- if (this.oldBlockReconnect) {
1156
+ const {
1157
+ amqpConnection: connectionLocal,
1158
+ creatingConnection,
1159
+ connectionCreatedEventName,
1160
+ connectionFailedEventName,
1161
+ blockReconnect,
1162
+ } = connection;
1163
+
1164
+ if (blockReconnect) {
1049
1165
  debug('rabbit: block reconnect');
1050
1166
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
1051
1167
  // @ts-ignore
1052
1168
  return resolve();
1053
1169
  }
1054
- if (this.oldConnection !== null) {
1055
- if (this.options?.disableReconnect || this.oldConnection?.isConnected()) {
1170
+ if (connectionLocal !== null) {
1171
+ if (this.options?.disableReconnect || connectionLocal?.isConnected()) {
1056
1172
  debug('rabbit: connection - is connected');
1057
1173
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
1058
1174
  // @ts-ignore
1059
- return resolve(this.oldConnection);
1175
+ return resolve(connectionLocal);
1060
1176
  }
1061
1177
  debug('rabbit: connection - reconnecting');
1062
1178
  }
1063
- if (this.oldCreatingConnection) {
1179
+ if (creatingConnection) {
1064
1180
  debug('rabbit: creating connection emi');
1065
- this.oldEm.once(CONNECTION_CREATED_CONST, resolve);
1066
- this.oldEm.once(CONNECTION_FAILED_CONST, reject);
1181
+ this.oldEm.once(connectionCreatedEventName, resolve);
1182
+ this.oldEm.once(connectionFailedEventName, reject);
1067
1183
  return;
1068
1184
  }
1069
- this.oldCreatingConnection = true;
1185
+ connection.creatingConnection = true;
1070
1186
  let isResolved = false;
1071
1187
 
1072
1188
  // It is import to use it as a function and not as a variable
@@ -1083,47 +1199,50 @@ class RabbitMq implements IAfRabbitMq {
1083
1199
  };
1084
1200
 
1085
1201
  const defaultUrls = findServers();
1086
- const connection: AmqpConnectionManager = await connect(defaultUrls, {
1202
+ const newConnection: AmqpConnectionManager = await connect(defaultUrls, {
1087
1203
  findServers,
1088
1204
  });
1089
1205
 
1090
- this.oldConnection = connection;
1091
- this.oldConnection.on('error', (err) => {
1206
+ connection.amqpConnection = newConnection;
1207
+ newConnection.on('error', (err) => {
1092
1208
  logger.error('rabbit: connection error', { err });
1093
1209
  if (!isResolved) {
1094
1210
  isResolved = true;
1095
1211
  reject(err);
1096
- this.oldEm.emit(CONNECTION_FAILED_CONST, err);
1212
+ this.oldEm.emit(connectionFailedEventName, err);
1097
1213
  }
1098
1214
  });
1099
1215
 
1100
- this.oldConnection.on('connectFailed', (err) => {
1216
+ newConnection.on('connectFailed', (err) => {
1101
1217
  this.oldConsumersTags = [];
1218
+ if (typeof err.url === 'string') {
1219
+ err.url = this.maskURL(err.url);
1220
+ }
1102
1221
  logger.error('rabbit: connection connectFailed', { err });
1103
1222
  if (!isResolved) {
1104
1223
  isResolved = true;
1105
1224
  reject(err);
1106
- this.oldEm.emit(CONNECTION_FAILED_CONST, err);
1225
+ this.oldEm.emit(connectionFailedEventName, err);
1107
1226
  }
1108
1227
  });
1109
1228
 
1110
- this.oldConnection.on('disconnect', ({ err }) => {
1229
+ newConnection.on('disconnect', ({ err }) => {
1111
1230
  this.oldConsumersTags = [];
1112
1231
  debug('rabbit: connection closed');
1113
1232
  if (this.options?.disableReconnect) {
1114
1233
  logger.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
1115
- this.oldBlockReconnect = true;
1234
+ connection.blockReconnect = true;
1116
1235
  } else {
1117
1236
  logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
1118
1237
  }
1119
1238
  });
1120
1239
 
1121
- this.oldConnection.once('connect', async () => {
1240
+ newConnection.once('connect', async () => {
1122
1241
  debug('rabbit: connection established');
1123
- this.oldCreatingConnection = false;
1124
- this.oldEm.emit(CONNECTION_CREATED_CONST, connection);
1242
+ connection.creatingConnection = false;
1243
+ this.oldEm.emit(connectionCreatedEventName, newConnection);
1125
1244
  isResolved = true;
1126
- resolve(connection);
1245
+ resolve(newConnection);
1127
1246
  });
1128
1247
  });
1129
1248
  }
@@ -1157,6 +1276,7 @@ class RabbitMq implements IAfRabbitMq {
1157
1276
 
1158
1277
  async setupQueueOld(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
1159
1278
  let queue: Replies.AssertQueue;
1279
+ const connection = this.oldPublishConnection;
1160
1280
  const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
1161
1281
  const localeOptions = {
1162
1282
  ...options,
@@ -1168,7 +1288,7 @@ class RabbitMq implements IAfRabbitMq {
1168
1288
  },
1169
1289
  };
1170
1290
  try {
1171
- const channel: ChannelWrapper = await this.assertChannelOld();
1291
+ const channel: ChannelWrapper = await this.assertChannelOld({ connection });
1172
1292
  debug('assertQueue->channel.addSetup', { queueName });
1173
1293
  await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
1174
1294
  debug('assertQueue->channel.assertQueue', { queueName });
@@ -1177,8 +1297,8 @@ class RabbitMq implements IAfRabbitMq {
1177
1297
  logger.error('rabbit: assertQueue error', { queueName, options, error: e });
1178
1298
  if (!this.options?.dontRetryAssert) {
1179
1299
  debug('retrying assertQueue', { queueName });
1180
- const channel = await this.assertChannelOld({ force: true });
1181
- await this.deleteQueueOld(queueName);
1300
+ const channel = await this.assertChannelOld({ force: true, connection });
1301
+ await this.deleteQueueOld(queueName, connection);
1182
1302
 
1183
1303
  debug('retrying assertQueue->channel.addSetup', { queueName });
1184
1304
  await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
@@ -1193,19 +1313,21 @@ class RabbitMq implements IAfRabbitMq {
1193
1313
  return queue;
1194
1314
  }
1195
1315
 
1196
- async assertChannelOld({ force = false } : assertChannelOpts = {}): Promise<ChannelWrapper> {
1316
+ async assertChannelOld({ force = false, connection }: assertChannelOpts): Promise<ChannelWrapper> {
1197
1317
  if (!this.oldPublishChannelSetupPromise) {
1198
1318
  this.oldPublishChannelSetupPromise = new Promise<ChannelWrapper>(async (resolve, reject) => {
1199
- if (this.oldChannel && !force) {
1200
- return resolve(this.oldChannel);
1319
+ if (this.oldPublishChannel && !force) {
1320
+ return resolve(this.oldPublishChannel);
1201
1321
  }
1202
1322
 
1203
1323
  try {
1204
- const channel = await this.getNewChannelOld({});
1324
+ const channel = await this.getNewChannelOld({ connection });
1205
1325
  channel.on('error', (err) => {
1206
1326
  logger.error('rabbit: channel error', { err });
1207
1327
  });
1208
- this.oldChannel = channel;
1328
+ if (this.oldPublishConnection === connection) {
1329
+ this.oldPublishChannel = channel;
1330
+ }
1209
1331
  resolve(channel);
1210
1332
  } catch (e) {
1211
1333
  reject(e);
@@ -1215,17 +1337,17 @@ class RabbitMq implements IAfRabbitMq {
1215
1337
  return this.oldPublishChannelSetupPromise;
1216
1338
  }
1217
1339
 
1218
- private async deleteQueueOld(queue: string) {
1340
+ private async deleteQueueOld(queue: string, connection: ConnectionData) {
1219
1341
  RabbitMq.validateName('queue', queue);
1220
- const channel: ChannelWrapper = await this.assertChannelOld();
1342
+ const channel: ChannelWrapper = await this.assertChannelOld({ connection });
1221
1343
  logger.info('rabbit: deleting queue', { queue });
1222
1344
  const deleteQueueRes = await channel.deleteQueue(queue);
1223
1345
  debug('queue deleted', deleteQueueRes);
1224
1346
  return deleteQueueRes;
1225
1347
  }
1226
1348
 
1227
- async assertExchangeOld(exchangeName: string, options?: any) {
1228
- const channel: ChannelWrapper = await this.assertChannelOld();
1349
+ async assertExchangeOld(exchangeName: string, connection: ConnectionData) {
1350
+ const channel: ChannelWrapper = await this.assertChannelOld({ connection });
1229
1351
 
1230
1352
  if (this.oldExchanges[exchangeName]) {
1231
1353
  delete this.oldAssertExchangePromises[exchangeName];
@@ -1240,6 +1362,40 @@ class RabbitMq implements IAfRabbitMq {
1240
1362
  this.oldExchanges[exchangeName] = await this.oldAssertExchangePromises[exchangeName];
1241
1363
  return this.oldExchanges[exchangeName];
1242
1364
  }
1365
+
1366
+ async isConnectedOld() : Promise<boolean> {
1367
+ debug('rabbit: start old is connected');
1368
+ const oldConsumeConnection = await this.getConnectionOld(this.oldConsumeConnection);
1369
+ const oldPublishConnection = await this.getConnectionOld(this.oldPublishConnection);
1370
+ const oldIsConnected = oldConsumeConnection.isConnected() && oldPublishConnection.isConnected();
1371
+ if (!oldIsConnected) {
1372
+ logger.error('rabbit: old isConnected - false');
1373
+ return false;
1374
+ }
1375
+ const oldChannel: any = await this.assertChannelOld({ connection: this.oldPublishConnection });
1376
+ try {
1377
+ await Promise.all([
1378
+ oldChannel.waitForConnect(),
1379
+ ...this.oldConsumers.map((c: AfConsumer) => oldChannel.checkQueue(c.queue)),
1380
+ ]);
1381
+ } catch (e) {
1382
+ logger.error('rabbit: old isConnected - false');
1383
+ return false;
1384
+ }
1385
+ logger.debug('rabbit: old isConnected - true');
1386
+ return true;
1387
+ }
1388
+
1389
+ private maskURL = (url: string): string => {
1390
+ try {
1391
+ const urlObj = new URL(url);
1392
+ urlObj.username = '***';
1393
+ urlObj.password = '***';
1394
+ return urlObj.toString();
1395
+ } catch {
1396
+ return url;
1397
+ }
1398
+ }
1243
1399
  }
1244
1400
 
1245
1401
  export default RabbitMq;