@autofleet/rabbit 3.2.25 → 3.2.26-beta.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/dist/index.d.ts CHANGED
@@ -3,7 +3,8 @@ import { EventEmitter } from 'events';
3
3
  import { AmqpConnectionManager, ChannelWrapper, CreateChannelOpts } from 'amqp-connection-manager';
4
4
  import { ConfirmChannel, ConsumeMessage, Options, Replies } from 'amqplib';
5
5
  import { RedisConfig } from './lib/redis';
6
- import { CallbackFunction, ConsumeMessageOrNull, ConsumeOptions, CustomMessageHeaders, QueuesCache, RedisLockType, ExchangesCache, QueueSetupPromisesDictionary, AssertExchangePromisesDictionary } from './lib/types';
6
+ import { ConnectionPurpose } from './lib/consts';
7
+ import { CallbackFunction, ConsumeMessageOrNull, ConsumeOptions, CustomMessageHeaders, QueuesCache, RedisLockType, ExchangesCache, QueueSetupPromisesDictionary, AssertExchangePromisesDictionary, ConnectionData } from './lib/types';
7
8
  export interface IAfRabbitMq {
8
9
  ack: any;
9
10
  nack: any;
@@ -37,10 +38,12 @@ type newChannelOpts = {
37
38
  name?: string;
38
39
  onClose?: null | ((args: any | null) => void);
39
40
  options?: CreateChannelOpts | undefined;
41
+ connectionPurpose?: ConnectionPurpose;
40
42
  };
41
43
  type assertChannelOpts = {
42
44
  channelName?: string;
43
45
  force?: boolean;
46
+ connectionPurpose?: ConnectionPurpose;
44
47
  };
45
48
  declare class RabbitMq implements IAfRabbitMq {
46
49
  static parseMsg(msg: any): any;
@@ -57,12 +60,14 @@ declare class RabbitMq implements IAfRabbitMq {
57
60
  };
58
61
  DISCONNECT_MSG: string;
59
62
  RECONNECT_MSG: string;
60
- channel: ChannelWrapper | null;
63
+ publishChannel: ChannelWrapper | null;
61
64
  publishChannelSetupPromise: Promise<ChannelWrapper> | null;
62
65
  blockReconnect: boolean | null | undefined;
63
- connection: AmqpConnectionManager | null | undefined;
66
+ connectionsMap: {
67
+ [ConnectionPurpose.Consume]: ConnectionData;
68
+ [ConnectionPurpose.Publish]: ConnectionData;
69
+ };
64
70
  em: EventEmitter;
65
- creatingConnection: boolean;
66
71
  exchanges: ExchangesCache;
67
72
  queues: QueuesCache;
68
73
  queueSetupPromises: QueueSetupPromisesDictionary;
@@ -77,16 +82,16 @@ declare class RabbitMq implements IAfRabbitMq {
77
82
  private shouldConsumeMessageByTimestamp;
78
83
  ack: (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp?: boolean, releaseLock?: null) => (userMsg: ConsumeMessage) => Promise<any>;
79
84
  nack: (channel: ConfirmChannel, queue: string, options: any, deadQueueOptions: Options.AssertQueue, msg: ConsumeMessageOrNull, releaseLock: any) => (userMsg: ConsumeMessageOrNull, { skipRetry, }?: NackOptions) => Promise<any>;
80
- getConnection(): Promise<AmqpConnectionManager>;
81
- getNewChannel({ name, onClose, options }?: newChannelOpts): Promise<ChannelWrapper>;
82
- assertChannel({ force }?: assertChannelOpts): Promise<ChannelWrapper>;
85
+ getConnection(connectionPurpose: ConnectionPurpose): Promise<AmqpConnectionManager | null | undefined>;
86
+ getNewChannel({ name, onClose, options, connectionPurpose, }: newChannelOpts): Promise<ChannelWrapper>;
87
+ assertChannel({ force, connectionPurpose }: assertChannelOpts): Promise<ChannelWrapper>;
83
88
  assertExchange(exchangeName: string, options?: any): Promise<any>;
84
- getQueueLength(queue: string): Promise<Replies.AssertQueue>;
89
+ getQueueLength(queue: string, connectionPurpose?: ConnectionPurpose): Promise<Replies.AssertQueue>;
85
90
  private deleteQueue;
86
- bindQueue(queue: string, exchange: string): Promise<void>;
87
- setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue>;
91
+ bindQueue(queue: string, exchange: string, connectionPurpose: ConnectionPurpose): Promise<void>;
92
+ setupQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue): Promise<Replies.AssertQueue>;
88
93
  static shouldUseQuorum(queueName: string): boolean;
89
- assertQueue(queueName: string, options?: Options.AssertQueue): Promise<any>;
94
+ assertQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue): Promise<any>;
90
95
  private saveConsumer;
91
96
  consume(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any>;
92
97
  private lockRedisIfNeeded;
package/dist/index.js CHANGED
@@ -117,10 +117,22 @@ class RabbitMq {
117
117
  }
118
118
  };
119
119
  this.em = new events_1.EventEmitter();
120
- this.channel = null;
120
+ this.publishChannel = null;
121
121
  this.publishChannelSetupPromise = null;
122
- this.connection = null;
123
- this.creatingConnection = false;
122
+ this.connectionsMap = {
123
+ [consts_1.ConnectionPurpose.Consume]: {
124
+ connection: null,
125
+ creatingConnection: false,
126
+ connectionCreatedEventName: 'consumeConnectionCreated',
127
+ connectionFailedEventName: 'consumeConnectionFailed',
128
+ },
129
+ [consts_1.ConnectionPurpose.Publish]: {
130
+ connection: null,
131
+ creatingConnection: false,
132
+ connectionCreatedEventName: 'publishConnectionCreated',
133
+ connectionFailedEventName: 'publishConnectionFailed',
134
+ },
135
+ };
124
136
  this.exchanges = {};
125
137
  this.queues = {};
126
138
  this.queueSetupPromises = {};
@@ -142,30 +154,31 @@ class RabbitMq {
142
154
  });
143
155
  }
144
156
  }
