@autofleet/rabbit 4.0.2 → 4.1.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.
package/src/index.ts CHANGED
@@ -1,24 +1,27 @@
1
- /* eslint-disable no-empty,consistent-return,no-async-promise-executor,@typescript-eslint/no-unused-vars,no-param-reassign */
2
- import { EventEmitter, once } from 'events';
3
- import { promisify } from 'util';
1
+ import { env } from 'node:process';
2
+ import { setImmediate } from 'node:timers/promises';
3
+ import { EventEmitter, once } from 'node:events';
4
4
  import moment from 'moment';
5
5
  import RedisLock from 'redis-lock';
6
6
  import {
7
- AmqpConnectionManager, ChannelWrapper, connect, CreateChannelOpts,
7
+ type AmqpConnectionManager, type ChannelWrapper, connect, type CreateChannelOpts,
8
8
  } from 'amqp-connection-manager';
9
+ // eslint-disable-next-line import/no-unresolved
10
+ import type { PublishOptions } from 'amqp-connection-manager/dist/types/ChannelWrapper';
9
11
  import {
10
12
  ConfirmChannel, ConsumeMessage, Options, Replies,
11
13
  } from 'amqplib';
12
14
  import { backOff } from 'exponential-backoff';
15
+ import type { LoggerInstanceManager } from '@autofleet/logger';
13
16
  import {
14
- getCurrentPayload, newTrace, traceTypes, createOrSetRabbitTrace, outbreak,
17
+ getUser, getCurrentPayload, newTrace, traceTypes, createOrSetRabbitTrace, outbreak,
15
18
  } from '@autofleet/zehut';
16
19
  import { randomUUID } from 'node:crypto';
17
20
  import logger from './logger';
18
21
  import RabbitError from './lib/rabbitError';
19
22
  import getRedisInstance, { RedisConfig } from './lib/redis';
20
23
  import {
21
- assertExchangeFanout, getAssertVhostExponentialBackoffConfig, rand, wrapSetImmediate,
24
+ assertExchangeFanout, createDeferredPromise, getAssertVhostExponentialBackoffConfig, rand,
22
25
  } from './lib/utils';
23
26
  import {
24
27
  AUTOMATION_ID_HEADER,
@@ -26,69 +29,66 @@ import {
26
29
  DEFAULT_OPTIONS,
27
30
  RETRY_HEADER,
28
31
  TRACING_HEADER,
29
- USER_OBJECT,
30
32
  USER_TRACING_HEADER,
31
33
  } from './lib/consts';
32
34
  import {
33
- CallbackFunction,
34
- ConsumeMessageOrNull,
35
- ConsumeOptions,
36
- CustomMessageHeaders,
37
- QueuesCache,
38
- RedisLockType,
39
- ExchangesCache,
35
+ type AssertExchangePromisesDictionary,
36
+ type CallbackFunction,
37
+ type ConnectionData,
38
+ type ConsumeMessageOrNull,
39
+ type ConsumeOptions,
40
40
  CONSUMER_DEFAULT_OPTIONS,
41
- QueueSetupPromisesDictionary,
42
- AssertExchangePromisesDictionary,
43
- ConnectionData,
41
+ type CustomMessageHeaders,
42
+ type ExchangesCache,
43
+ type Message,
44
+ type NackOptions,
45
+ type QueuesCache,
46
+ type QueueSetupPromisesDictionary,
44
47
  } from './lib/types';
45
48
 
46
- // const debug = nodeDebug('af-rabbitmq')
47
- const debug = logger.debug.bind(logger);
48
-
49
49
  const PUBLISH_TIMEOUT = 1000 * 10;
50
50
 
51
- // TODO: [QUORUM-PHASE-3] Delete this env var
51
+ // TODO: [QUORUM-PHASE-3] Delete these env vars
52
52
  const {
53
53
  DISABLE_QUORUM_QUEUES_CONSUME,
54
54
  DISABLE_QUORUM_QUEUES_PUBLISH,
55
- } = process.env;
55
+ } = env;
56
56
 
57
57
  export interface IAfRabbitMq {
58
- ack: any;
59
- nack: any;
60
- assertChannel: any;
61
- assertExchange: any;
62
- assertQueue: any;
63
- consume: any;
64
- consumeFromExchange: any;
65
- publish: any;
66
- sendToQueue: any;
67
- redisClient?: any;
68
- }
69
-
70
- interface NackOptions {
71
- skipRetry?: boolean;
58
+ ack(channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp?: boolean, releaseLock?: (() => Promise<void>) | null): (userMsg: ConsumeMessage) => Promise<void>;
59
+ nack(
60
+ channel: ConfirmChannel,
61
+ queue: string,
62
+ options: ConsumeOptions,
63
+ deadQueueOptions: Options.AssertQueue,
64
+ msg: ConsumeMessageOrNull,
65
+ releaseLock?: () => Promise<void>,
66
+ ): (userMsg: ConsumeMessageOrNull, nackOptions?: NackOptions) => Promise<void> ;
67
+ assertChannel(options?: assertChannelOpts): Promise<ChannelWrapper>;
68
+ assertExchange(exchangeName: string, connection: ConnectionData): Promise<Replies.AssertExchange>
69
+ assertQueue(queueName: string, options?: Options.AssertQueue | null): Promise<Replies.AssertQueue>;
70
+ consume(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<void>;
71
+ consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<void>
72
+ publish(exchange: string, content: any, customHeaders?: PublishOptions['headers']): Promise<void>
73
+ sendToQueue(queue: string, content: any, options?: Options.AssertQueue | null, customHeaders?: PublishOptions['headers']): Promise<boolean | undefined>;
74
+ redisClient?: ReturnType<typeof getRedisInstance>;
72
75
  }
73
76
 
74
77
  export interface AfRabbitOptions {
75
78
  disableReconnect?: boolean;
76
-
77
79
  /**
78
80
  * When you want your own grace-full shutdown, set this to true.
79
81
  * @default false
80
82
  */
81
83
  dontGracefulShutdown?: boolean;
82
-
83
84
  /**
84
- * dont retry on creation error
85
+ * don't retry on creation error
85
86
  * @default false
86
87
  */
87
88
  dontRetryAssert?: boolean;
88
-
89
89
  rabbitHost?: string;
90
-
91
90
  vhost?: string;
91
+ logger?: LoggerInstanceManager;
92
92
  }
93
93
 
94
94
  type newChannelOpts = {
@@ -113,13 +113,12 @@ type AfConsumer = {
113
113
  const HEARTBEAT = '60';
114
114
 
115
115
  class RabbitMq implements IAfRabbitMq {
116
- static parseMsg(msg: any) : any {
117
- let { content } = msg;
118
- content = content.toString();
116
+ static parseMsg(msg: ConsumeMessage): Message {
117
+ let content = msg.content.toString();
119
118
 
120
119
  try {
121
120
  content = JSON.parse(content);
122
- } catch (e) { }
121
+ } catch { /* ignore error */ }
123
122
 
124
123
  return {
125
124
  ...msg,
@@ -133,11 +132,10 @@ class RabbitMq implements IAfRabbitMq {
133
132
  }
134
133
  }
135
134
 
136
- static getPublishOptions(customHeaders: CustomMessageHeaders = {}) {
137
- const trace = getCurrentPayload();
138
- const user = trace?.context?.get(USER_OBJECT);
139
- const traceId = trace?.context?.get(TRACING_HEADER);
140
- const outbreakTrace = outbreak.getCurrentContext();
135
+ static getPublishOptions(customHeaders: CustomMessageHeaders = {}): PublishOptions {
136
+ const user = getUser();
137
+ const traceId = getCurrentPayload()?.context?.get(TRACING_HEADER) || outbreak.getCurrentContext()?.context?.get(TRACING_HEADER);
138
+
141
139
  return {
142
140
  timestamp: moment().unix(),
143
141
  timeout: PUBLISH_TIMEOUT,
@@ -145,113 +143,110 @@ class RabbitMq implements IAfRabbitMq {
145
143
  creationTimestamp: moment().valueOf(),
146
144
  ...customHeaders,
147
145
  [USER_TRACING_HEADER]: user?.id,
148
- [TRACING_HEADER]: traceId || outbreakTrace?.context?.get(TRACING_HEADER),
146
+ [TRACING_HEADER]: traceId,
149
147
  },
150
148
  };
151
149
  }
152
150
 
153
- DISCONNECT_MSG = 'rabbit: connection disconnect';
154
-
155
- RECONNECT_MSG = 'rabbit: connection disconnect - reconnecting';
151
+ readonly DISCONNECT_MSG = 'rabbit: connection disconnect';
156
152
 
157
- publishChannel: ChannelWrapper | null;
153
+ readonly RECONNECT_MSG = 'rabbit: connection disconnect - reconnecting';
158
154
 
159
- publishChannelSetupPromise: Promise<ChannelWrapper> | null;
155
+ publishChannel: ChannelWrapper | null = null;
160
156
 
161
- blockReconnect: boolean | null | undefined
157
+ publishChannelSetupPromise: Promise<ChannelWrapper> | null = null;
162
158
 
163
- publishConnection: ConnectionData
164
-
165
- consumeConnection: ConnectionData
166
-
167
- em: EventEmitter;
159
+ readonly publishConnection: ConnectionData = {
160
+ amqpConnection: null,
161
+ creatingConnection: false,
162
+ connectionCreatedEventName: 'publishConnectionCreated',
163
+ connectionFailedEventName: 'publishConnectionFailed',
164
+ blockReconnect: false,
165
+ };
168
166
 
169
- creatingConnection: boolean;
167
+ readonly consumeConnection: ConnectionData = {
168
+ amqpConnection: null,
169
+ creatingConnection: false,
170
+ connectionCreatedEventName: 'consumeConnectionCreated',
171
+ connectionFailedEventName: 'consumeConnectionFailed',
172
+ blockReconnect: false,
173
+ };
170
174
 
171
- exchanges: ExchangesCache;
175
+ readonly em: EventEmitter = new EventEmitter();
172
176
 
173
- queues: QueuesCache;
177
+ readonly exchanges: ExchangesCache = {};
174
178
 
175
- queueSetupPromises: QueueSetupPromisesDictionary;
179
+ readonly queues: QueuesCache = {};
176
180
 
177
- assertExchangePromises: AssertExchangePromisesDictionary;
181
+ readonly queueSetupPromises: QueueSetupPromisesDictionary = {};
178
182
 
179
- options: AfRabbitOptions;
183
+ readonly assertExchangePromises: AssertExchangePromisesDictionary = {};
180
184
 
181
- redisClient: any;
185
+ readonly redisClient?: ReturnType<typeof getRedisInstance>;
182
186
 
183
- redisLock?: RedisLockType;
187
+ readonly redisLock?: ReturnType<typeof RedisLock>;
184
188
 
185
189
  /** Array of consumers tags used for canceling consumption */
186
- consumersTags: Map<string, { channel: ConfirmChannel; consumerTag: string }>;
190
+ readonly consumersTags: Map<string, { channel: ConfirmChannel; consumerTag: string }> = new Map<string, { channel: ConfirmChannel; consumerTag: string }>();
187
191
 
188
- private consumersToRegister: Array<AfConsumer> = [];
192
+ private readonly consumersToRegister: Array<AfConsumer> = [];
189
193
 
190
194
  private doesVHostExist = false;
191
195
 
192
- private vhost = 'quorum-vhost';
196
+ private readonly vhost = 'quorum-vhost';
193
197
 
194
198
  private gracefulShutdownStarted = false;
195
199
 
196
200
  // TODO:[QUORUM-PHASE-3] Delete the old properties that we use for the old consumers and publishers
197
- oldPublishChannel: ChannelWrapper | null;
201
+ oldPublishChannel: ChannelWrapper | null = null;
198
202
 
199
- oldPublishChannelSetupPromise: Promise<ChannelWrapper> | null;
203
+ oldPublishChannelSetupPromise: Promise<ChannelWrapper> | null = null;
200
204
 
201
- oldBlockReconnect: boolean | null | undefined
205
+ readonly oldPublishConnection: ConnectionData = {
206
+ amqpConnection: null,
207
+ creatingConnection: false,
208
+ connectionCreatedEventName: 'oldPublishConnectionCreated',
209
+ connectionFailedEventName: 'oldPublishConnectionFailed',
210
+ blockReconnect: false,
211
+ };
202
212
 
203
- oldPublishConnection: ConnectionData
213
+ readonly oldConsumeConnection: ConnectionData = {
214
+ amqpConnection: null,
215
+ creatingConnection: false,
216
+ connectionCreatedEventName: 'oldConsumeConnectionCreated',
217
+ connectionFailedEventName: 'oldConsumeConnectionFailed',
218
+ blockReconnect: false,
219
+ };
204
220
 
205
- oldConsumeConnection: ConnectionData
221
+ readonly oldEm: EventEmitter = new EventEmitter();
206
222
 
207
- oldEm: EventEmitter;
223
+ readonly oldExchanges: ExchangesCache = {};
208
224
 
209
- oldCreatingConnection: boolean;
225
+ readonly oldQueues: QueuesCache = {};
210
226
 
211
- oldExchanges: ExchangesCache;
227
+ readonly oldQueueSetupPromises: QueueSetupPromisesDictionary = {};
212
228
 
213
- oldQueues: QueuesCache;
229
+ readonly oldAssertExchangePromises: AssertExchangePromisesDictionary = {};
214
230
 
215
- oldQueueSetupPromises: QueueSetupPromisesDictionary;
231
+ readonly oldConsumersTags: Map<string, { channel: ConfirmChannel; consumerTag: string }> = new Map<string, { channel: ConfirmChannel; consumerTag: string }>();
216
232
 
217
- oldAssertExchangePromises: AssertExchangePromisesDictionary;
233
+ private readonly oldConsumersToRegister: Array<AfConsumer> = [];
218
234
 
219
- oldConsumersTags: Map<string, { channel: ConfirmChannel; consumerTag: string }>;
235
+ #logger: LoggerInstanceManager;
220
236
 
221
- private oldConsumersToRegister: Array<AfConsumer> = [];
237
+ constructor(public readonly options: AfRabbitOptions = {}, private readonly redisConfig?: RedisConfig) {
238
+ this.#logger = options?.logger || logger;
222
239
 
223
- constructor(options: AfRabbitOptions = {}, redisConfig?: RedisConfig) {
224
- this.em = new EventEmitter();
225
- this.publishChannel = null;
226
- this.publishChannelSetupPromise = null;
227
- this.publishConnection = {
228
- amqpConnection: null,
229
- creatingConnection: false,
230
- connectionCreatedEventName: 'publishConnectionCreated',
231
- connectionFailedEventName: 'publishConnectionFailed',
232
- blockReconnect: false,
233
- };
234
- this.consumeConnection = {
235
- amqpConnection: null,
236
- creatingConnection: false,
237
- connectionCreatedEventName: 'consumeConnectionCreated',
238
- connectionFailedEventName: 'consumeConnectionFailed',
239
- blockReconnect: false,
240
- };
241
- this.creatingConnection = false;
242
- this.exchanges = {};
243
- this.queues = {};
244
- this.queueSetupPromises = {};
245
- this.assertExchangePromises = {};
246
- this.consumersToRegister = [];
247
- this.options = options;
248
-
249
- this.redisClient = redisConfig && getRedisInstance(redisConfig);
250
- if (this.redisClient) {
251
- this.redisLock = promisify(RedisLock(this.redisClient)) as RedisLockType;
240
+ if (redisConfig) {
241
+ this.redisClient = getRedisInstance(redisConfig).on('error', (err) => {
242
+ this.#logger.error('rabbit: Redis error', { err, redisConfig });
243
+ });
244
+ this.redisClient.connect().catch((err) => {
245
+ this.#logger.error('rabbit: Failed to connect to Redis', { err, redisConfig });
246
+ });
247
+ this.redisLock = RedisLock(this.redisClient);
252
248
  }
253
- this.consumersTags = new Map<string, { channel: ConfirmChannel; consumerTag: string }>();
254
- logger.info(`rabbit: [gracefully-shutdown] adding gracefully shutdown for process.pid ${process.pid}`);
249
+ this.#logger.info(`rabbit: [gracefully-shutdown] adding gracefully shutdown for process.pid ${process.pid}`);
255
250
  if (!this.options?.dontGracefulShutdown) {
256
251
  process.on('SIGTERM', async () => {
257
252
  await this.gracefulShutdown('SIGTERM');
@@ -260,32 +255,10 @@ class RabbitMq implements IAfRabbitMq {
260
255
  await this.gracefulShutdown('SIGINT');
261
256
  });
262
257
  }
258
+ }
263
259
 
264
- // TODO: [QUORUM-PHASE-3] Delete the old properties that we use for the old consumers and publishers
265
- this.oldEm = new EventEmitter();
266
- this.oldPublishChannel = null;
267
- this.oldPublishChannelSetupPromise = null;
268
- this.oldPublishConnection = {
269
- amqpConnection: null,
270
- creatingConnection: false,
271
- connectionCreatedEventName: 'oldPublishConnectionCreated',
272
- connectionFailedEventName: 'oldPublishConnectionFailed',
273
- blockReconnect: false,
274
- };
275
- this.oldConsumeConnection = {
276
- amqpConnection: null,
277
- creatingConnection: false,
278
- connectionCreatedEventName: 'oldConsumeConnectionCreated',
279
- connectionFailedEventName: 'oldConsumeConnectionFailed',
280
- blockReconnect: false,
281
- };
282
- this.oldCreatingConnection = false;
283
- this.oldExchanges = {};
284
- this.oldQueues = {};
285
- this.oldQueueSetupPromises = {};
286
- this.oldAssertExchangePromises = {};
287
- this.oldConsumersToRegister = [];
288
- this.oldConsumersTags = new Map<string, { channel: ConfirmChannel; consumerTag: string }>();
260
+ private getRedisKey(key: string): string {
261
+ return `${this.redisConfig?.prefix || ''}${key}`;
289
262
  }
290
263
 
291
264
  private assertVHost = async () => {
@@ -313,12 +286,12 @@ class RabbitMq implements IAfRabbitMq {
313
286
 
314
287
  if (response.status === 200) {
315
288
  this.doesVHostExist = true;
316
- logger.info('Vhost exists', { vhost: this.vhost });
289
+ this.#logger.info('Vhost exists', { vhost: this.vhost });
317
290
  return;
318
291
  }
319
292
 
320
293
  if (response.status !== 404) {
321
- logger.error('Failed to check vhost', { response });
294
+ this.#logger.error('Failed to check vhost', { response });
322
295
  throw new RabbitError('Failed to check vhost');
323
296
  }
324
297
 
@@ -329,25 +302,26 @@ class RabbitMq implements IAfRabbitMq {
329
302
  });
330
303
 
331
304
  if (!createResponse.ok) {
332
- logger.error('Failed to create vhost', { response: createResponse });
305
+ this.#logger.error('Failed to create vhost', { response: createResponse });
333
306
  throw new RabbitError('Failed to create vhost');
334
307
  }
335
308
 
336
309
  this.doesVHostExist = true;
337
- logger.info('Vhost created', { vhost: this.vhost });
310
+ this.#logger.info('Vhost created', { vhost: this.vhost });
338
311
  } catch (error) {
339
- logger.error('Failed to check or create vhost', { error });
312
+ this.#logger.error('Failed to check or create vhost', { error });
340
313
  throw error;
341
314
  }
342
315
  };
343
316
 
344
317
  private shouldConsumeMessageByTimestamp = async (msg: ConsumeMessageOrNull) => {
345
318
  if (msg) {
346
- const { properties: { headers } } = msg;
319
+ const { headers } = msg.properties;
347
320
  const timestamp = headers?.creationTimestamp;
348
321
 
349
322
  if (timestamp && headers?.redisTimestampValidationKey && this.redisClient) {
350
- const lastMessageTimestamp = await this.redisClient.getAsync(headers.redisTimestampValidationKey);
323
+ const key = this.getRedisKey(headers.redisTimestampValidationKey);
324
+ const lastMessageTimestamp = await this.redisClient.get(key);
351
325
  return !lastMessageTimestamp || (parseInt(lastMessageTimestamp, 10) <= parseInt(timestamp, 10));
352
326
  }
353
327
  return true;
@@ -355,16 +329,22 @@ class RabbitMq implements IAfRabbitMq {
355
329
  return false;
356
330
  }
357
331
 
358
- public ack = (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp = false, releaseLock = null) => async (userMsg: ConsumeMessage) : Promise<any> => {
332
+ public ack = (
333
+ channel: ConfirmChannel,
334
+ msg: ConsumeMessageOrNull,
335
+ shouldUpdateRedisTimestamp = false,
336
+ releaseLock: (() => Promise<void>) | null = null,
337
+ ) => async (userMsg: ConsumeMessage) : Promise<void> => { // eslint-disable-line @typescript-eslint/no-unused-vars
359
338
  if (msg) {
360
- debug('rabbit acking message', { deliveryTag: msg.fields.deliveryTag });
339
+ this.#logger.debug('rabbit acking message', { deliveryTag: msg.fields.deliveryTag });
361
340
  await channel.ack(msg);
362
- const { properties: { headers } } = msg;
341
+ const { headers } = msg.properties;
363
342
  const timestamp = headers?.creationTimestamp;
364
343
 
365
344
  if (shouldUpdateRedisTimestamp && timestamp && headers?.redisTimestampValidationKey && this.redisClient) {
366
345
  const parsedTimestamp = parseInt(timestamp, 10);
367
- await this.redisClient.setAsync(headers.redisTimestampValidationKey, parsedTimestamp, 'EX', 3600);
346
+ const key = this.getRedisKey(headers.redisTimestampValidationKey);
347
+ await this.redisClient.set(key, parsedTimestamp, { EX: 3600 });
368
348
  await this.unlockRedisIfNeeded(releaseLock);
369
349
  }
370
350
  }
@@ -373,142 +353,123 @@ class RabbitMq implements IAfRabbitMq {
373
353
  public nack = (
374
354
  channel: ConfirmChannel,
375
355
  queue: string,
376
- options: any,
356
+ options: ConsumeOptions,
377
357
  deadQueueOptions: Options.AssertQueue,
378
358
  msg: ConsumeMessageOrNull,
379
- releaseLock: any,
359
+ releaseLock?: (() => Promise<void>) | null,
380
360
  ) => async (
381
361
  userMsg: ConsumeMessageOrNull,
382
362
  {
383
363
  skipRetry = false,
384
364
  }: NackOptions = { },
385
- ) : Promise<any> => {
365
+ ): Promise<void> => {
386
366
  await this.unlockRedisIfNeeded(releaseLock);
387
- if (channel && msg) {
388
- if (
389
- !skipRetry
390
- && (
391
- !msg.properties.headers?.[RETRY_HEADER]
392
- || parseInt(msg.properties.headers[RETRY_HEADER], 10) < options.retries
393
- )
394
- ) {
395
- await this.sendToQueue(queue, RabbitMq.parseMsg(msg).content, options, {
396
- ...msg.properties.headers,
397
- [RETRY_HEADER]: msg.properties.headers?.[RETRY_HEADER]
398
- ? msg.properties.headers[RETRY_HEADER] + 1
399
- : 1,
400
- });
401
- } else {
402
- const deadQueue = `${queue}-dead`;
403
- await this.sendToQueue(deadQueue, RabbitMq.parseMsg(msg).content, deadQueueOptions, {
404
- ...msg.properties.headers,
405
- [RETRY_HEADER]: msg.properties.headers?.[RETRY_HEADER]
406
- ? msg.properties.headers[RETRY_HEADER] + 1
407
- : 1,
408
- });
409
- }
410
- debug('rabbit nacking message', { deliveryTag: msg.fields.deliveryTag });
411
- await channel.ack(msg);
412
- } else {
413
- logger.error('no channel or msg', {
414
- msg,
415
- });
367
+ if (!channel || !msg) {
368
+ this.#logger.error('no channel or msg', { msg });
369
+ return;
416
370
  }
371
+ const currentRetryHeader = Number.parseInt(msg.properties.headers?.[RETRY_HEADER] || '0', 10) || 0;
372
+ const sendToDeadQueue = skipRetry || currentRetryHeader >= options.retries!;
373
+ await this.sendToQueue(`${queue}${sendToDeadQueue ? '-dead' : ''}`, RabbitMq.parseMsg(msg).content, sendToDeadQueue ? deadQueueOptions : options, {
374
+ ...msg.properties.headers,
375
+ [RETRY_HEADER]: currentRetryHeader + 1,
376
+ });
377
+ this.#logger.debug('rabbit nacking message', { deliveryTag: msg.fields.deliveryTag });
378
+ await channel.ack(msg);
417
379
  }
418
380
 
419
- async getConnection(connection: ConnectionData) {
420
- return new Promise<AmqpConnectionManager>(async (resolve, reject) => {
421
- const {
422
- amqpConnection: connectionLocal,
423
- creatingConnection,
424
- connectionCreatedEventName,
425
- connectionFailedEventName,
426
- blockReconnect,
427
- } = connection;
428
-
429
- if (blockReconnect) {
430
- debug('rabbit: block reconnect');
431
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
432
- // @ts-ignore
433
- return resolve();
434
- }
435
- if (connectionLocal !== null) {
436
- if (this.options?.disableReconnect || connectionLocal?.isConnected()) {
437
- debug('rabbit: connection - is connected');
438
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
439
- // @ts-ignore
440
- return resolve(connectionLocal);
441
- }
442
- debug('rabbit: connection - reconnecting');
381
+ async getConnection(connection: ConnectionData): Promise<AmqpConnectionManager> {
382
+ const {
383
+ amqpConnection: connectionLocal,
384
+ creatingConnection,
385
+ connectionCreatedEventName,
386
+ connectionFailedEventName,
387
+ blockReconnect,
388
+ } = connection;
389
+
390
+ if (blockReconnect) {
391
+ this.#logger.debug('rabbit: block reconnect');
392
+ // @ts-expect-error we are returning undefined while the function clearly expects a connection.
393
+ return undefined;
394
+ }
395
+
396
+ if (connectionLocal !== null) {
397
+ if (this.options?.disableReconnect || connectionLocal?.isConnected()) {
398
+ this.#logger.debug('rabbit: connection - is connected');
399
+ return connectionLocal;
443
400
  }
444
- if (creatingConnection) {
445
- debug('rabbit: creating connection emi');
446
- this.em.once(connectionCreatedEventName, resolve);
447
- this.em.once(connectionFailedEventName, reject);
448
- return;
401
+ this.#logger.debug('rabbit: connection - reconnecting');
402
+ }
403
+ if (creatingConnection) {
404
+ this.#logger.debug('rabbit: creating connection emi');
405
+ const [event, value] = await Promise.race([connectionCreatedEventName, connectionFailedEventName].map((e) => once(this.em, e).then(([result]) => [e, result] as const)));
406
+ if (event === connectionCreatedEventName) {
407
+ return value;
449
408
  }
450
- connection.creatingConnection = true;
451
- let isResolved = false;
452
-
453
- // It is import to use it as a function and not as a variable
454
- // because of k8s changes the env variables
455
- // and we want to use the new values
456
- const findServers = () => {
457
- const userName = process.env.RABBITMQ_USERNAME || 'guest';
458
- const password = process.env.RABBITMQ_PASSWORD || 'guest';
459
- const host = this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost';
460
-
461
- debug('rabbit: creating connection', { host, userName, HEARTBEAT });
462
- return [`amqp://${userName}:${password}@${host}/${this.vhost}?heartbeat=${HEARTBEAT}`];
463
- };
464
-
465
- const defaultUrls = findServers();
466
- const newConnection: AmqpConnectionManager = await connect(defaultUrls, {
467
- findServers,
468
- });
409
+ throw value;
410
+ }
411
+ connection.creatingConnection = true;
412
+ let isResolved = false;
413
+
414
+ // It is import to use it as a function and not as a variable
415
+ // because of k8s changes the env variables
416
+ // and we want to use the new values
417
+ const findServers = () => {
418
+ const userName = process.env.RABBITMQ_USERNAME || 'guest';
419
+ const password = process.env.RABBITMQ_PASSWORD || 'guest';
420
+ const host = this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost';
421
+
422
+ this.#logger.debug('rabbit: creating connection', { host, userName, HEARTBEAT });
423
+ return [`amqp://${userName}:${password}@${host}/${this.vhost}?heartbeat=${HEARTBEAT}`];
424
+ };
469
425
 
470
- connection.amqpConnection = newConnection;
471
- newConnection.on('error', (err) => {
472
- logger.error('rabbit: connection error', { err });
473
- if (!isResolved) {
474
- isResolved = true;
475
- reject(err);
476
- this.em.emit(connectionFailedEventName, err);
477
- }
478
- });
426
+ const defaultUrls = findServers();
427
+ const newConnection: AmqpConnectionManager = connect(defaultUrls, { findServers });
428
+ connection.amqpConnection = newConnection;
479
429
 
480
- newConnection.on('connectFailed', (err) => {
481
- this.consumersTags.clear();
482
- if (typeof err.url === 'string') {
483
- err.url = this.maskURL(err.url);
484
- }
485
- logger.error('rabbit: connection connectFailed', { err, advice: 'Check if the vhost exist', vhost: this.vhost });
486
- if (!isResolved) {
487
- isResolved = true;
488
- reject(err);
489
- this.em.emit(connectionFailedEventName, err);
490
- }
491
- });
492
-
493
- newConnection.on('disconnect', ({ err }) => {
494
- this.consumersTags.clear();
495
- debug('rabbit: connection closed');
496
- if (this.options?.disableReconnect) {
497
- logger.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
498
- connection.blockReconnect = true;
499
- } else {
500
- logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
501
- }
502
- });
430
+ const { promise, reject, resolve } = createDeferredPromise<AmqpConnectionManager>();
431
+ newConnection.on('error', (err) => {
432
+ this.#logger.error('rabbit: connection error', { err });
433
+ if (!isResolved) {
434
+ isResolved = true;
435
+ reject(err);
436
+ this.em.emit(connectionFailedEventName, err);
437
+ }
438
+ });
503
439
 
504
- newConnection.once('connect', async () => {
505
- debug('rabbit: connection established');
506
- connection.creatingConnection = false;
507
- this.em.emit(connectionCreatedEventName, newConnection);
440
+ newConnection.on('connectFailed', (err) => {
441
+ this.consumersTags.clear();
442
+ if (typeof err.url === 'string') {
443
+ err.url = this.maskURL(err.url);
444
+ }
445
+ this.#logger.error('rabbit: connection connectFailed', { err, advice: 'Check if the vhost exist', vhost: this.vhost });
446
+ if (!isResolved) {
508
447
  isResolved = true;
509
- resolve(newConnection);
510
- });
448
+ reject(err);
449
+ this.em.emit(connectionFailedEventName, err);
450
+ }
451
+ });
452
+
453
+ newConnection.on('disconnect', ({ err }) => {
454
+ this.consumersTags.clear();
455
+ this.#logger.debug('rabbit: connection closed');
456
+ if (this.options?.disableReconnect) {
457
+ this.#logger.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
458
+ connection.blockReconnect = true;
459
+ } else {
460
+ this.#logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
461
+ }
462
+ });
463
+
464
+ newConnection.once('connect', async () => {
465
+ this.#logger.debug('rabbit: connection established');
466
+ connection.creatingConnection = false;
467
+ this.em.emit(connectionCreatedEventName, newConnection);
468
+ isResolved = true;
469
+ resolve(newConnection);
511
470
  });
471
+
472
+ return promise;
512
473
  }
513
474
 
514
475
  async getNewChannel({
@@ -516,90 +477,92 @@ class RabbitMq implements IAfRabbitMq {
516
477
  onClose = null,
517
478
  options = {},
518
479
  connection,
519
- }: newChannelOpts) {
480
+ }: newChannelOpts): Promise<ChannelWrapper> {
520
481
  let localConnection!: AmqpConnectionManager;
521
482
  try {
522
483
  localConnection = await this.getConnection(connection);
523
484
  } catch (e) {
524
- logger.error(`rabbit: error on get connection for new channel ${name} `, { e });
485
+ this.#logger.error(`rabbit: error on get connection for new channel ${name} `, { e });
525
486
  throw e;
526
487
  }
527
488
  const channel = localConnection?.createChannel({ ...options });
528
489
  once(channel, 'close').then((args) => {
529
- logger.error(`rabbit: channel ${name} closed`);
490
+ this.#logger.error(`rabbit: channel ${name} closed`);
530
491
  onClose?.(args);
531
492
  });
532
493
  try {
533
494
  await once(channel, 'connect');
534
- debug(`rabbit: channel ${name} CONNECTED`);
495
+ this.#logger.debug(`rabbit: channel ${name} CONNECTED`);
535
496
  return channel;
536
497
  } catch (err) {
537
- logger.error(`rabbit: channel error ${name} error`, { err });
498
+ this.#logger.error(`rabbit: channel error ${name} error`, { err });
538
499
  throw err;
539
500
  }
540
501
  }
541
502
 
542
503
  async assertChannel({ force = false, connection }: assertChannelOpts): Promise<ChannelWrapper> {
543
- if (!this.publishChannelSetupPromise) {
544
- this.publishChannelSetupPromise = new Promise<ChannelWrapper>(async (resolve, reject) => {
545
- if (this.publishChannel && !force) {
546
- return resolve(this.publishChannel);
547
- }
504
+ if (this.publishChannelSetupPromise) {
505
+ return this.publishChannelSetupPromise;
506
+ }
507
+ const { promise, resolve, reject } = createDeferredPromise<ChannelWrapper>();
508
+ this.publishChannelSetupPromise = promise;
509
+ if (this.publishChannel && !force) {
510
+ resolve(this.publishChannel);
511
+ return promise;
512
+ }
548
513
 
549
- try {
550
- const channel = await this.getNewChannel({ connection });
551
- channel.on('error', (err) => {
552
- logger.error('rabbit: channel error', { err });
553
- });
554
- if (this.publishConnection === connection) {
555
- this.publishChannel = channel;
556
- }
557
- resolve(channel);
558
- } catch (e) {
559
- reject(e);
560
- }
514
+ try {
515
+ const channel = await this.getNewChannel({ connection });
516
+ channel.on('error', (err) => {
517
+ this.#logger.error('rabbit: channel error', { err });
561
518
  });
519
+ if (this.publishConnection === connection) {
520
+ this.publishChannel = channel;
521
+ }
522
+ resolve(channel);
523
+ } catch (e) {
524
+ reject(e);
562
525
  }
563
- return this.publishChannelSetupPromise;
526
+ return promise;
564
527
  }
565
528
 
566
- async assertExchange(exchangeName: string, connection: ConnectionData) {
529
+ async assertExchange(exchangeName: string, connection: ConnectionData): Promise<Replies.AssertExchange> {
567
530
  const channel: ChannelWrapper = await this.assertChannel({ connection });
568
531
 
569
532
  if (this.exchanges[exchangeName]) {
570
533
  delete this.assertExchangePromises[exchangeName];
571
- return this.exchanges[exchangeName];
534
+ return this.exchanges[exchangeName]!;
572
535
  }
573
536
 
574
537
  if (this.assertExchangePromises[exchangeName]) {
575
- return this.assertExchangePromises[exchangeName];
538
+ return this.assertExchangePromises[exchangeName]!;
576
539
  }
577
540
 
578
541
  this.assertExchangePromises[exchangeName] = assertExchangeFanout(channel, exchangeName);
579
542
  this.exchanges[exchangeName] = await this.assertExchangePromises[exchangeName];
580
- return this.exchanges[exchangeName];
543
+ return this.exchanges[exchangeName]!;
581
544
  }
582
545
 
583
- async getQueueLength(queue: string) {
546
+ async getQueueLength(queue: string): Promise<Replies.AssertQueue> {
584
547
  RabbitMq.validateName('queue', queue);
585
548
  const { publishChannel: channel } = this;
586
549
  if (!channel) {
587
550
  throw new RabbitError('channel is not defined');
588
551
  }
589
- debug('rabbit: getting queue length', { queue, connected: this.publishConnection.amqpConnection?.isConnected() });
552
+ this.#logger.debug('rabbit: getting queue length', { queue, connected: this.publishConnection.amqpConnection?.isConnected() });
590
553
  return channel?.checkQueue(queue);
591
554
  }
592
555
 
593
- private async deleteQueue(queue: string, connection: ConnectionData) {
556
+ private async deleteQueue(queue: string, connection: ConnectionData): Promise<Replies.DeleteQueue> {
594
557
  RabbitMq.validateName('queue', queue);
595
558
  const channel: ChannelWrapper = await this.assertChannel({ connection });
596
- logger.info('rabbit: deleting queue', { queue });
559
+ this.#logger.info('rabbit: deleting queue', { queue });
597
560
  const deleteQueueRes = await channel.deleteQueue(queue);
598
- debug('queue deleted', deleteQueueRes);
561
+ this.#logger.debug('queue deleted', deleteQueueRes);
599
562
  return deleteQueueRes;
600
563
  }
601
564
 
602
- async bindQueue(queue: string, exchange: string) {
565
+ async bindQueue(queue: string, exchange: string): Promise<void> {
603
566
  const channel: ChannelWrapper = await this.assertChannel({ connection: this.publishConnection });
604
567
  await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.bindQueue(queue, exchange, ''));
605
568
  return channel.bindQueue(queue, exchange, '');
@@ -619,22 +582,22 @@ class RabbitMq implements IAfRabbitMq {
619
582
  };
620
583
  try {
621
584
  const channel: ChannelWrapper = await this.assertChannel({ connection });
622
- debug('assertQueue->channel.addSetup', { queueName });
585
+ this.#logger.debug('assertQueue->channel.addSetup', { queueName });
623
586
  await channel.addSetup(async (setupChannel: ConfirmChannel) => {
624
587
  await setupChannel.assertQueue(queueName, localeOptions);
625
588
  });
626
- debug('assertQueue->channel.assertQueue', { queueName });
589
+ this.#logger.debug('assertQueue->channel.assertQueue', { queueName });
627
590
  queue = await channel.assertQueue(queueName, localeOptions);
628
591
  } catch (e) {
629
- logger.error('rabbit: assertQueue error', { queueName, options, error: e });
592
+ this.#logger.error('rabbit: assertQueue error', { queueName, options, error: e });
630
593
  if (!this.options?.dontRetryAssert) {
631
- debug('retrying assertQueue', { queueName });
594
+ this.#logger.debug('retrying assertQueue', { queueName });
632
595
  const channel = await this.assertChannel({ force: true, connection });
633
596
  await this.deleteQueue(queueName, connection);
634
597
 
635
- debug('retrying assertQueue->channel.addSetup', { queueName });
598
+ this.#logger.debug('retrying assertQueue->channel.addSetup', { queueName });
636
599
  await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
637
- debug('retrying assertQueue->channel.assertQueue', { queueName });
600
+ this.#logger.debug('retrying assertQueue->channel.assertQueue', { queueName });
638
601
  queue = await channel.assertQueue(queueName, localeOptions);
639
602
  } else {
640
603
  throw e;
@@ -661,25 +624,21 @@ class RabbitMq implements IAfRabbitMq {
661
624
  return false;
662
625
  }
663
626
 
664
- async assertQueue(queueName: string, options?: Options.AssertQueue): Promise<any> {
627
+ async assertQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
665
628
  RabbitMq.validateName('queue', queueName);
666
629
  if (this.queues[queueName]) {
667
630
  delete this.queueSetupPromises[queueName];
668
- return this.queues[queueName];
631
+ return this.queues[queueName]!;
669
632
  }
670
633
 
671
- if (this.queueSetupPromises[queueName]) {
672
- return this.queueSetupPromises[queueName];
673
- }
674
-
675
- this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
676
- return this.queueSetupPromises[queueName];
634
+ this.queueSetupPromises[queueName] ||= this.setupQueue(queueName, options);
635
+ return this.queueSetupPromises[queueName]!;
677
636
  }
678
637
 
679
- private saveConsumer(queue: string, callback: CallbackFunction, options: ConsumeOptions | undefined) {
638
+ private saveConsumer(queue: string, callback: CallbackFunction, options: ConsumeOptions | undefined): void {
680
639
  const isConsumerExist: boolean = this.consumersToRegister.some((consumer) => consumer.queue === queue);
681
640
  if (!isConsumerExist) {
682
- logger.info(`rabbit: consumer: ${queue} saved in consumer array`);
641
+ this.#logger.info(`rabbit: consumer: ${queue} saved in consumer array`);
683
642
  this.consumersToRegister.push({
684
643
  queue,
685
644
  callback,
@@ -689,7 +648,7 @@ class RabbitMq implements IAfRabbitMq {
689
648
  }
690
649
 
691
650
  // Used by the microservices to consume messages from the queue
692
- async consume(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
651
+ async consume(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<void> {
693
652
  this.saveConsumerOld(queue, callback, options);
694
653
 
695
654
  // TODO: [QUORUM-PHASE-3] Use only the implementation of consumeNew and delete consumeNew and consumeOld
@@ -703,16 +662,16 @@ class RabbitMq implements IAfRabbitMq {
703
662
  }
704
663
 
705
664
  // TODO: [QUORUM-PHASE-3] Delete consumeNew we do not use it anymore
706
- async consumeNew(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
665
+ async consumeNew(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<void> {
707
666
  await this.consumeFromRabbit(queue, callback, options);
708
667
  }
709
668
 
710
669
  // TODO: [QUORUM-PHASE-3] Delete consumeOld we do not use it anymore
711
- async consumeOld(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
670
+ async consumeOld(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<void> {
712
671
  await this.consumeFromRabbitOld(queue, callback, options);
713
672
  }
714
673
 
715
- private async lockRedisIfNeeded(msg: any, options: any) {
674
+ private async lockRedisIfNeeded(msg: ReturnType<typeof RabbitMq.parseMsg>, options: ConsumeOptions): Promise<(() => Promise<void>) | null> {
716
675
  const { properties: { headers } } = msg;
717
676
  const timestamp = headers?.creationTimestamp;
718
677
  let releaseLock = null;
@@ -726,13 +685,13 @@ class RabbitMq implements IAfRabbitMq {
726
685
  return releaseLock;
727
686
  }
728
687
 
729
- private async unlockRedisIfNeeded(releaseLock: any) {
688
+ private async unlockRedisIfNeeded(releaseLock?: (() => Promise<void>) | null): Promise<void> {
730
689
  if (this.redisLock && releaseLock) {
731
690
  await releaseLock();
732
691
  }
733
692
  }
734
693
 
735
- private async consumeFromRabbit(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
694
+ private async consumeFromRabbit(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<void> {
736
695
  const optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options };
737
696
  RabbitMq.validateName('queue', queue);
738
697
  const uniqueId = randomUUID();
@@ -743,11 +702,11 @@ class RabbitMq implements IAfRabbitMq {
743
702
  if (!this.redisLock) {
744
703
  throw new RabbitError('Usage of consumeWithLock requires RedisInstance');
745
704
  }
746
- logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
705
+ this.#logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
747
706
  }
748
707
  const channel = await this.getNewChannel({ connection: this.consumeConnection });
749
708
  return channel.addSetup(async (confirmChannel: ConfirmChannel) => {
750
- const q = await this.assertQueue(queue, optionsWithDefaults);
709
+ await this.assertQueue(queue, optionsWithDefaults);
751
710
  await confirmChannel.prefetch(limit, false);
752
711
  const { consumerTag } = await confirmChannel.consume(
753
712
  queue,
@@ -756,9 +715,7 @@ class RabbitMq implements IAfRabbitMq {
756
715
  return null;
757
716
  }
758
717
 
759
- const traceId = msg.properties.headers![TRACING_HEADER];
760
- const userId = msg.properties.headers![USER_TRACING_HEADER];
761
- const automationId = msg.properties.headers![AUTOMATION_ID_HEADER];
718
+ const { [TRACING_HEADER]: traceId, [USER_TRACING_HEADER]: userId, [AUTOMATION_ID_HEADER]: automationId } = msg.properties.headers ?? {};
762
719
  const parsedMessage = RabbitMq.parseMsg(msg);
763
720
  const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
764
721
  const trace = newTrace(traceTypes.RABBIT);
@@ -773,13 +730,13 @@ class RabbitMq implements IAfRabbitMq {
773
730
  createOrSetRabbitTrace(outbreakTrace, userId),
774
731
  ]);
775
732
  } catch (e) {
776
- logger.error('rabbit: failed to setRabbitTrace', { userId, e });
733
+ this.#logger.error('rabbit: failed to setRabbitTrace', { userId, e });
777
734
  return this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg);
778
735
  }
779
736
  }
780
737
 
781
738
  if (traceId) {
782
- (trace as any)?.context?.set(TRACING_HEADER, traceId);
739
+ trace?.context?.set(TRACING_HEADER, traceId);
783
740
  (outbreakTrace as any)?.context.set(TRACING_HEADER, traceId);
784
741
  }
785
742
 
@@ -803,44 +760,44 @@ class RabbitMq implements IAfRabbitMq {
803
760
  return;
804
761
  }
805
762
  messageAcked = true;
806
- return this.ack(confirmChannel, msg, true, releaseLock)(msg);
763
+ await this.ack(confirmChannel, msg, true, releaseLock)(msg);
807
764
  };
808
765
 
809
766
  const localNack = async (_: ConsumeMessageOrNull, nackOptions: NackOptions = {}) => {
810
767
  if (messageAcked) {
811
768
  return;
812
769
  }
813
- debug('rabbit localNack', { messageAcked, uniqueId, deliveryTag: msg.fields.deliveryTag });
770
+ this.#logger.debug('rabbit localNack', { messageAcked, uniqueId, deliveryTag: msg.fields.deliveryTag });
814
771
  messageAcked = true;
815
- return this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg, nackOptions);
772
+ await this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg, nackOptions);
816
773
  };
817
774
 
818
775
  try {
819
- await callback(
776
+ return await callback(
820
777
  parsedMessage,
821
778
  localAck,
822
779
  localNack,
823
780
  );
824
781
  } catch (e) {
825
- await localNack(msg);
782
+ return localNack(msg);
826
783
  }
827
784
  }, CONSUMER_DEFAULT_OPTIONS,
828
785
  );
829
786
  if (!consumerTag) {
830
- logger.error(`rabbit: failed to consume from queue ${queue}`);
787
+ this.#logger.error(`rabbit: failed to consume from queue ${queue}`);
831
788
  } else {
832
- logger.info(`rabbit: adding tag ${consumerTag} to the array.`);
789
+ this.#logger.info(`rabbit: adding tag ${consumerTag} to the array.`);
833
790
  this.consumersTags.set(queue, { channel: confirmChannel, consumerTag });
834
791
  }
835
792
  });
836
793
  }
837
794
 
838
795
  // Used by the microservices to consume messages from the exchange
839
- async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions) : Promise<any> {
796
+ async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions) : Promise<void> {
840
797
  const optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options };
841
798
  RabbitMq.validateName('exchange', exchange);
842
799
  RabbitMq.validateName('queue', queue);
843
- const { limit, deadMessageTtl } = optionsWithDefaults;
800
+ const { limit } = optionsWithDefaults;
844
801
  // TODO: [QUORUM-PHASE-3] Delete the if statement after all the queues are created as quorum queues
845
802
  if (options?.isQuorumQueue !== false && DISABLE_QUORUM_QUEUES_CONSUME !== 'true') {
846
803
  await this.saveConsumer(queue, callback, options);
@@ -890,28 +847,22 @@ class RabbitMq implements IAfRabbitMq {
890
847
 
891
848
  // Used by the microservices to publish messages to the exchange
892
849
  // TODO: [QUORUM-PHASE-3] isQuorumQueue flag is not needed anymore because all the queues are created as quorum queues
893
- async publish(exchange: string, content: any, customHeaders?: any, isQuorumQueue = true) : Promise<boolean> {
850
+ async publish(exchange: string, content: any, customHeaders?: PublishOptions['headers'], isQuorumQueue = true): Promise<void> {
851
+ await setImmediate();
894
852
  if (isQuorumQueue && DISABLE_QUORUM_QUEUES_PUBLISH !== 'true') {
895
- return wrapSetImmediate(async () => {
896
- await this.assertVHost();
897
- RabbitMq.validateName('exchange', exchange);
898
- const channel: ChannelWrapper = await this.assertChannel({ connection: this.publishConnection });
899
- await this.assertExchange(exchange, this.publishConnection);
900
- await channel.publish(exchange, '',
901
- Buffer.from(JSON.stringify(content)),
902
- RabbitMq.getPublishOptions(customHeaders));
903
- });
853
+ await this.assertVHost();
854
+ RabbitMq.validateName('exchange', exchange);
855
+ const channel: ChannelWrapper = await this.assertChannel({ connection: this.publishConnection });
856
+ await this.assertExchange(exchange, this.publishConnection);
857
+ await channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
858
+ return;
904
859
  }
905
860
 
906
861
  // TODO: [QUORUM-PHASE-3] Delete the old implementation
907
- return wrapSetImmediate(async () => {
908
- RabbitMq.validateName('exchange', exchange);
909
- const channel: ChannelWrapper = await this.assertChannelOld({ connection: this.oldPublishConnection });
910
- await this.assertExchangeOld(exchange, this.oldPublishConnection);
911
- await channel.publish(exchange, '',
912
- Buffer.from(JSON.stringify(content)),
913
- RabbitMq.getPublishOptions(customHeaders));
914
- });
862
+ RabbitMq.validateName('exchange', exchange);
863
+ const channel: ChannelWrapper = await this.assertChannelOld({ connection: this.oldPublishConnection });
864
+ await this.assertExchangeOld(exchange, this.oldPublishConnection);
865
+ await channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
915
866
  }
916
867
 
917
868
  // Used by the microservices to send messages to the queue
@@ -919,8 +870,8 @@ class RabbitMq implements IAfRabbitMq {
919
870
  async sendToQueue(
920
871
  queue: string,
921
872
  content: any,
922
- options?: any,
923
- customHeaders?: any,
873
+ options?: Options.AssertQueue,
874
+ customHeaders?: PublishOptions['headers'],
924
875
  isQuorumQueue = true,
925
876
  ): Promise<boolean | undefined> {
926
877
  if (isQuorumQueue && DISABLE_QUORUM_QUEUES_PUBLISH !== 'true') {
@@ -928,7 +879,7 @@ class RabbitMq implements IAfRabbitMq {
928
879
  await this.assertVHost();
929
880
  await this.assertChannel({ connection: this.publishConnection });
930
881
  } catch (e) {
931
- logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
882
+ this.#logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
932
883
  throw e;
933
884
  }
934
885
 
@@ -936,26 +887,24 @@ class RabbitMq implements IAfRabbitMq {
936
887
  RabbitMq.validateName('queue', queue);
937
888
  await this.assertQueue(queue, options);
938
889
  } catch (e) {
939
- logger.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
890
+ this.#logger.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
940
891
  throw e;
941
892
  }
942
893
 
943
894
  try {
944
- const res = await this.publishChannel?.sendToQueue(queue,
945
- Buffer.from(JSON.stringify(content)),
946
- RabbitMq.getPublishOptions(customHeaders));
947
- debug(`rabbit: sending to queue ${queue}`, { res });
895
+ const res = await this.publishChannel?.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
896
+ this.#logger.debug(`rabbit: sending to queue ${queue}`, { res });
948
897
  return res;
949
898
  } catch (e) {
950
899
  const isConnected = await this.isConnected();
951
- logger.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
900
+ this.#logger.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
952
901
  throw e;
953
902
  }
954
903
  } else {
955
904
  try {
956
905
  await this.assertChannelOld({ connection: this.oldPublishConnection });
957
906
  } catch (e) {
958
- logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
907
+ this.#logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
959
908
  throw e;
960
909
  }
961
910
 
@@ -963,19 +912,17 @@ class RabbitMq implements IAfRabbitMq {
963
912
  RabbitMq.validateName('queue', queue);
964
913
  await this.assertQueueOld(queue, options);
965
914
  } catch (e) {
966
- logger.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
915
+ this.#logger.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
967
916
  throw e;
968
917
  }
969
918
 
970
919
  try {
971
- const res = await this.oldPublishChannel?.sendToQueue(queue,
972
- Buffer.from(JSON.stringify(content)),
973
- RabbitMq.getPublishOptions(customHeaders));
974
- debug(`rabbit: sending to queue ${queue}`, { res });
920
+ const res = await this.oldPublishChannel?.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
921
+ this.#logger.debug(`rabbit: sending to queue ${queue}`, { res });
975
922
  return res;
976
923
  } catch (e) {
977
924
  const isConnected = await this.isConnectedOld();
978
- logger.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
925
+ this.#logger.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
979
926
  throw e;
980
927
  }
981
928
  }
@@ -986,26 +933,23 @@ class RabbitMq implements IAfRabbitMq {
986
933
  // TODO:[QUORUM-PHASE-3] Remove the condition
987
934
  if (!this.gracefulShutdownStarted) {
988
935
  if (DISABLE_QUORUM_QUEUES_CONSUME !== 'true' || DISABLE_QUORUM_QUEUES_PUBLISH !== 'true') {
989
- debug('rabbit: start is connected');
936
+ this.#logger.debug('rabbit: start is connected');
990
937
  const [consumeConnection, publishConnection] = await Promise.all([
991
938
  this.getConnection(this.consumeConnection),
992
939
  this.getConnection(this.publishConnection),
993
940
  ]);
994
941
  isConnected = consumeConnection.isConnected() && publishConnection.isConnected();
995
942
  if (!isConnected) {
996
- logger.error('rabbit: isConnected - false');
943
+ this.#logger.error('rabbit: isConnected - false');
997
944
  return false;
998
945
  }
999
946
  try {
1000
- const unRegisteredConsumers = this.consumersToRegister.filter((c: AfConsumer) => {
1001
- const doesConsumerExist = this.consumersTags.get(c.queue);
1002
- return !doesConsumerExist;
1003
- });
947
+ const unRegisteredConsumers = this.consumersToRegister.filter((c) => !this.consumersTags.get(c.queue));
1004
948
 
1005
949
  if (unRegisteredConsumers.length > 0) {
1006
950
  const queueNames = unRegisteredConsumers.map((c) => c.queue);
1007
951
 
1008
- logger.error('rabbit: found unregistered consumers for queues', {
952
+ this.#logger.error('rabbit: found unregistered consumers for queues', {
1009
953
  count: queueNames.length,
1010
954
  queues: queueNames,
1011
955
  });
@@ -1015,10 +959,10 @@ class RabbitMq implements IAfRabbitMq {
1015
959
  await channel.waitForConnect();
1016
960
 
1017
961
  await Promise.all(
1018
- this.consumersToRegister.map((c: AfConsumer) => channel.checkQueue(c.queue)),
962
+ this.consumersToRegister.map((c) => channel.checkQueue(c.queue)),
1019
963
  );
1020
964
  } catch (e: RabbitError | any) {
1021
- logger.error('rabbit: isConnected - false', { msg: e.message });
965
+ this.#logger.error('rabbit: isConnected - false', { msg: e.message });
1022
966
  return false;
1023
967
  }
1024
968
  }
@@ -1026,20 +970,20 @@ class RabbitMq implements IAfRabbitMq {
1026
970
  isConnected = await this.isConnectedOld();
1027
971
  }
1028
972
 
1029
- logger.debug(`rabbit: isConnected - ${isConnected}`);
973
+ this.#logger.debug(`rabbit: isConnected - ${isConnected}`);
1030
974
  return isConnected;
1031
975
  }
1032
976
 
1033
- async gracefulShutdown(signal: string) : Promise<void> {
977
+ async gracefulShutdown(signal: string): Promise<void> {
1034
978
  // TODO: [QUORUM-PHASE-3] Delete the old implementation they are not needed anymore
1035
979
  this.gracefulShutdownStarted = true;
1036
980
  const tagsNumber = this.consumersTags.size + this.oldConsumersTags.size;
1037
- logger.info(`rabbit: [gracefully-shutdown] received ${signal}! canceling #${tagsNumber} tags...`);
981
+ this.#logger.info(`rabbit: [gracefully-shutdown] received ${signal}! canceling #${tagsNumber} tags...`);
1038
982
  const cancelTagPromises = [...this.consumersTags].map(
1039
- ([_, { channel, consumerTag }]) => channel.cancel(consumerTag),
983
+ ([, { channel, consumerTag }]) => channel.cancel(consumerTag),
1040
984
  );
1041
985
  const cancelTagPromisesOld = [...this.oldConsumersTags].map(
1042
- ([_, { channel, consumerTag }]) => channel.cancel(consumerTag),
986
+ ([, { channel, consumerTag }]) => channel.cancel(consumerTag),
1043
987
  );
1044
988
  // Clean the array to avoid race
1045
989
  this.consumersTags.clear();
@@ -1047,13 +991,13 @@ class RabbitMq implements IAfRabbitMq {
1047
991
  const results = await Promise.allSettled([...cancelTagPromises, ...cancelTagPromisesOld]);
1048
992
  const rejected = results.filter((p) => p.status === 'rejected');
1049
993
  if (rejected.length > 0) {
1050
- logger.warn(`rabbit: [gracefully-shutdown] #${rejected.length}/${tagsNumber} tags failed to cancel: ${rejected}`);
994
+ this.#logger.warn(`rabbit: [gracefully-shutdown] #${rejected.length}/${tagsNumber} tags failed to cancel: ${rejected}`);
1051
995
  } else {
1052
- logger.info('rabbit: [gracefully-shutdown] all tags successfully canceled.');
996
+ this.#logger.info('rabbit: [gracefully-shutdown] all tags successfully canceled.');
1053
997
  }
1054
998
  }
1055
999
 
1056
- private async consumeFromRabbitOld(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
1000
+ private async consumeFromRabbitOld(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<void> {
1057
1001
  const optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options };
1058
1002
  RabbitMq.validateName('queue', queue);
1059
1003
  const uniqueId = randomUUID();
@@ -1064,11 +1008,11 @@ class RabbitMq implements IAfRabbitMq {
1064
1008
  if (!this.redisLock) {
1065
1009
  throw new RabbitError('Usage of consumeWithLock requires RedisInstance');
1066
1010
  }
1067
- logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
1011
+ this.#logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
1068
1012
  }
1069
1013
  const channel = await this.getNewChannelOld({ connection: this.oldConsumeConnection });
1070
1014
  return channel.addSetup(async (confirmChannel: ConfirmChannel) => {
1071
- const q = await this.assertQueueOld(queue, optionsWithDefaults);
1015
+ await this.assertQueueOld(queue, optionsWithDefaults);
1072
1016
  await confirmChannel.prefetch(limit, false);
1073
1017
  const { consumerTag } = await confirmChannel.consume(
1074
1018
  queue,
@@ -1077,9 +1021,7 @@ class RabbitMq implements IAfRabbitMq {
1077
1021
  return null;
1078
1022
  }
1079
1023
 
1080
- const traceId = msg.properties.headers![TRACING_HEADER];
1081
- const userId = msg.properties.headers![USER_TRACING_HEADER];
1082
- const automationId = msg.properties.headers![AUTOMATION_ID_HEADER];
1024
+ const { [TRACING_HEADER]: traceId, [USER_TRACING_HEADER]: userId, [AUTOMATION_ID_HEADER]: automationId } = msg.properties.headers ?? {};
1083
1025
  const parsedMessage = RabbitMq.parseMsg(msg);
1084
1026
  const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
1085
1027
  const trace = newTrace(traceTypes.RABBIT);
@@ -1094,13 +1036,13 @@ class RabbitMq implements IAfRabbitMq {
1094
1036
  createOrSetRabbitTrace(outbreakTrace, userId),
1095
1037
  ]);
1096
1038
  } catch (e) {
1097
- logger.error('rabbit: failed to setRabbitTrace', { userId, e });
1039
+ this.#logger.error('rabbit: failed to setRabbitTrace', { userId, e });
1098
1040
  return this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg);
1099
1041
  }
1100
1042
  }
1101
1043
 
1102
1044
  if (traceId) {
1103
- (trace as any)?.context?.set(TRACING_HEADER, traceId);
1045
+ trace?.context?.set(TRACING_HEADER, traceId);
1104
1046
  (outbreakTrace as any)?.context.set(TRACING_HEADER, traceId);
1105
1047
  }
1106
1048
 
@@ -1124,33 +1066,33 @@ class RabbitMq implements IAfRabbitMq {
1124
1066
  return;
1125
1067
  }
1126
1068
  messageAcked = true;
1127
- return this.ack(confirmChannel, msg, true, releaseLock)(msg);
1069
+ await this.ack(confirmChannel, msg, true, releaseLock)(msg);
1128
1070
  };
1129
1071
 
1130
1072
  const localNack = async (_: ConsumeMessageOrNull, nackOptions: NackOptions = {}) => {
1131
1073
  if (messageAcked) {
1132
1074
  return;
1133
1075
  }
1134
- debug('rabbit localNack', { messageAcked, uniqueId, deliveryTag: msg.fields.deliveryTag });
1076
+ this.#logger.debug('rabbit localNack', { messageAcked, uniqueId, deliveryTag: msg.fields.deliveryTag });
1135
1077
  messageAcked = true;
1136
- return this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg, nackOptions);
1078
+ await this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg, nackOptions);
1137
1079
  };
1138
1080
 
1139
1081
  try {
1140
- await callback(
1082
+ return await callback(
1141
1083
  parsedMessage,
1142
1084
  localAck,
1143
1085
  localNack,
1144
1086
  );
1145
1087
  } catch (e) {
1146
- await localNack(msg);
1088
+ return localNack(msg);
1147
1089
  }
1148
1090
  }, CONSUMER_DEFAULT_OPTIONS,
1149
1091
  );
1150
1092
  if (!consumerTag) {
1151
- logger.error(`rabbit: failed to consume from queue ${queue} old`);
1093
+ this.#logger.error(`rabbit: failed to consume from queue ${queue} old`);
1152
1094
  } else {
1153
- logger.info(`rabbit: adding tag ${consumerTag} to the array old`);
1095
+ this.#logger.info(`rabbit: adding tag ${consumerTag} to the array old`);
1154
1096
  this.oldConsumersTags.set(queue, { channel: confirmChannel, consumerTag });
1155
1097
  }
1156
1098
  });
@@ -1162,12 +1104,12 @@ class RabbitMq implements IAfRabbitMq {
1162
1104
  onClose = null,
1163
1105
  options = {},
1164
1106
  connection,
1165
- }: newChannelOpts) {
1107
+ }: newChannelOpts): Promise<ChannelWrapper> {
1166
1108
  let localConnection!: AmqpConnectionManager;
1167
1109
  try {
1168
1110
  localConnection = await this.getConnectionOld(connection);
1169
1111
  } catch (e) {
1170
- logger.error(`rabbit: error on get connection for new channel ${name} `, { e });
1112
+ this.#logger.error(`rabbit: error on get connection for new channel ${name} `, { e });
1171
1113
  throw e;
1172
1114
  }
1173
1115
 
@@ -1177,116 +1119,116 @@ class RabbitMq implements IAfRabbitMq {
1177
1119
 
1178
1120
  const channel = localConnection?.createChannel({ ...options });
1179
1121
  once(channel, 'close').then((args) => {
1180
- logger.error(`rabbit: channel ${name} closed`);
1122
+ this.#logger.error(`rabbit: channel ${name} closed`);
1181
1123
  onClose?.(args);
1182
1124
  });
1183
1125
  try {
1184
1126
  await once(channel, 'connect');
1185
- debug(`rabbit: channel ${name} CONNECTED`);
1127
+ this.#logger.debug(`rabbit: channel ${name} CONNECTED`);
1186
1128
  return channel;
1187
1129
  } catch (err) {
1188
- logger.error(`rabbit: channel error ${name} error`, { err });
1130
+ this.#logger.error(`rabbit: channel error ${name} error`, { err });
1189
1131
  throw err;
1190
1132
  }
1191
1133
  }
1192
1134
 
1193
- async getConnectionOld(connection: ConnectionData) {
1194
- return new Promise<AmqpConnectionManager>(async (resolve, reject) => {
1195
- const {
1196
- amqpConnection: connectionLocal,
1197
- creatingConnection,
1198
- connectionCreatedEventName,
1199
- connectionFailedEventName,
1200
- blockReconnect,
1201
- } = connection;
1202
-
1203
- if (blockReconnect) {
1204
- debug('rabbit: block reconnect');
1135
+ async getConnectionOld(connection: ConnectionData): Promise<AmqpConnectionManager> {
1136
+ const {
1137
+ amqpConnection: connectionLocal,
1138
+ creatingConnection,
1139
+ connectionCreatedEventName,
1140
+ connectionFailedEventName,
1141
+ blockReconnect,
1142
+ } = connection;
1143
+
1144
+ if (blockReconnect) {
1145
+ this.#logger.debug('rabbit: block reconnect');
1146
+ // @ts-expect-error we are returning undefined while the function clearly expects a connection.
1147
+ return undefined;
1148
+ }
1149
+ if (connectionLocal !== null) {
1150
+ if (this.options?.disableReconnect || connectionLocal?.isConnected()) {
1151
+ this.#logger.debug('rabbit: connection - is connected');
1205
1152
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
1206
1153
  // @ts-ignore
1207
- return resolve();
1208
- }
1209
- if (connectionLocal !== null) {
1210
- if (this.options?.disableReconnect || connectionLocal?.isConnected()) {
1211
- debug('rabbit: connection - is connected');
1212
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
1213
- // @ts-ignore
1214
- return resolve(connectionLocal);
1215
- }
1216
- debug('rabbit: connection - reconnecting');
1154
+ return connectionLocal;
1217
1155
  }
1218
- if (creatingConnection) {
1219
- debug('rabbit: creating connection emi');
1220
- this.oldEm.once(connectionCreatedEventName, resolve);
1221
- this.oldEm.once(connectionFailedEventName, reject);
1222
- return;
1156
+ this.#logger.debug('rabbit: connection - reconnecting');
1157
+ }
1158
+ if (creatingConnection) {
1159
+ const [event, value] = await Promise.race([connectionCreatedEventName, connectionFailedEventName].map((e) => once(this.oldEm, e).then(([result]) => [e, result] as const)));
1160
+ if (event === connectionCreatedEventName) {
1161
+ return value;
1223
1162
  }
1224
- connection.creatingConnection = true;
1225
- let isResolved = false;
1226
-
1227
- // It is import to use it as a function and not as a variable
1228
- // because of k8s changes the env variables
1229
- // and we want to use the new values
1230
- const findServers = () => {
1231
- const userName = process.env.RABBITMQ_USERNAME || 'guest';
1232
- const password = process.env.RABBITMQ_PASSWORD || 'guest';
1233
- const host = this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost';
1163
+ throw value;
1164
+ }
1165
+ connection.creatingConnection = true;
1166
+ let isResolved = false;
1234
1167
 
1235
- debug('rabbit: creating connection', { host, userName, HEARTBEAT });
1168
+ // It is import to use it as a function and not as a variable
1169
+ // because of k8s changes the env variables
1170
+ // and we want to use the new values
1171
+ const findServers = () => {
1172
+ const userName = process.env.RABBITMQ_USERNAME || 'guest';
1173
+ const password = process.env.RABBITMQ_PASSWORD || 'guest';
1174
+ const host = this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost';
1236
1175
 
1237
- return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
1238
- };
1176
+ this.#logger.debug('rabbit: creating connection', { host, userName, HEARTBEAT });
1239
1177
 
1240
- const defaultUrls = findServers();
1241
- const newConnection: AmqpConnectionManager = await connect(defaultUrls, {
1242
- findServers,
1243
- });
1178
+ return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
1179
+ };
1244
1180
 
1245
- connection.amqpConnection = newConnection;
1246
- newConnection.on('error', (err) => {
1247
- logger.error('rabbit: connection error', { err });
1248
- if (!isResolved) {
1249
- isResolved = true;
1250
- reject(err);
1251
- this.oldEm.emit(connectionFailedEventName, err);
1252
- }
1253
- });
1181
+ const defaultUrls = findServers();
1182
+ const newConnection: AmqpConnectionManager = await connect(defaultUrls, { findServers });
1254
1183
 
1255
- newConnection.on('connectFailed', (err) => {
1256
- this.oldConsumersTags.clear();
1257
- if (typeof err.url === 'string') {
1258
- err.url = this.maskURL(err.url);
1259
- }
1260
- logger.error('rabbit: connection connectFailed', { err });
1261
- if (!isResolved) {
1262
- isResolved = true;
1263
- reject(err);
1264
- this.oldEm.emit(connectionFailedEventName, err);
1265
- }
1266
- });
1184
+ connection.amqpConnection = newConnection;
1185
+ const { promise, reject, resolve } = createDeferredPromise<AmqpConnectionManager>();
1267
1186
 
1268
- newConnection.on('disconnect', ({ err }) => {
1269
- this.oldConsumersTags.clear();
1270
- debug('rabbit: connection closed');
1271
- if (this.options?.disableReconnect) {
1272
- logger.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
1273
- connection.blockReconnect = true;
1274
- } else {
1275
- logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
1276
- }
1277
- });
1187
+ newConnection.on('error', (err) => {
1188
+ this.#logger.error('rabbit: connection error', { err });
1189
+ if (!isResolved) {
1190
+ isResolved = true;
1191
+ reject(err);
1192
+ this.oldEm.emit(connectionFailedEventName, err);
1193
+ }
1194
+ });
1278
1195
 
1279
- newConnection.once('connect', async () => {
1280
- debug('rabbit: connection established');
1281
- connection.creatingConnection = false;
1282
- this.oldEm.emit(connectionCreatedEventName, newConnection);
1196
+ newConnection.on('connectFailed', (err) => {
1197
+ this.oldConsumersTags.clear();
1198
+ if (typeof err.url === 'string') {
1199
+ err.url = this.maskURL(err.url);
1200
+ }
1201
+ logger.error('rabbit: connection connectFailed', { err });
1202
+ if (!isResolved) {
1283
1203
  isResolved = true;
1284
- resolve(newConnection);
1285
- });
1204
+ reject(err);
1205
+ this.oldEm.emit(connectionFailedEventName, err);
1206
+ }
1286
1207
  });
1208
+
1209
+ newConnection.on('disconnect', ({ err }) => {
1210
+ this.oldConsumersTags.clear();
1211
+ this.#logger.debug('rabbit: connection closed');
1212
+ if (this.options?.disableReconnect) {
1213
+ logger.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
1214
+ connection.blockReconnect = true;
1215
+ } else {
1216
+ logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
1217
+ }
1218
+ });
1219
+
1220
+ newConnection.once('connect', async () => {
1221
+ this.#logger.debug('rabbit: connection established');
1222
+ connection.creatingConnection = false;
1223
+ this.oldEm.emit(connectionCreatedEventName, newConnection);
1224
+ isResolved = true;
1225
+ resolve(newConnection);
1226
+ });
1227
+
1228
+ return promise;
1287
1229
  }
1288
1230
 
1289
- private saveConsumerOld(queue: string, callback: CallbackFunction, options: ConsumeOptions | undefined) {
1231
+ private saveConsumerOld(queue: string, callback: CallbackFunction, options: ConsumeOptions | undefined): void {
1290
1232
  const isConsumerExist: boolean = this.oldConsumersToRegister.some((consumer) => consumer.queue === queue);
1291
1233
  if (!isConsumerExist) {
1292
1234
  logger.info(`rabbit: consumer: ${queue} saved in consumer array`);
@@ -1298,19 +1240,15 @@ class RabbitMq implements IAfRabbitMq {
1298
1240
  }
1299
1241
  }
1300
1242
 
1301
- async assertQueueOld(queueName: string, options?: Options.AssertQueue): Promise<any> {
1243
+ async assertQueueOld(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
1302
1244
  RabbitMq.validateName('queue', queueName);
1303
1245
  if (this.oldQueues[queueName]) {
1304
1246
  delete this.oldQueueSetupPromises[queueName];
1305
- return this.oldQueues[queueName];
1247
+ return this.oldQueues[queueName]!;
1306
1248
  }
1307
1249
 
1308
- if (this.oldQueueSetupPromises[queueName]) {
1309
- return this.oldQueueSetupPromises[queueName];
1310
- }
1311
-
1312
- this.oldQueueSetupPromises[queueName] = this.setupQueueOld(queueName, options);
1313
- return this.oldQueueSetupPromises[queueName];
1250
+ this.oldQueueSetupPromises[queueName] ||= this.setupQueueOld(queueName, options);
1251
+ return this.oldQueueSetupPromises[queueName]!;
1314
1252
  }
1315
1253
 
1316
1254
  async setupQueueOld(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
@@ -1328,20 +1266,20 @@ class RabbitMq implements IAfRabbitMq {
1328
1266
  };
1329
1267
  try {
1330
1268
  const channel: ChannelWrapper = await this.assertChannelOld({ connection });
1331
- debug('assertQueue->channel.addSetup', { queueName });
1269
+ this.#logger.debug('assertQueue->channel.addSetup', { queueName });
1332
1270
  await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
1333
- debug('assertQueue->channel.assertQueue', { queueName });
1271
+ this.#logger.debug('assertQueue->channel.assertQueue', { queueName });
1334
1272
  queue = await channel.assertQueue(queueName, localeOptions);
1335
1273
  } catch (e) {
1336
1274
  logger.error('rabbit: assertQueue error', { queueName, options, error: e });
1337
1275
  if (!this.options?.dontRetryAssert) {
1338
- debug('retrying assertQueue', { queueName });
1276
+ this.#logger.debug('retrying assertQueue', { queueName });
1339
1277
  const channel = await this.assertChannelOld({ force: true, connection });
1340
1278
  await this.deleteQueueOld(queueName, connection);
1341
1279
 
1342
- debug('retrying assertQueue->channel.addSetup', { queueName });
1280
+ this.#logger.debug('retrying assertQueue->channel.addSetup', { queueName });
1343
1281
  await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
1344
- debug('retrying assertQueue->channel.assertQueue', { queueName });
1282
+ this.#logger.debug('retrying assertQueue->channel.assertQueue', { queueName });
1345
1283
  queue = await channel.assertQueue(queueName, localeOptions);
1346
1284
  } else {
1347
1285
  throw e;
@@ -1353,58 +1291,60 @@ class RabbitMq implements IAfRabbitMq {
1353
1291
  }
1354
1292
 
1355
1293
  async assertChannelOld({ force = false, connection }: assertChannelOpts): Promise<ChannelWrapper> {
1356
- if (!this.oldPublishChannelSetupPromise) {
1357
- this.oldPublishChannelSetupPromise = new Promise<ChannelWrapper>(async (resolve, reject) => {
1358
- if (this.oldPublishChannel && !force) {
1359
- return resolve(this.oldPublishChannel);
1360
- }
1294
+ if (this.oldPublishChannelSetupPromise) {
1295
+ return this.oldPublishChannelSetupPromise;
1296
+ }
1297
+ const { promise, resolve, reject } = createDeferredPromise<ChannelWrapper>();
1298
+ this.oldPublishChannelSetupPromise = promise;
1299
+ if (this.oldPublishChannel && !force) {
1300
+ resolve(this.oldPublishChannel);
1301
+ return promise;
1302
+ }
1361
1303
 
1362
- try {
1363
- const channel = await this.getNewChannelOld({ connection });
1364
- channel.on('error', (err) => {
1365
- logger.error('rabbit: channel error', { err });
1366
- });
1367
- if (this.oldPublishConnection === connection) {
1368
- this.oldPublishChannel = channel;
1369
- }
1370
- resolve(channel);
1371
- } catch (e) {
1372
- reject(e);
1373
- }
1304
+ try {
1305
+ const channel = await this.getNewChannelOld({ connection });
1306
+ channel.on('error', (err) => {
1307
+ logger.error('rabbit: channel error', { err });
1374
1308
  });
1309
+ if (this.oldPublishConnection === connection) {
1310
+ this.oldPublishChannel = channel;
1311
+ }
1312
+ resolve(channel);
1313
+ } catch (e) {
1314
+ reject(e);
1375
1315
  }
1376
- return this.oldPublishChannelSetupPromise;
1316
+ return promise;
1377
1317
  }
1378
1318
 
1379
- private async deleteQueueOld(queue: string, connection: ConnectionData) {
1319
+ private async deleteQueueOld(queue: string, connection: ConnectionData): Promise<Replies.DeleteQueue> {
1380
1320
  RabbitMq.validateName('queue', queue);
1381
1321
  const channel: ChannelWrapper = await this.assertChannelOld({ connection });
1382
1322
  logger.info('rabbit: deleting queue', { queue });
1383
1323
  const deleteQueueRes = await channel.deleteQueue(queue);
1384
- debug('queue deleted', deleteQueueRes);
1324
+ this.#logger.debug('queue deleted', deleteQueueRes);
1385
1325
  return deleteQueueRes;
1386
1326
  }
1387
1327
 
1388
- async assertExchangeOld(exchangeName: string, connection: ConnectionData) {
1328
+ async assertExchangeOld(exchangeName: string, connection: ConnectionData): Promise<Replies.AssertExchange> {
1389
1329
  const channel: ChannelWrapper = await this.assertChannelOld({ connection });
1390
1330
 
1391
1331
  if (this.oldExchanges[exchangeName]) {
1392
1332
  delete this.oldAssertExchangePromises[exchangeName];
1393
- return this.oldExchanges[exchangeName];
1333
+ return this.oldExchanges[exchangeName]!;
1394
1334
  }
1395
1335
 
1396
1336
  if (this.oldAssertExchangePromises[exchangeName]) {
1397
- return this.oldAssertExchangePromises[exchangeName];
1337
+ return this.oldAssertExchangePromises[exchangeName]!;
1398
1338
  }
1399
1339
 
1400
1340
  this.oldAssertExchangePromises[exchangeName] = assertExchangeFanout(channel, exchangeName);
1401
1341
  this.oldExchanges[exchangeName] = await this.oldAssertExchangePromises[exchangeName];
1402
- return this.oldExchanges[exchangeName];
1342
+ return this.oldExchanges[exchangeName]!;
1403
1343
  }
1404
1344
 
1405
- async isConnectedOld() : Promise<boolean> {
1345
+ async isConnectedOld(): Promise<boolean> {
1406
1346
  if (!this.gracefulShutdownStarted) {
1407
- debug('rabbit: start is connected old');
1347
+ this.#logger.debug('rabbit: start is connected old');
1408
1348
  const [oldConsumeConnection, oldPublishConnection] = await Promise.all([
1409
1349
  this.getConnectionOld(this.oldConsumeConnection),
1410
1350
  this.getConnectionOld(this.oldPublishConnection),
@@ -1416,10 +1356,7 @@ class RabbitMq implements IAfRabbitMq {
1416
1356
  }
1417
1357
 
1418
1358
  try {
1419
- const unRegisteredConsumersOld = this.consumersToRegister.filter((c: AfConsumer) => {
1420
- const doesConsumerExist = this.consumersTags.get(c.queue);
1421
- return !doesConsumerExist;
1422
- });
1359
+ const unRegisteredConsumersOld = this.consumersToRegister.filter((c) => !this.consumersTags.get(c.queue));
1423
1360
 
1424
1361
  if (unRegisteredConsumersOld.length > 0) {
1425
1362
  const queueNames = unRegisteredConsumersOld.map((c) => c.queue);
@@ -1431,7 +1368,7 @@ class RabbitMq implements IAfRabbitMq {
1431
1368
  throw new RabbitError('Found unregistered consumers old');
1432
1369
  }
1433
1370
 
1434
- const channelOld: any = await this.assertChannelOld({ connection: this.oldPublishConnection });
1371
+ const channelOld = await this.assertChannelOld({ connection: this.oldPublishConnection });
1435
1372
  await channelOld.waitForConnect();
1436
1373
 
1437
1374
  await Promise.all(