145
- async getConnection() {
157
+ async getConnection(connectionPurpose) {
146
158
  return new Promise(async (resolve, reject) => {
159
+ const { connection, creatingConnection, connectionCreatedEventName, connectionFailedEventName, } = this.connectionsMap[connectionPurpose];
147
160
  if (this.blockReconnect) {
148
161
  debug('rabbit: block reconnect');
149
162
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
150
163
  // @ts-ignore
151
164
  return resolve();
152
165
  }
153
- if (this.connection !== null) {
154
- if (this.options?.disableReconnect || this.connection?.isConnected()) {
166
+ if (connection !== null) {
167
+ if (this.options?.disableReconnect || connection?.isConnected()) {
155
168
  debug('rabbit: connection - is connected');
156
169
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
157
170
  // @ts-ignore
158
- return resolve(this.connection);
171
+ return resolve(connection);
159
172
  }
160
173
  debug('rabbit: connection - reconnecting');
161
174
  }
162
- if (this.creatingConnection) {
175
+ if (creatingConnection) {
163
176
  debug('rabbit: creating connection emi');
164
- this.em.once(consts_1.CONNECTION_CREATED_CONST, resolve);
165
- this.em.once(consts_1.CONNECTION_FAILED_CONST, reject);
177
+ this.em.once(connectionCreatedEventName, resolve);
178
+ this.em.once(connectionFailedEventName, reject);
166
179
  return;
167
180
  }
168
- this.creatingConnection = true;
181
+ this.connectionsMap[connectionPurpose].creatingConnection = true;
169
182
  let isResolved = false;
170
183
  // It is import to use it as a function and not as a variable
171
184
  // because of k8s changes the env variables
@@ -178,29 +191,29 @@ class RabbitMq {
178
191
  return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
179
192
  };
180
193
  const defaultUrls = findServers();
181
- const connection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
194
+ const newConnection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
182
195
  findServers,
183
196
  });
184
- this.connection = connection;
185
- this.connection.on('error', (err) => {
197
+ this.connectionsMap[connectionPurpose].connection = newConnection;
198
+ logger_1.default.info(`rabbit: created new connection ${connectionPurpose}`);
199
+ newConnection.on('error', (err) => {
186
200
  logger_1.default.error('rabbit: connection error', { err });
187
201
  if (!isResolved) {
188
202
  isResolved = true;
189
203
  reject(err);
190
- this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
204
+ this.em.emit(connectionFailedEventName, err);
191
205
  }
192
206
  });
193
- this.connection.on('connectFailed', (err) => {
207
+ newConnection.on('connectFailed', (err) => {
194
208
  this.consumersTags = [];
195
209
  logger_1.default.error('rabbit: connection connectFailed', { err });
196
210
  if (!isResolved) {
197
211
  isResolved = true;
198
212
  reject(err);
199
- this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
213
+ this.em.emit(connectionFailedEventName, err);
200
214
  }
201
215
  });
202
- this.connection.on('disconnect', ({ err }) => {
203
- // this.channel = null;
216
+ newConnection.on('disconnect', ({ err }) => {
204
217
  this.consumersTags = [];
205
218
  debug('rabbit: connection closed');
206
219
  if (this.options?.disableReconnect) {
@@ -211,24 +224,26 @@ class RabbitMq {
211
224
  logger_1.default.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
212
225
  }
213
226
  });
214
- this.connection.once('connect', async () => {
215
- debug('rabbit: connection established');
216
- this.creatingConnection = false;
217
- this.em.emit(consts_1.CONNECTION_CREATED_CONST, connection);
227
+ newConnection.once('connect', async () => {
228
+ this.connectionsMap[connectionPurpose].creatingConnection = false;
229
+ this.em.emit(connectionCreatedEventName, newConnection);
218
230
  isResolved = true;
219
- resolve(connection);
231
+ resolve(newConnection);
220
232
  });
221
233
  });
222
234
  }
223
- async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {} } = {}) {
235
+ async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {}, connectionPurpose = consts_1.ConnectionPurpose.Consume, }) {
224
236
  let connection;
225
237
  try {
226
- connection = await this.getConnection();
238
+ connection = await this.getConnection(connectionPurpose);
227
239
  }
228
240
  catch (e) {
229
241
  logger_1.default.error(`rabbit: error on get connection for new channel ${name} `, { e });
230
242
  throw e;
231
243
  }
244
+ if (!connection) {
245
+ throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
246
+ }
232
247
  const channel = connection.createChannel({ ...options });
233
248
  (0, events_1.once)(channel, 'close').then((args) => {
234
249
  logger_1.default.error(`rabbit: channel ${name} closed`);
@@ -244,18 +259,20 @@ class RabbitMq {
244
259
  throw err;
245
260
  }
246
261
  }
247
- async assertChannel({ force = false } = {}) {
262
+ async assertChannel({ force = false, connectionPurpose = consts_1.ConnectionPurpose.Consume }) {
263
+ debug('rabbit: start assert channel', { connectionPurpose, publishPromise: this.publishChannelSetupPromise, channel: this.publishChannel });
248
264
  if (!this.publishChannelSetupPromise) {
249
265
  this.publishChannelSetupPromise = new Promise(async (resolve, reject) => {
250
- if (this.channel && !force) {
251
- return resolve(this.channel);
266
+ if (this.publishChannel && !force) {
267
+ return resolve(this.publishChannel);
252
268
  }
253
269
  try {
254
- const channel = await this.getNewChannel({});
270
+ const channel = await this.getNewChannel({ connectionPurpose });
271
+ debug('rabbit: new channel got', { connectionPurpose, channel: this.publishChannel });
255
272
  channel.on('error', (err) => {
256
273
  logger_1.default.error('rabbit: channel error', { err });
257
274
  });
258
- this.channel = channel;
275
+ this.publishChannel = channel;
259
276
  resolve(channel);
260
277
  }
261
278
  catch (e) {
@@ -265,8 +282,8 @@ class RabbitMq {
265
282
  }
266
283
  return this.publishChannelSetupPromise;
267
284
  }
268
- async assertExchange(exchangeName, options) {
269
- const channel = await this.assertChannel();
285
+ async assertExchange(exchangeName, options = { connectionPurpose: consts_1.ConnectionPurpose.Consume }) {
286
+ const channel = await this.assertChannel({ connectionPurpose: options.connectionPurpose });
270
287
  if (this.exchanges[exchangeName]) {
271
288
  delete this.assertExchangePromises[exchangeName];
272
289
  return this.exchanges[exchangeName];
@@ -278,29 +295,30 @@ class RabbitMq {
278
295
  this.exchanges[exchangeName] = await this.assertExchangePromises[exchangeName];
279
296
  return this.exchanges[exchangeName];
280
297
  }
281
- async getQueueLength(queue) {
298
+ async getQueueLength(queue, connectionPurpose = consts_1.ConnectionPurpose.Consume) {
282
299
  RabbitMq.validateName('queue', queue);
283
- const { channel } = this;
284
- if (!channel) {
300
+ const { connection } = this.connectionsMap[connectionPurpose];
301
+ const { publishChannel } = this;
302
+ if (!publishChannel) {
285
303
  throw new Error('channel is not defined');
286
304
  }
287
- debug('rabbit: getting queue length', { queue, connected: this.connection?.isConnected() });
288
- return channel?.checkQueue(queue);
305
+ debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
306
+ return publishChannel?.checkQueue(queue);
289
307
  }
290
- async deleteQueue(queue) {
308
+ async deleteQueue(queue, connectionPurpose) {
291
309
  RabbitMq.validateName('queue', queue);
292
- const channel = await this.assertChannel();
310
+ const channel = await this.assertChannel({ connectionPurpose });
293
311
  logger_1.default.info('rabbit: deleting queue', { queue });
294
312
  const deleteQueueRes = await channel.deleteQueue(queue);
295
313
  debug('queue deleted', deleteQueueRes);
296
314
  return deleteQueueRes;
297
315
  }
298
- async bindQueue(queue, exchange) {
299
- const channel = await this.assertChannel();
316
+ async bindQueue(queue, exchange, connectionPurpose) {
317
+ const channel = await this.assertChannel({ connectionPurpose });
300
318
  await channel.addSetup((setupChannel) => setupChannel.bindQueue(queue, exchange, ''));
301
319
  return channel.bindQueue(queue, exchange, '');
302
320
  }
303
- async setupQueue(queueName, options) {
321
+ async setupQueue(queueName, connectionPurpose, options) {
304
322
  let queue;
305
323
  const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
306
324
  const localeOptions = {
@@ -313,7 +331,7 @@ class RabbitMq {
313
331
  },
314
332
  };
315
333
  try {
316
- const channel = await this.assertChannel();
334
+ const channel = await this.assertChannel({ connectionPurpose });
317
335
  debug('assertQueue->channel.addSetup', { queueName });
318
336
  await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
319
337
  debug('assertQueue->channel.assertQueue', { queueName });
@@ -323,8 +341,8 @@ class RabbitMq {
323
341
  logger_1.default.error('rabbit: assertQueue error', { queueName, options, error: e });
324
342
  if (!this.options?.dontRetryAssert) {
325
343
  debug('retrying assertQueue', { queueName });
326
- const channel = await this.assertChannel({ force: true });
327
- await this.deleteQueue(queueName);
344
+ const channel = await this.assertChannel({ force: true, connectionPurpose });
345
+ await this.deleteQueue(queueName, connectionPurpose);
328
346
  debug('retrying assertQueue->channel.addSetup', { queueName });
329
347
  await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
330
348
  debug('retrying assertQueue->channel.assertQueue', { queueName });
@@ -348,7 +366,8 @@ class RabbitMq {
348
366
  }
349
367
  return false;
350
368
  }
351
- async assertQueue(queueName, options) {
369
+ async assertQueue(queueName, connectionPurpose, options) {
370
+ debug('rabbit: start assert queue', { connectionPurpose, queueName });
352
371
  RabbitMq.validateName('queue', queueName);
353
372
  if (this.queues[queueName]) {
354
373
  delete this.queueSetupPromises[queueName];
@@ -357,7 +376,8 @@ class RabbitMq {
357
376
  if (this.queueSetupPromises[queueName]) {
358
377
  return this.queueSetupPromises[queueName];
359
378
  }
360
- this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
379
+ this.queueSetupPromises[queueName] = this.setupQueue(queueName, connectionPurpose, options);
380
+ debug('rabbit: done assert queue', { connectionPurpose, queueName });
361
381
  return this.queueSetupPromises[queueName];
362
382
  }
363
383
  saveConsumer(queue, callback, options) {
@@ -400,9 +420,9 @@ class RabbitMq {
400
420
  }
401
421
  logger_1.default.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
402
422
  }
403
- const channel = await this.getNewChannel({});
423
+ const channel = await this.getNewChannel({ connectionPurpose: consts_1.ConnectionPurpose.Consume });
404
424
  return channel.addSetup(async (confirmChannel) => {
405
- const q = await this.assertQueue(queue, optionsWithDefaults);
425
+ const q = await this.assertQueue(queue, consts_1.ConnectionPurpose.Consume, optionsWithDefaults);
406
426
  await confirmChannel.prefetch(limit, false);
407
427
  const { consumerTag } = await confirmChannel.consume(queue, async (msg) => {
408
428
  if (!msg) {
@@ -484,12 +504,12 @@ class RabbitMq {
484
504
  RabbitMq.validateName('queue', queue);
485
505
  const { limit, deadMessageTtl } = optionsWithDefaults;
486
506
  await this.saveConsumer(queue, callback, options);
487
- const channel = await this.getNewChannel({ name: `consume-exchange-${exchange}-queue-${queue}` });
507
+ const channel = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}` });
488
508
  return channel.addSetup(async (c) => {
489
509
  const assertExchange = await (0, utils_1.assertExchangeFanout)(c, exchange);
490
510
  await c.assertQueue(queue);
491
511
  this.exchanges[exchange] = assertExchange;
492
- await c.prefetch(limit, false);
512
+ await c.prefetch(limit, true);
493
513
  return Promise.all([
494
514
  c.bindQueue(queue, exchange, ''),
495
515
  this.consume(queue, callback, options),
@@ -497,16 +517,17 @@ class RabbitMq {
497
517
  });
498
518
  }
499
519
  async publish(exchange, content, customHeaders) {
520
+ debug('rabbit: start publish msg');
500
521
  return (0, utils_1.wrapSetImmediate)(async () => {
501
522
  RabbitMq.validateName('exchange', exchange);
502
- const channel = await this.assertChannel();
503
- await this.assertExchange(exchange);
523
+ const channel = await this.assertChannel({ connectionPurpose: consts_1.ConnectionPurpose.Publish });
524
+ await this.assertExchange(exchange, { connectionPurpose: consts_1.ConnectionPurpose.Publish });
504
525
  await channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
505
526
  });
506
527
  }
507
528
  async sendToQueue(queue, content, options, customHeaders) {
508
529
  try {
509
- await this.assertChannel();
530
+ await this.assertChannel({ connectionPurpose: consts_1.ConnectionPurpose.Publish });
510
531
  }
511
532
  catch (e) {
512
533
  logger_1.default.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
@@ -514,43 +535,49 @@ class RabbitMq {
514
535
  }
515
536
  try {
516
537
  RabbitMq.validateName('queue', queue);
517
- await this.assertQueue(queue, options);
538
+ await this.assertQueue(queue, consts_1.ConnectionPurpose.Publish, options);
518
539
  }
519
540
  catch (e) {
520
541
  logger_1.default.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
521
542
  throw e;
522
543
  }
523
544
  try {
524
- const res = await this.channel?.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
545
+ const res = await this.publishChannel?.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
525
546
  debug(`rabbit: sending to queue ${queue}`, { res });
526
547
  return res;
527
548
  }
528
549
  catch (e) {
529
- const isConnected = await this.isConnected();
530
- logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
550
+ logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queue}`, { e });
531
551
  throw e;
532
552
  }
533
553
  }
534
554
  async isConnected() {
535
- const connection = await this.getConnection();
536
- const isConnected = connection.isConnected();
537
- if (!isConnected) {
538
- logger_1.default.error('rabbit: isConnected - false');
539
- return false;
540
- }
541
- const channel = await this.assertChannel();
542
- try {
543
- await Promise.all([
544
- channel.waitForConnect(),
545
- ...this.consumers.map((c) => channel.checkQueue(c.queue)),
546
- ]);
547
- }
548
- catch (e) {
549
- logger_1.default.error('rabbit: isConnected - false');
550
- return false;
551
- }
552
- logger_1.default.info('rabbit: isConnected - true');
553
- return true;
555
+ debug('rabbit: start is connected');
556
+ const isEachConnectionConnected = await Promise.all(Object.entries(this.connectionsMap).map(async ([connectionPurpose, connectionData]) => {
557
+ const { connection } = connectionData;
558
+ debug('rabbit: is connected inside map', { connection, connectionPurpose });
559
+ const isConnected = connection?.isConnected();
560
+ if (!isConnected) {
561
+ logger_1.default.error('rabbit: isConnected - false', { connectionPurpose });
562
+ return false;
563
+ }
564
+ if (connectionPurpose === consts_1.ConnectionPurpose.Publish) {
565
+ const channel = await this.assertChannel({ connectionPurpose: connectionPurpose });
566
+ try {
567
+ await Promise.all([
568
+ channel.waitForConnect(),
569
+ ...this.consumers.map((c) => channel.checkQueue(c.queue)),
570
+ ]);
571
+ }
572
+ catch (e) {
573
+ logger_1.default.error('rabbit: isConnected - false');
574
+ return false;
575
+ }
576
+ }
577
+ logger_1.default.info('rabbit: isConnected - true');
578
+ return true;
579
+ }));
580
+ return isEachConnectionConnected.every((isConnected) => isConnected === true);
554
581
  }
555
582
  async gracefulShutdown(signal) {
556
583
  const tagsNumber = this.consumersTags.length;
@@ -6,8 +6,6 @@ export declare const USER_TRACING_HEADER = "x-af-user-id";
6
6
  export declare const AUTOMATION_ID_HEADER = "x-af-automation-id";
7
7
  export declare const USER_OBJECT = "userObject";
8
8
  export declare const DEFAULT_USE_CONSUME_WITH_LOCK = false;
9
- export declare const CONNECTION_CREATED_CONST = "connectionCreated";
10
- export declare const CONNECTION_FAILED_CONST = "connectionFailed";
11
9
  export declare const DEFAULT_OPTIONS: {
12
10
  limit: number;
13
11
  retries: number;
@@ -17,3 +15,7 @@ export declare const DEFAULT_OPTIONS: {
17
15
  auditContext: null;
18
16
  enableRabbitTrace: boolean;
19
17
  };
18
+ export declare enum ConnectionPurpose {
19
+ Consume = "consume",
20
+ Publish = "publish"
21
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_OPTIONS = exports.CONNECTION_FAILED_CONST = exports.CONNECTION_CREATED_CONST = exports.DEFAULT_USE_CONSUME_WITH_LOCK = exports.USER_OBJECT = exports.AUTOMATION_ID_HEADER = exports.USER_TRACING_HEADER = exports.TRACING_HEADER = exports.RETRY_HEADER = exports.DEFAULT_LOCK_TIMEOUT = exports.DEFAULT_DEAD_TTL_TWO_DAYS = void 0;
3
+ exports.ConnectionPurpose = exports.DEFAULT_OPTIONS = exports.DEFAULT_USE_CONSUME_WITH_LOCK = exports.USER_OBJECT = exports.AUTOMATION_ID_HEADER = exports.USER_TRACING_HEADER = exports.TRACING_HEADER = exports.RETRY_HEADER = exports.DEFAULT_LOCK_TIMEOUT = exports.DEFAULT_DEAD_TTL_TWO_DAYS = void 0;
4
4
  exports.DEFAULT_DEAD_TTL_TWO_DAYS = 60000 * 60 * 12;
5
5
  exports.DEFAULT_LOCK_TIMEOUT = 1000 * 5;
6
6
  exports.RETRY_HEADER = 'x-retry-count';
@@ -9,8 +9,6 @@ exports.USER_TRACING_HEADER = 'x-af-user-id';
9
9
  exports.AUTOMATION_ID_HEADER = 'x-af-automation-id';
10
10
  exports.USER_OBJECT = 'userObject';
11
11
  exports.DEFAULT_USE_CONSUME_WITH_LOCK = false;
12
- exports.CONNECTION_CREATED_CONST = 'connectionCreated';
13
- exports.CONNECTION_FAILED_CONST = 'connectionFailed';
14
12
  exports.DEFAULT_OPTIONS = {
15
13
  limit: 1,
16
14
  retries: 1,
@@ -20,3 +18,8 @@ exports.DEFAULT_OPTIONS = {
20
18
  auditContext: null,
21
19
  enableRabbitTrace: false,
22
20
  };
21
+ var ConnectionPurpose;
22
+ (function (ConnectionPurpose) {
23
+ ConnectionPurpose["Consume"] = "consume";
24
+ ConnectionPurpose["Publish"] = "publish";
25
+ })(ConnectionPurpose = exports.ConnectionPurpose || (exports.ConnectionPurpose = {}));
@@ -1,3 +1,4 @@
1
+ import { AmqpConnectionManager } from 'amqp-connection-manager';
1
2
  import { ConsumeMessage, Options, Replies } from 'amqplib';
2
3
  export interface ExchangesCache {
3
4
  [key: string]: any;
@@ -41,3 +42,9 @@ export type AfConsumer = {
41
42
  options: ConsumeOptions | undefined;
42
43
  };
43
44
  export declare const CONSUMER_DEFAULT_OPTIONS: Options.Consume;
45
+ export type ConnectionData = {
46
+ connection: AmqpConnectionManager | null;
47
+ creatingConnection: boolean;
48
+ connectionCreatedEventName: string;
49
+ connectionFailedEventName: string;
50
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/rabbit",
3
- "version": "3.2.25",
3
+ "version": "3.2.26-beta.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "engines": {
@@ -18,7 +18,7 @@
18
18
  "dev": "nodemon"
19
19
  },
20
20
  "dependencies": {
21
- "@autofleet/zehut": "^3.1.2",
21
+ "@autofleet/zehut": "^3.0.10",
22
22
  "amqp-connection-manager": "4.1.9",
23
23
  "amqplib": "0.10.3",
24
24
  "bluebird": "^3.7.2",
package/src/index.ts CHANGED
@@ -20,8 +20,7 @@ import getRedisInstance, { RedisConfig } from './lib/redis';
20
20
  import { assertExchangeFanout, rand, wrapSetImmediate } from './lib/utils';
21
21
  import {
22
22
  AUTOMATION_ID_HEADER,
23
- CONNECTION_CREATED_CONST,
24
- CONNECTION_FAILED_CONST,
23
+ ConnectionPurpose,
25
24
  DEFAULT_LOCK_TIMEOUT,
26
25
  DEFAULT_OPTIONS,
27
26
  RETRY_HEADER,
@@ -40,13 +39,13 @@ import {
40
39
  CONSUMER_DEFAULT_OPTIONS,
41
40
  QueueSetupPromisesDictionary,
42
41
  AssertExchangePromisesDictionary,
42
+ ConnectionData,
43
43
  } from './lib/types';
44
44
 
45
45
  // const debug = nodeDebug('af-rabbitmq')
46
46
  const debug = logger.debug.bind(logger);
47
47
 
48
48
  const PUBLISH_TIMEOUT = 1000 * 10;
49
-
50
49
  export interface IAfRabbitMq {
51
50
  ack: any;
52
51
  nack: any;
@@ -86,11 +85,13 @@ type newChannelOpts = {
86
85
  name?: string;
87
86
  onClose?: null | ((args: any | null) => void);
88
87
  options?: CreateChannelOpts | undefined;
88
+ connectionPurpose?: ConnectionPurpose,
89
89
  };
90
90
 
91
91
  type assertChannelOpts = {
92
92
  channelName?: string;
93
93
  force?: boolean;
94
+ connectionPurpose?: ConnectionPurpose;
94
95
  }
95
96
 
96
97
  type AfConsumer = {
@@ -102,7 +103,7 @@ type AfConsumer = {
102
103
  const HEARTBEAT = '60';
103
104
 
104
105
  class RabbitMq implements IAfRabbitMq {
105
- static parseMsg(msg: any) : any {
106
+ static parseMsg(msg: any): any {
106
107
  let { content } = msg;
107
108
  content = content.toString();
108
109
 
@@ -143,18 +144,19 @@ class RabbitMq implements IAfRabbitMq {
143
144
 
144
145
  RECONNECT_MSG = 'rabbit: connection disconnect - reconnecting';
145
146
 
146
- channel: ChannelWrapper | null;
147
+ publishChannel: ChannelWrapper | null;
147
148
 
148
149
  publishChannelSetupPromise: Promise<ChannelWrapper> | null;
149
150
 
150
- blockReconnect: boolean | null | undefined
151
+ blockReconnect: boolean | null | undefined;
151
152
 
152
- connection: AmqpConnectionManager | null | undefined
153
+ connectionsMap: {
154
+ [ConnectionPurpose.Consume]: ConnectionData;
155
+ [ConnectionPurpose.Publish]: ConnectionData;
156
+ };
153
157
 
154
158
  em: EventEmitter;
155
159
 
156
- creatingConnection: boolean;
157
-
158
160
  exchanges: ExchangesCache;
159
161
 
160
162
  queues: QueuesCache;
@@ -176,10 +178,22 @@ class RabbitMq implements IAfRabbitMq {
176
178
 
177
179
  constructor(options?: AfRabbitOptions, redisConfig?: RedisConfig) {
178
180
  this.em = new EventEmitter();
179
- this.channel = null;
181
+ this.publishChannel = null;
180
182
  this.publishChannelSetupPromise = null;
181
- this.connection = null;
182
- this.creatingConnection = false;
183
+ this.connectionsMap = {
184
+ [ConnectionPurpose.Consume]: {
185
+ connection: null,
186
+ creatingConnection: false,
187
+ connectionCreatedEventName: 'consumeConnectionCreated',
188
+ connectionFailedEventName: 'consumeConnectionFailed',
189
+ },
190
+ [ConnectionPurpose.Publish]: {
191
+ connection: null,
192
+ creatingConnection: false,
193
+ connectionCreatedEventName: 'publishConnectionCreated',
194
+ connectionFailedEventName: 'publishConnectionFailed',
195
+ },
196
+ };
183
197
  this.exchanges = {};
184
198
  this.queues = {};
185
199
  this.queueSetupPromises = {};
@@ -216,7 +230,7 @@ class RabbitMq implements IAfRabbitMq {
216
230
  return false;
217
231
  }
218
232
 
219
- public ack = (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp = false, releaseLock = null) => async (userMsg: ConsumeMessage) : Promise<any> => {
233
+ public ack = (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp = false, releaseLock = null) => async (userMsg: ConsumeMessage): Promise<any> => {
220
234
  if (msg) {
221
235
  debug('rabbit acking message', { deliveryTag: msg.fields.deliveryTag });
222
236
  await channel.ack(msg);
@@ -242,8 +256,8 @@ class RabbitMq implements IAfRabbitMq {
242
256
  userMsg: ConsumeMessageOrNull,
243
257
  {
244
258
  skipRetry = false,
245
- }: NackOptions = { },
246
- ) : Promise<any> => {
259
+ }: NackOptions = {},
260
+ ): Promise<any> => {
247
261
  await this.unlockRedisIfNeeded(releaseLock);
248
262
  if (channel && msg) {
249
263
  if (
@@ -277,30 +291,36 @@ class RabbitMq implements IAfRabbitMq {
277
291
  }
278
292
  }
279
293
 
280
- async getConnection() {
281
- return new Promise<AmqpConnectionManager>(async (resolve, reject) => {
294
+ async getConnection(connectionPurpose: ConnectionPurpose) {
295
+ return new Promise<AmqpConnectionManager | undefined | null>(async (resolve, reject) => {
296
+ const {
297
+ connection,
298
+ creatingConnection,
299
+ connectionCreatedEventName,
300
+ connectionFailedEventName,
301
+ } = this.connectionsMap[connectionPurpose];
282
302
  if (this.blockReconnect) {
283
303
  debug('rabbit: block reconnect');
284
304
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
285
305
  // @ts-ignore
286
306
  return resolve();
287
307
  }
288
- if (this.connection !== null) {
289
- if (this.options?.disableReconnect || this.connection?.isConnected()) {
308
+ if (connection !== null) {
309
+ if (this.options?.disableReconnect || connection?.isConnected()) {
290
310
  debug('rabbit: connection - is connected');
291
311
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
292
312
  // @ts-ignore
293
- return resolve(this.connection);
313
+ return resolve(connection);
294
314
  }
295
315
  debug('rabbit: connection - reconnecting');
296
316
  }
297
- if (this.creatingConnection) {
317
+ if (creatingConnection) {
298
318
  debug('rabbit: creating connection emi');
299
- this.em.once(CONNECTION_CREATED_CONST, resolve);
300
- this.em.once(CONNECTION_FAILED_CONST, reject);
319
+ this.em.once(connectionCreatedEventName, resolve);
320
+ this.em.once(connectionFailedEventName, reject);
301
321
  return;
302
322
  }
303
- this.creatingConnection = true;
323
+ this.connectionsMap[connectionPurpose].creatingConnection = true;
304
324
  let isResolved = false;
305
325
 
306
326
  // It is import to use it as a function and not as a variable
@@ -317,32 +337,33 @@ class RabbitMq implements IAfRabbitMq {
317
337
  };
318
338
 
319
339
  const defaultUrls = findServers();
320
- const connection: AmqpConnectionManager = await connect(defaultUrls, {
340
+ const newConnection: AmqpConnectionManager = await connect(defaultUrls, {
321
341
  findServers,
322
342
  });
323
343
 
324
- this.connection = connection;
325
- this.connection.on('error', (err) => {
344
+ this.connectionsMap[connectionPurpose].connection = newConnection;
345
+ logger.info(`rabbit: created new connection ${connectionPurpose}`);
346
+
347
+ newConnection.on('error', (err) => {
326
348
  logger.error('rabbit: connection error', { err });
327
349
  if (!isResolved) {
328
350
  isResolved = true;
329
351
  reject(err);
330
- this.em.emit(CONNECTION_FAILED_CONST, err);
352
+ this.em.emit(connectionFailedEventName, err);
331
353
  }
332
354
  });
333
355
 
334
- this.connection.on('connectFailed', (err) => {
356
+ newConnection.on('connectFailed', (err) => {
335
357
  this.consumersTags = [];
336
358
  logger.error('rabbit: connection connectFailed', { err });
337
359
  if (!isResolved) {
338
360
  isResolved = true;
339
361
  reject(err);
340
- this.em.emit(CONNECTION_FAILED_CONST, err);
362
+ this.em.emit(connectionFailedEventName, err);
341
363
  }
342
364
  });
343
365
 
344
- this.connection.on('disconnect', ({ err }) => {
345
- // this.channel = null;
366
+ newConnection.on('disconnect', ({ err }) => {
346
367
  this.consumersTags = [];
347
368
  debug('rabbit: connection closed');
348
369
  if (this.options?.disableReconnect) {
@@ -352,25 +373,28 @@ class RabbitMq implements IAfRabbitMq {
352
373
  logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
353
374
  }
354
375
  });
355
-
356
- this.connection.once('connect', async () => {
357
- debug('rabbit: connection established');
358
- this.creatingConnection = false;
359
- this.em.emit(CONNECTION_CREATED_CONST, connection);
376
+ newConnection.once('connect', async () => {
377
+ this.connectionsMap[connectionPurpose].creatingConnection = false;
378
+ this.em.emit(connectionCreatedEventName, newConnection);
360
379
  isResolved = true;
361
- resolve(connection);
380
+ resolve(newConnection);
362
381
  });
363
382
  });
364
383
  }
365
384
 
366
- async getNewChannel({ name = rand().toString(), onClose = null, options = {} }: newChannelOpts = {}) {
367
- let connection!: AmqpConnectionManager;
385
+ async getNewChannel({
386
+ name = rand().toString(), onClose = null, options = {}, connectionPurpose = ConnectionPurpose.Consume,
387
+ }: newChannelOpts): Promise<ChannelWrapper> {
388
+ let connection!: AmqpConnectionManager | undefined | null;
368
389
  try {
369
- connection = await this.getConnection();
390
+ connection = await this.getConnection(connectionPurpose);
370
391
  } catch (e) {
371
392
  logger.error(`rabbit: error on get connection for new channel ${name} `, { e });
372
393
  throw e;
373
394
  }
395
+ if (!connection) {
396
+ throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
397
+ }
374
398
  const channel = connection.createChannel({ ...options });
375
399
  once(channel, 'close').then((args) => {
376
400
  logger.error(`rabbit: channel ${name} closed`);
@@ -386,19 +410,21 @@ class RabbitMq implements IAfRabbitMq {
386
410
  }
387
411
  }
388
412
 
389
- async assertChannel({ force = false } : assertChannelOpts = {}): Promise<ChannelWrapper> {
413
+ async assertChannel({ force = false, connectionPurpose = ConnectionPurpose.Consume }: assertChannelOpts): Promise<ChannelWrapper> {
414
+ debug('rabbit: start assert channel', { connectionPurpose, publishPromise: this.publishChannelSetupPromise, channel: this.publishChannel });
390
415
  if (!this.publishChannelSetupPromise) {
391
416
  this.publishChannelSetupPromise = new Promise<ChannelWrapper>(async (resolve, reject) => {
392
- if (this.channel && !force) {
393
- return resolve(this.channel);
417
+ if (this.publishChannel && !force) {
418
+ return resolve(this.publishChannel);
394
419
  }
395
420
 
396
421
  try {
397
- const channel = await this.getNewChannel({});
422
+ const channel = await this.getNewChannel({ connectionPurpose });
423
+ debug('rabbit: new channel got', { connectionPurpose, channel: this.publishChannel });
398
424
  channel.on('error', (err) => {
399
425
  logger.error('rabbit: channel error', { err });
400
426
  });
401
- this.channel = channel;
427
+ this.publishChannel = channel;
402
428
  resolve(channel);
403
429
  } catch (e) {
404
430
  reject(e);
@@ -408,9 +434,8 @@ class RabbitMq implements IAfRabbitMq {
408
434
  return this.publishChannelSetupPromise;
409
435
  }
410
436
 
411
- async assertExchange(exchangeName: string, options?: any) {
412
- const channel: ChannelWrapper = await this.assertChannel();
413
-
437
+ async assertExchange(exchangeName: string, options: any = { connectionPurpose: ConnectionPurpose.Consume }) {
438
+ const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose: options.connectionPurpose });
414
439
  if (this.exchanges[exchangeName]) {
415
440
  delete this.assertExchangePromises[exchangeName];
416
441
  return this.exchanges[exchangeName];
@@ -425,32 +450,33 @@ class RabbitMq implements IAfRabbitMq {
425
450
  return this.exchanges[exchangeName];
426
451
  }
427
452
 
428
- async getQueueLength(queue: string) {
453
+ async getQueueLength(queue: string, connectionPurpose: ConnectionPurpose = ConnectionPurpose.Consume): Promise<Replies.AssertQueue> {
429
454
  RabbitMq.validateName('queue', queue);
430
- const { channel } = this;
431
- if (!channel) {
455
+ const { connection } = this.connectionsMap[connectionPurpose];
456
+ const { publishChannel } = this;
457
+ if (!publishChannel) {
432
458
  throw new Error('channel is not defined');
433
459
  }
434
- debug('rabbit: getting queue length', { queue, connected: this.connection?.isConnected() });
435
- return channel?.checkQueue(queue);
460
+ debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
461
+ return publishChannel?.checkQueue(queue);
436
462
  }
437
463
 
438
- private async deleteQueue(queue: string) {
464
+ private async deleteQueue(queue: string, connectionPurpose: ConnectionPurpose) {
439
465
  RabbitMq.validateName('queue', queue);
440
- const channel: ChannelWrapper = await this.assertChannel();
466
+ const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
441
467
  logger.info('rabbit: deleting queue', { queue });
442
468
  const deleteQueueRes = await channel.deleteQueue(queue);
443
469
  debug('queue deleted', deleteQueueRes);
444
470
  return deleteQueueRes;
445
471
  }
446
472
 
447
- async bindQueue(queue: string, exchange: string) {
448
- const channel: ChannelWrapper = await this.assertChannel();
473
+ async bindQueue(queue: string, exchange: string, connectionPurpose: ConnectionPurpose) {
474
+ const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
449
475
  await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.bindQueue(queue, exchange, ''));
450
476
  return channel.bindQueue(queue, exchange, '');
451
477
  }
452
478
 
453
- async setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
479
+ async setupQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
454
480
  let queue: Replies.AssertQueue;
455
481
  const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
456
482
  const localeOptions = {
@@ -463,7 +489,7 @@ class RabbitMq implements IAfRabbitMq {
463
489
  },
464
490
  };
465
491
  try {
466
- const channel: ChannelWrapper = await this.assertChannel();
492
+ const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
467
493
  debug('assertQueue->channel.addSetup', { queueName });
468
494
  await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
469
495
  debug('assertQueue->channel.assertQueue', { queueName });
@@ -472,8 +498,8 @@ class RabbitMq implements IAfRabbitMq {
472
498
  logger.error('rabbit: assertQueue error', { queueName, options, error: e });
473
499
  if (!this.options?.dontRetryAssert) {
474
500
  debug('retrying assertQueue', { queueName });
475
- const channel = await this.assertChannel({ force: true });
476
- await this.deleteQueue(queueName);
501
+ const channel = await this.assertChannel({ force: true, connectionPurpose });
502
+ await this.deleteQueue(queueName, connectionPurpose);
477
503
 
478
504
  debug('retrying assertQueue->channel.addSetup', { queueName });
479
505
  await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
@@ -503,7 +529,8 @@ class RabbitMq implements IAfRabbitMq {
503
529
  return false;
504
530
  }
505
531
 
506
- async assertQueue(queueName: string, options?: Options.AssertQueue): Promise<any> {
532
+ async assertQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue) {
533
+ debug('rabbit: start assert queue', { connectionPurpose, queueName });
507
534
  RabbitMq.validateName('queue', queueName);
508
535
  if (this.queues[queueName]) {
509
536
  delete this.queueSetupPromises[queueName];
@@ -514,12 +541,13 @@ class RabbitMq implements IAfRabbitMq {
514
541
  return this.queueSetupPromises[queueName];
515
542
  }
516
543
 
517
- this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
544
+ this.queueSetupPromises[queueName] = this.setupQueue(queueName, connectionPurpose, options);
545
+ debug('rabbit: done assert queue', { connectionPurpose, queueName });
518
546
  return this.queueSetupPromises[queueName];
519
547
  }
520
548
 
521
549
  private saveConsumer(queue: string, callback: CallbackFunction, options: ConsumeOptions | undefined) {
522
- const isConsumerExist :boolean = this.consumers.some((consumer) => consumer.queue === queue);
550
+ const isConsumerExist: boolean = this.consumers.some((consumer) => consumer.queue === queue);
523
551
  if (!isConsumerExist) {
524
552
  logger.info(`rabbit: consumer: ${queue} saved in consumer array`);
525
553
  this.consumers.push({
@@ -568,9 +596,9 @@ class RabbitMq implements IAfRabbitMq {
568
596
  }
569
597
  logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
570
598
  }
571
- const channel = await this.getNewChannel({});
599
+ const channel = await this.getNewChannel({ connectionPurpose: ConnectionPurpose.Consume });
572
600
  return channel.addSetup(async (confirmChannel: ConfirmChannel) => {
573
- const q = await this.assertQueue(queue, optionsWithDefaults);
601
+ const q = await this.assertQueue(queue, ConnectionPurpose.Consume, optionsWithDefaults);
574
602
  await confirmChannel.prefetch(limit, false);
575
603
  const { consumerTag } = await confirmChannel.consume(
576
604
  queue,
@@ -658,19 +686,19 @@ class RabbitMq implements IAfRabbitMq {
658
686
  });
659
687
  }
660
688
 
661
- async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions) : Promise<any> {
689
+ async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
662
690
  const optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options };
663
691
  RabbitMq.validateName('exchange', exchange);
664
692
  RabbitMq.validateName('queue', queue);
665
693
  const { limit, deadMessageTtl } = optionsWithDefaults;
666
694
  await this.saveConsumer(queue, callback, options);
667
- const channel: ChannelWrapper = await this.getNewChannel({ name: `consume-exchange-${exchange}-queue-${queue}` });
695
+ const channel: ChannelWrapper = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}` });
668
696
 
669
697
  return channel.addSetup(async (c: ConfirmChannel) => {
670
698
  const assertExchange = await assertExchangeFanout(c, exchange);
671
699
  await c.assertQueue(queue);
672
700
  this.exchanges[exchange] = assertExchange;
673
- await c.prefetch(limit, false);
701
+ await c.prefetch(limit, true);
674
702
  return Promise.all([
675
703
  c.bindQueue(queue, exchange, ''),
676
704
  this.consume(
@@ -682,11 +710,12 @@ class RabbitMq implements IAfRabbitMq {
682
710
  });
683
711
  }
684
712
 
685
- async publish(exchange: string, content: any, customHeaders?: any) : Promise<boolean> {
713
+ async publish(exchange: string, content: any, customHeaders?: any): Promise<boolean> {
714
+ debug('rabbit: start publish msg');
686
715
  return wrapSetImmediate(async () => {
687
716
  RabbitMq.validateName('exchange', exchange);
688
- const channel: ChannelWrapper = await this.assertChannel();
689
- await this.assertExchange(exchange);
717
+ const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose: ConnectionPurpose.Publish });
718
+ await this.assertExchange(exchange, { connectionPurpose: ConnectionPurpose.Publish });
690
719
  await channel.publish(exchange, '',
691
720
  Buffer.from(JSON.stringify(content)),
692
721
  RabbitMq.getPublishOptions(customHeaders));
@@ -700,7 +729,7 @@ class RabbitMq implements IAfRabbitMq {
700
729
  customHeaders?: any,
701
730
  ): Promise<boolean | undefined> {
702
731
  try {
703
- await this.assertChannel();
732
+ await this.assertChannel({ connectionPurpose: ConnectionPurpose.Publish });
704
733
  } catch (e) {
705
734
  logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
706
735
  throw e;
@@ -708,47 +737,55 @@ class RabbitMq implements IAfRabbitMq {
708
737
 
709
738
  try {
710
739
  RabbitMq.validateName('queue', queue);
711
- await this.assertQueue(queue, options);
740
+ await this.assertQueue(queue, ConnectionPurpose.Publish, options);
712
741
  } catch (e) {
713
742
  logger.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
714
743
  throw e;
715
744
  }
716
745
 
717
746
  try {
718
- const res = await this.channel?.sendToQueue(queue,
747
+ const res = await this.publishChannel?.sendToQueue(queue,
719
748
  Buffer.from(JSON.stringify(content)),
720
749
  RabbitMq.getPublishOptions(customHeaders));
721
750
  debug(`rabbit: sending to queue ${queue}`, { res });
722
751
  return res;
723
752
  } catch (e) {
724
- const isConnected = await this.isConnected();
725
- logger.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
753
+ logger.error(`rabbit sendToQueue: failed to send to queue ${queue}`, { e });
726
754
  throw e;
727
755
  }
728
756
  }
729
757
 
730
- async isConnected() : Promise<boolean> {
731
- const connection = await this.getConnection();
732
- const isConnected = connection.isConnected();
733
- if (!isConnected) {
734
- logger.error('rabbit: isConnected - false');
735
- return false;
736
- }
737
- const channel: any = await this.assertChannel();
738
- try {
739
- await Promise.all([
740
- channel.waitForConnect(),
741
- ...this.consumers.map((c: AfConsumer) => channel.checkQueue(c.queue)),
742
- ]);
743
- } catch (e) {
744
- logger.error('rabbit: isConnected - false');
745
- return false;
746
- }
747
- logger.info('rabbit: isConnected - true');
748
- return true;
758
+ async isConnected(): Promise<boolean> {
759
+ debug('rabbit: start is connected');
760
+ const isEachConnectionConnected = await Promise.all(
761
+ Object.entries(this.connectionsMap).map(async ([connectionPurpose, connectionData]) => {
762
+ const { connection } = connectionData;
763
+ debug('rabbit: is connected inside map', { connection, connectionPurpose });
764
+ const isConnected = connection?.isConnected();
765
+ if (!isConnected) {
766
+ logger.error('rabbit: isConnected - false', { connectionPurpose });
767
+ return false;
768
+ }
769
+ if (connectionPurpose === ConnectionPurpose.Publish) {
770
+ const channel: any = await this.assertChannel({ connectionPurpose: connectionPurpose as ConnectionPurpose });
771
+ try {
772
+ await Promise.all([
773
+ channel.waitForConnect(),
774
+ ...this.consumers.map((c: AfConsumer) => channel.checkQueue(c.queue)),
775
+ ]);
776
+ } catch (e) {
777
+ logger.error('rabbit: isConnected - false');
778
+ return false;
779
+ }
780
+ }
781
+ logger.info('rabbit: isConnected - true');
782
+ return true;
783
+ }),
784
+ );
785
+ return isEachConnectionConnected.every((isConnected) => isConnected === true);
749
786
  }
750
787
 
751
- async gracefulShutdown(signal: string) : Promise<void> {
788
+ async gracefulShutdown(signal: string): Promise<void> {
752
789
  const tagsNumber = this.consumersTags.length;
753
790
  logger.info(`rabbit: [gracefully-shutdown] received ${signal}! canceling #${tagsNumber} tags...`);
754
791
  const cancelTagPromises = this.consumersTags.map(([channel, tag]) => channel.cancel(tag));
package/src/lib/consts.ts CHANGED
@@ -6,8 +6,6 @@ export const USER_TRACING_HEADER = 'x-af-user-id';
6
6
  export const AUTOMATION_ID_HEADER = 'x-af-automation-id';
7
7
  export const USER_OBJECT = 'userObject';
8
8
  export const DEFAULT_USE_CONSUME_WITH_LOCK = false;
9
- export const CONNECTION_CREATED_CONST = 'connectionCreated';
10
- export const CONNECTION_FAILED_CONST = 'connectionFailed';
11
9
  export const DEFAULT_OPTIONS = {
12
10
  limit: 1,
13
11
  retries: 1,
@@ -17,3 +15,8 @@ export const DEFAULT_OPTIONS = {
17
15
  auditContext: null,
18
16
  enableRabbitTrace: false,
19
17
  };
18
+
19
+ export enum ConnectionPurpose {
20
+ Consume = 'consume',
21
+ Publish = 'publish',
22
+ }
package/src/lib/types.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { AmqpConnectionManager } from 'amqp-connection-manager';
1
2
  import { ConsumeMessage, Options, Replies } from 'amqplib';
2
3
 
3
4
  export interface ExchangesCache {
@@ -59,3 +60,10 @@ export const CONSUMER_DEFAULT_OPTIONS: Options.Consume = {
59
60
  [HA_PROMOTE_ON_SHUTDOWN]: 'always',
60
61
  },
61
62
  };
63
+
64
+ export type ConnectionData = {
65
+ connection: AmqpConnectionManager | null;
66
+ creatingConnection: boolean;
67
+ connectionCreatedEventName: string;
68
+ connectionFailedEventName: string;
69
+ };