@autofleet/rabbit 3.2.21 → 3.2.22-beta.1

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,7 @@ 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 { CallbackFunction, ConsumeMessageOrNull, ConsumeOptions, CustomMessageHeaders, QueuesCache, RedisLockType, ExchangesCache, QueueSetupPromisesDictionary, ConnectionPurpose, ConnectionData } from './lib/types';
7
7
  export interface IAfRabbitMq {
8
8
  ack: any;
9
9
  nack: any;
@@ -37,10 +37,12 @@ type newChannelOpts = {
37
37
  name?: string;
38
38
  onClose?: null | ((args: any | null) => void);
39
39
  options?: CreateChannelOpts | undefined;
40
+ connectionPurpose: ConnectionPurpose;
40
41
  };
41
42
  type assertChannelOpts = {
42
43
  channelName?: string;
43
44
  force?: boolean;
45
+ connectionPurpose: ConnectionPurpose;
44
46
  };
45
47
  declare class RabbitMq implements IAfRabbitMq {
46
48
  static parseMsg(msg: any): any;
@@ -60,13 +62,14 @@ declare class RabbitMq implements IAfRabbitMq {
60
62
  channel: ChannelWrapper | null;
61
63
  publishChannelSetupPromise: Promise<ChannelWrapper> | null;
62
64
  blockReconnect: boolean | null | undefined;
63
- connection: AmqpConnectionManager | null | undefined;
65
+ connectionsMap: {
66
+ [ConnectionPurpose.Consume]: ConnectionData;
67
+ [ConnectionPurpose.Publish]: ConnectionData;
68
+ };
64
69
  em: EventEmitter;
65
- creatingConnection: boolean;
66
70
  exchanges: ExchangesCache;
67
71
  queues: QueuesCache;
68
72
  queueSetupPromises: QueueSetupPromisesDictionary;
69
- assertExchangePromises: AssertExchangePromisesDictionary;
70
73
  options: AfRabbitOptions | undefined;
71
74
  redisClient: any;
72
75
  redisLock?: RedisLockType;
@@ -77,16 +80,15 @@ declare class RabbitMq implements IAfRabbitMq {
77
80
  private shouldConsumeMessageByTimestamp;
78
81
  ack: (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp?: boolean, releaseLock?: null) => (userMsg: ConsumeMessage) => Promise<any>;
79
82
  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>;
83
+ getConnection(connectionPurpose: ConnectionPurpose): Promise<AmqpConnectionManager | null | undefined>;
84
+ getNewChannel({ name, onClose, options, connectionPurpose, }: newChannelOpts): Promise<ChannelWrapper>;
85
+ assertChannel({ force, connectionPurpose }: assertChannelOpts): Promise<ChannelWrapper>;
83
86
  assertExchange(exchangeName: string, options?: any): Promise<any>;
84
- getQueueLength(queue: string): Promise<Replies.AssertQueue>;
87
+ getQueueLength(queue: string, connectionPurpose?: ConnectionPurpose): Promise<Replies.AssertQueue>;
85
88
  private deleteQueue;
86
- bindQueue(queue: string, exchange: string): Promise<void>;
87
- setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue>;
88
- static shouldUseQuorum(queueName: string): boolean;
89
- assertQueue(queueName: string, options?: Options.AssertQueue): Promise<any>;
89
+ bindQueue(queue: string, exchange: string, connectionPurpose: ConnectionPurpose): Promise<void>;
90
+ setupQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue): Promise<Replies.AssertQueue>;
91
+ assertQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue): Promise<any>;
90
92
  private saveConsumer;
91
93
  consume(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any>;
92
94
  private lockRedisIfNeeded;
@@ -95,7 +97,7 @@ declare class RabbitMq implements IAfRabbitMq {
95
97
  consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any>;
96
98
  publish(exchange: string, content: any, customHeaders?: any): Promise<boolean>;
97
99
  sendToQueue(queue: string, content: any, options?: any, customHeaders?: any): Promise<boolean | undefined>;
98
- isConnected(): Promise<boolean>;
100
+ isConnected(connectionPurpose: ConnectionPurpose): Promise<boolean>;
99
101
  gracefulShutdown(signal: string): Promise<void>;
100
102
  }
101
103
  export default RabbitMq;
package/dist/index.js CHANGED
@@ -118,12 +118,13 @@ class RabbitMq {
118
118
  this.em = new events_1.EventEmitter();
119
119
  this.channel = null;
120
120
  this.publishChannelSetupPromise = null;
121
- this.connection = null;
122
- this.creatingConnection = false;
121
+ this.connectionsMap = {
122
+ [types_1.ConnectionPurpose.Consume]: { connection: null, creatingConnection: false },
123
+ [types_1.ConnectionPurpose.Publish]: { connection: null, creatingConnection: false },
124
+ };
123
125
  this.exchanges = {};
124
126
  this.queues = {};
125
127
  this.queueSetupPromises = {};
126
- this.assertExchangePromises = {};
127
128
  this.consumers = [];
128
129
  this.options = options;
129
130
  this.redisClient = redisConfig && (0, redis_1.default)(redisConfig);
@@ -141,30 +142,31 @@ class RabbitMq {
141
142
  });
142
143
  }
143
144
  }
144
- async getConnection() {
145
+ async getConnection(connectionPurpose) {
145
146
  return new Promise(async (resolve, reject) => {
147
+ const { connection, creatingConnection } = this.connectionsMap[connectionPurpose];
146
148
  if (this.blockReconnect) {
147
149
  debug('rabbit: block reconnect');
148
150
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
149
151
  // @ts-ignore
150
152
  return resolve();
151
153
  }
152
- if (this.connection !== null) {
153
- if (this.options?.disableReconnect || this.connection?.isConnected()) {
154
+ if (connection !== null) {
155
+ if (this.options?.disableReconnect || connection?.isConnected()) {
154
156
  debug('rabbit: connection - is connected');
155
157
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
156
158
  // @ts-ignore
157
- return resolve(this.connection);
159
+ return resolve(connection);
158
160
  }
159
161
  debug('rabbit: connection - reconnecting');
160
162
  }
161
- if (this.creatingConnection) {
163
+ if (creatingConnection) {
162
164
  debug('rabbit: creating connection emi');
163
165
  this.em.once(consts_1.CONNECTION_CREATED_CONST, resolve);
164
166
  this.em.once(consts_1.CONNECTION_FAILED_CONST, reject);
165
167
  return;
166
168
  }
167
- this.creatingConnection = true;
169
+ this.connectionsMap[connectionPurpose].creatingConnection = true;
168
170
  let isResolved = false;
169
171
  // It is import to use it as a function and not as a variable
170
172
  // because of k8s changes the env variables
@@ -177,11 +179,15 @@ class RabbitMq {
177
179
  return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
178
180
  };
179
181
  const defaultUrls = findServers();
180
- const connection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
182
+ const newConnection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
181
183
  findServers,
182
184
  });
183
- this.connection = connection;
184
- this.connection.on('error', (err) => {
185
+ if (!newConnection) {
186
+ logger_1.default.error('rabbit: couldnt create a connection');
187
+ return resolve(connection);
188
+ }
189
+ this.connectionsMap[connectionPurpose].connection = newConnection;
190
+ newConnection.on('error', (err) => {
185
191
  logger_1.default.error('rabbit: connection error', { err });
186
192
  if (!isResolved) {
187
193
  isResolved = true;
@@ -189,7 +195,7 @@ class RabbitMq {
189
195
  this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
190
196
  }
191
197
  });
192
- this.connection.on('connectFailed', (err) => {
198
+ newConnection.on('connectFailed', (err) => {
193
199
  this.consumersTags = [];
194
200
  logger_1.default.error('rabbit: connection connectFailed', { err });
195
201
  if (!isResolved) {
@@ -198,8 +204,7 @@ class RabbitMq {
198
204
  this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
199
205
  }
200
206
  });
201
- this.connection.on('disconnect', ({ err }) => {
202
- // this.channel = null;
207
+ newConnection.on('disconnect', ({ err }) => {
203
208
  this.consumersTags = [];
204
209
  debug('rabbit: connection closed');
205
210
  if (this.options?.disableReconnect) {
@@ -210,24 +215,27 @@ class RabbitMq {
210
215
  logger_1.default.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
211
216
  }
212
217
  });
213
- this.connection.once('connect', async () => {
218
+ newConnection.once('connect', async () => {
214
219
  debug('rabbit: connection established');
215
- this.creatingConnection = false;
216
- this.em.emit(consts_1.CONNECTION_CREATED_CONST, connection);
220
+ this.connectionsMap[connectionPurpose].creatingConnection = false;
221
+ this.em.emit(consts_1.CONNECTION_CREATED_CONST, newConnection);
217
222
  isResolved = true;
218
- resolve(connection);
223
+ resolve(newConnection);
219
224
  });
220
225
  });
221
226
  }
222
- async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {} } = {}) {
227
+ async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {}, connectionPurpose = types_1.ConnectionPurpose.Consume, }) {
223
228
  let connection;
224
229
  try {
225
- connection = await this.getConnection();
230
+ connection = await this.getConnection(connectionPurpose);
226
231
  }
227
232
  catch (e) {
228
233
  logger_1.default.error(`rabbit: error on get connection for new channel ${name} `, { e });
229
234
  throw e;
230
235
  }
236
+ if (!connection) {
237
+ throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
238
+ }
231
239
  const channel = connection.createChannel({ ...options });
232
240
  (0, events_1.once)(channel, 'close').then((args) => {
233
241
  logger_1.default.error(`rabbit: channel ${name} closed`);
@@ -243,14 +251,14 @@ class RabbitMq {
243
251
  throw err;
244
252
  }
245
253
  }
246
- async assertChannel({ force = false } = {}) {
254
+ async assertChannel({ force = false, connectionPurpose = types_1.ConnectionPurpose.Consume }) {
247
255
  if (!this.publishChannelSetupPromise) {
248
256
  this.publishChannelSetupPromise = new Promise(async (resolve, reject) => {
249
257
  if (this.channel && !force) {
250
258
  return resolve(this.channel);
251
259
  }
252
260
  try {
253
- const channel = await this.getNewChannel({});
261
+ const channel = await this.getNewChannel({ connectionPurpose });
254
262
  channel.on('error', (err) => {
255
263
  logger_1.default.error('rabbit: channel error', { err });
256
264
  });
@@ -264,90 +272,66 @@ class RabbitMq {
264
272
  }
265
273
  return this.publishChannelSetupPromise;
266
274
  }
267
- async assertExchange(exchangeName, options) {
268
- const channel = await this.assertChannel();
275
+ async assertExchange(exchangeName, options = { connectionPurpose: types_1.ConnectionPurpose.Consume }) {
276
+ const channel = await this.assertChannel({ connectionPurpose: options.connectionPurpose });
269
277
  if (this.exchanges[exchangeName]) {
270
- delete this.assertExchangePromises[exchangeName];
271
278
  return this.exchanges[exchangeName];
272
279
  }
273
- if (this.assertExchangePromises[exchangeName]) {
274
- return this.assertExchangePromises[exchangeName];
275
- }
276
- this.assertExchangePromises[exchangeName] = (0, utils_1.assertExchangeFanout)(channel, exchangeName);
277
- this.exchanges[exchangeName] = await this.assertExchangePromises[exchangeName];
278
- return this.exchanges[exchangeName];
280
+ const exchange = await (0, utils_1.assertExchangeFanout)(channel, exchangeName);
281
+ this.exchanges[exchangeName] = exchange;
282
+ return exchange;
279
283
  }
280
- async getQueueLength(queue) {
284
+ async getQueueLength(queue, connectionPurpose = types_1.ConnectionPurpose.Consume) {
281
285
  RabbitMq.validateName('queue', queue);
286
+ const { connection } = this.connectionsMap[connectionPurpose];
282
287
  const { channel } = this;
283
288
  if (!channel) {
284
289
  throw new Error('channel is not defined');
285
290
  }
286
- debug('rabbit: getting queue length', { queue, connected: this.connection?.isConnected() });
291
+ debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
287
292
  return channel?.checkQueue(queue);
288
293
  }
289
- async deleteQueue(queue) {
294
+ async deleteQueue(queue, connectionPurpose) {
290
295
  RabbitMq.validateName('queue', queue);
291
- const channel = await this.assertChannel();
296
+ const channel = await this.assertChannel({ connectionPurpose });
292
297
  logger_1.default.info('rabbit: deleting queue', { queue });
293
298
  const deleteQueueRes = await channel.deleteQueue(queue);
294
299
  debug('queue deleted', deleteQueueRes);
295
300
  return deleteQueueRes;
296
301
  }
297
- async bindQueue(queue, exchange) {
298
- const channel = await this.assertChannel();
302
+ async bindQueue(queue, exchange, connectionPurpose) {
303
+ const channel = await this.assertChannel({ connectionPurpose });
299
304
  await channel.addSetup((setupChannel) => setupChannel.bindQueue(queue, exchange, ''));
300
305
  return channel.bindQueue(queue, exchange, '');
301
306
  }
302
- async setupQueue(queueName, options) {
307
+ async setupQueue(queueName, connectionPurpose, options) {
303
308
  let queue;
304
- const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
305
- const localeOptions = {
306
- ...options,
307
- durable: true,
308
- arguments: {
309
- ...options?.arguments,
310
- 'x-consumer-timeout': 1000 * 60 * 60 * 24,
311
- 'x-queue-type': shouldUseQuorum ? 'quorum' : 'classic',
312
- },
313
- };
314
309
  try {
315
- const channel = await this.assertChannel();
310
+ const channel = await this.assertChannel({ connectionPurpose });
316
311
  debug('assertQueue->channel.addSetup', { queueName });
317
- await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
312
+ await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, options));
318
313
  debug('assertQueue->channel.assertQueue', { queueName });
319
- queue = await channel.assertQueue(queueName, localeOptions);
314
+ queue = await channel.assertQueue(queueName, options);
320
315
  }
321
316
  catch (e) {
322
317
  logger_1.default.error('rabbit: assertQueue error', { queueName, options, error: e });
323
318
  if (!this.options?.dontRetryAssert) {
324
319
  debug('retrying assertQueue', { queueName });
325
- const channel = await this.assertChannel({ force: true });
326
- await this.deleteQueue(queueName);
320
+ const channel = await this.assertChannel({ force: true, connectionPurpose });
321
+ await this.deleteQueue(queueName, connectionPurpose);
327
322
  debug('retrying assertQueue->channel.addSetup', { queueName });
328
- await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
323
+ await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, options));
329
324
  debug('retrying assertQueue->channel.assertQueue', { queueName });
330
- queue = await channel.assertQueue(queueName, localeOptions);
325
+ queue = await channel.assertQueue(queueName, options);
331
326
  }
332
327
  else {
333
328
  throw e;
334
329
  }
335
330
  }
336
- this.queues[queueName] = queue;
331
+ this.queues[queueName] = queueName;
337
332
  return queue;
338
333
  }
339
- static shouldUseQuorum(queueName) {
340
- const envQuorumQueuesWhitelist = process.env.QUORUM_QUEUES_WHITELIST;
341
- if (envQuorumQueuesWhitelist === '*') {
342
- return true;
343
- }
344
- if (envQuorumQueuesWhitelist) {
345
- const whitelist = envQuorumQueuesWhitelist.split(',');
346
- return whitelist.includes(queueName);
347
- }
348
- return false;
349
- }
350
- async assertQueue(queueName, options) {
334
+ async assertQueue(queueName, connectionPurpose, options) {
351
335
  RabbitMq.validateName('queue', queueName);
352
336
  if (this.queues[queueName]) {
353
337
  delete this.queueSetupPromises[queueName];
@@ -356,7 +340,7 @@ class RabbitMq {
356
340
  if (this.queueSetupPromises[queueName]) {
357
341
  return this.queueSetupPromises[queueName];
358
342
  }
359
- this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
343
+ this.queueSetupPromises[queueName] = this.setupQueue(queueName, connectionPurpose, options);
360
344
  return this.queueSetupPromises[queueName];
361
345
  }
362
346
  saveConsumer(queue, callback, options) {
@@ -399,10 +383,10 @@ class RabbitMq {
399
383
  }
400
384
  logger_1.default.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
401
385
  }
402
- const channel = await this.getNewChannel({});
386
+ const channel = await this.getNewChannel({ connectionPurpose: types_1.ConnectionPurpose.Consume });
403
387
  return channel.addSetup(async (confirmChannel) => {
404
- const q = await this.assertQueue(queue, optionsWithDefaults);
405
- await confirmChannel.prefetch(limit, false);
388
+ await confirmChannel.assertQueue(queue, options ? { messageTtl: options.messageTtl } : undefined);
389
+ await confirmChannel.prefetch(limit, true);
406
390
  const { consumerTag } = await confirmChannel.consume(queue, async (msg) => {
407
391
  if (!msg) {
408
392
  return null;
@@ -479,7 +463,7 @@ class RabbitMq {
479
463
  RabbitMq.validateName('queue', queue);
480
464
  const { limit, deadMessageTtl } = optionsWithDefaults;
481
465
  await this.saveConsumer(queue, callback, options);
482
- const channel = await this.getNewChannel({ name: `consume-exchange-${exchange}-queue-${queue}` });
466
+ const channel = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}`, connectionPurpose: types_1.ConnectionPurpose.Consume });
483
467
  return channel.addSetup(async (c) => {
484
468
  const assertExchange = await (0, utils_1.assertExchangeFanout)(c, exchange);
485
469
  await c.assertQueue(queue);
@@ -494,14 +478,14 @@ class RabbitMq {
494
478
  async publish(exchange, content, customHeaders) {
495
479
  return (0, utils_1.wrapSetImmediate)(async () => {
496
480
  RabbitMq.validateName('exchange', exchange);
497
- const channel = await this.assertChannel();
498
- await this.assertExchange(exchange);
481
+ const channel = await this.assertChannel({ connectionPurpose: types_1.ConnectionPurpose.Publish });
482
+ await this.assertExchange(exchange, { connectionPurpose: types_1.ConnectionPurpose.Publish });
499
483
  await channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
500
484
  });
501
485
  }
502
486
  async sendToQueue(queue, content, options, customHeaders) {
503
487
  try {
504
- await this.assertChannel();
488
+ await this.assertChannel({ connectionPurpose: types_1.ConnectionPurpose.Publish });
505
489
  }
506
490
  catch (e) {
507
491
  logger_1.default.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
@@ -509,7 +493,7 @@ class RabbitMq {
509
493
  }
510
494
  try {
511
495
  RabbitMq.validateName('queue', queue);
512
- await this.assertQueue(queue, options);
496
+ await this.assertQueue(queue, types_1.ConnectionPurpose.Publish, options);
513
497
  }
514
498
  catch (e) {
515
499
  logger_1.default.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
@@ -521,19 +505,23 @@ class RabbitMq {
521
505
  return res;
522
506
  }
523
507
  catch (e) {
524
- const isConnected = await this.isConnected();
508
+ const isConnected = await this.isConnected(types_1.ConnectionPurpose.Publish);
525
509
  logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
526
510
  throw e;
527
511
  }
528
512
  }
529
- async isConnected() {
530
- const connection = await this.getConnection();
513
+ async isConnected(connectionPurpose) {
514
+ const connection = await this.getConnection(connectionPurpose);
515
+ if (!connection) {
516
+ logger_1.default.error('rabbit: isConnected - false');
517
+ return false;
518
+ }
531
519
  const isConnected = connection.isConnected();
532
520
  if (!isConnected) {
533
521
  logger_1.default.error('rabbit: isConnected - false');
534
522
  return false;
535
523
  }
536
- const channel = await this.assertChannel();
524
+ const channel = await this.assertChannel({ connectionPurpose });
537
525
  try {
538
526
  await Promise.all([
539
527
  channel.waitForConnect(),
@@ -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;
@@ -8,9 +9,6 @@ export interface QueuesCache {
8
9
  export interface QueueSetupPromisesDictionary {
9
10
  [key: string]: Promise<Replies.AssertQueue> | undefined;
10
11
  }
11
- export interface AssertExchangePromisesDictionary {
12
- [key: string]: Promise<Replies.AssertExchange> | undefined;
13
- }
14
12
  export type CustomMessageHeaders = {
15
13
  redisTimestampValidationKey?: string;
16
14
  };
@@ -41,3 +39,11 @@ export type AfConsumer = {
41
39
  options: ConsumeOptions | undefined;
42
40
  };
43
41
  export declare const CONSUMER_DEFAULT_OPTIONS: Options.Consume;
42
+ export type ConnectionData = {
43
+ connection: AmqpConnectionManager | null;
44
+ creatingConnection: boolean;
45
+ };
46
+ export declare enum ConnectionPurpose {
47
+ Consume = "consume",
48
+ Publish = "publish"
49
+ }
package/dist/lib/types.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CONSUMER_DEFAULT_OPTIONS = void 0;
3
+ exports.ConnectionPurpose = exports.CONSUMER_DEFAULT_OPTIONS = void 0;
4
4
  const HA_PROMOTE_ON_FAILURE = 'ha-promote-on-failure';
5
5
  const HA_PROMOTE_ON_SHUTDOWN = 'ha-promote-on-shutdown';
6
6
  exports.CONSUMER_DEFAULT_OPTIONS = {
@@ -9,3 +9,8 @@ exports.CONSUMER_DEFAULT_OPTIONS = {
9
9
  [HA_PROMOTE_ON_SHUTDOWN]: 'always',
10
10
  },
11
11
  };
12
+ var ConnectionPurpose;
13
+ (function (ConnectionPurpose) {
14
+ ConnectionPurpose["Consume"] = "consume";
15
+ ConnectionPurpose["Publish"] = "publish";
16
+ })(ConnectionPurpose = exports.ConnectionPurpose || (exports.ConnectionPurpose = {}));
@@ -1,5 +1,5 @@
1
1
  import { ChannelWrapper } from 'amqp-connection-manager';
2
- import { ConfirmChannel, Replies } from 'amqplib';
3
- export declare const assertExchangeFanout: (c: ChannelWrapper | ConfirmChannel, exchangeName: string) => Promise<Replies.AssertExchange>;
2
+ import { ConfirmChannel } from 'amqplib';
3
+ export declare const assertExchangeFanout: (c: ChannelWrapper | ConfirmChannel, exchangeName: string) => Promise<import("amqplib").Replies.AssertExchange>;
4
4
  export declare const wrapSetImmediate: (callback: () => any) => Promise<any>;
5
5
  export declare const rand: () => number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/rabbit",
3
- "version": "3.2.21",
3
+ "version": "3.2.22-beta.1",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
package/src/index.ts CHANGED
@@ -35,17 +35,15 @@ import {
35
35
  CustomMessageHeaders,
36
36
  QueuesCache,
37
37
  RedisLockType,
38
- ExchangesCache,
39
- CONSUMER_DEFAULT_OPTIONS,
40
- QueueSetupPromisesDictionary,
41
- AssertExchangePromisesDictionary,
38
+ ExchangesCache, CONSUMER_DEFAULT_OPTIONS, QueueSetupPromisesDictionary,
39
+ ConnectionPurpose,
40
+ ConnectionData,
42
41
  } from './lib/types';
43
42
 
44
43
  // const debug = nodeDebug('af-rabbitmq')
45
44
  const debug = logger.debug.bind(logger);
46
45
 
47
46
  const PUBLISH_TIMEOUT = 1000 * 10;
48
-
49
47
  export interface IAfRabbitMq {
50
48
  ack: any;
51
49
  nack: any;
@@ -85,11 +83,13 @@ type newChannelOpts = {
85
83
  name?: string;
86
84
  onClose?: null | ((args: any | null) => void);
87
85
  options?: CreateChannelOpts | undefined;
86
+ connectionPurpose: ConnectionPurpose,
88
87
  };
89
88
 
90
89
  type assertChannelOpts = {
91
90
  channelName?: string;
92
91
  force?: boolean;
92
+ connectionPurpose: ConnectionPurpose;
93
93
  }
94
94
 
95
95
  type AfConsumer = {
@@ -101,7 +101,7 @@ type AfConsumer = {
101
101
  const HEARTBEAT = '60';
102
102
 
103
103
  class RabbitMq implements IAfRabbitMq {
104
- static parseMsg(msg: any) : any {
104
+ static parseMsg(msg: any): any {
105
105
  let { content } = msg;
106
106
  content = content.toString();
107
107
 
@@ -146,22 +146,21 @@ class RabbitMq implements IAfRabbitMq {
146
146
 
147
147
  publishChannelSetupPromise: Promise<ChannelWrapper> | null;
148
148
 
149
- blockReconnect: boolean | null | undefined
149
+ blockReconnect: boolean | null | undefined;
150
150
 
151
- connection: AmqpConnectionManager | null | undefined
151
+ connectionsMap: {
152
+ [ConnectionPurpose.Consume]: ConnectionData;
153
+ [ConnectionPurpose.Publish]: ConnectionData;
154
+ };
152
155
 
153
156
  em: EventEmitter;
154
157
 
155
- creatingConnection: boolean;
156
-
157
158
  exchanges: ExchangesCache;
158
159
 
159
160
  queues: QueuesCache;
160
161
 
161
162
  queueSetupPromises: QueueSetupPromisesDictionary;
162
163
 
163
- assertExchangePromises: AssertExchangePromisesDictionary;
164
-
165
164
  options: AfRabbitOptions | undefined;
166
165
 
167
166
  redisClient: any;
@@ -177,12 +176,13 @@ class RabbitMq implements IAfRabbitMq {
177
176
  this.em = new EventEmitter();
178
177
  this.channel = null;
179
178
  this.publishChannelSetupPromise = null;
180
- this.connection = null;
181
- this.creatingConnection = false;
179
+ this.connectionsMap = {
180
+ [ConnectionPurpose.Consume]: { connection: null, creatingConnection: false },
181
+ [ConnectionPurpose.Publish]: { connection: null, creatingConnection: false },
182
+ };
182
183
  this.exchanges = {};
183
184
  this.queues = {};
184
185
  this.queueSetupPromises = {};
185
- this.assertExchangePromises = {};
186
186
  this.consumers = [];
187
187
  this.options = options;
188
188
  this.redisClient = redisConfig && getRedisInstance(redisConfig);
@@ -215,7 +215,7 @@ class RabbitMq implements IAfRabbitMq {
215
215
  return false;
216
216
  }
217
217
 
218
- public ack = (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp = false, releaseLock = null) => async (userMsg: ConsumeMessage) : Promise<any> => {
218
+ public ack = (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp = false, releaseLock = null) => async (userMsg: ConsumeMessage): Promise<any> => {
219
219
  if (msg) {
220
220
  debug('rabbit acking message', { deliveryTag: msg.fields.deliveryTag });
221
221
  await channel.ack(msg);
@@ -241,8 +241,8 @@ class RabbitMq implements IAfRabbitMq {
241
241
  userMsg: ConsumeMessageOrNull,
242
242
  {
243
243
  skipRetry = false,
244
- }: NackOptions = { },
245
- ) : Promise<any> => {
244
+ }: NackOptions = {},
245
+ ): Promise<any> => {
246
246
  await this.unlockRedisIfNeeded(releaseLock);
247
247
  if (channel && msg) {
248
248
  if (
@@ -276,30 +276,31 @@ class RabbitMq implements IAfRabbitMq {
276
276
  }
277
277
  }
278
278
 
279
- async getConnection() {
280
- return new Promise<AmqpConnectionManager>(async (resolve, reject) => {
279
+ async getConnection(connectionPurpose: ConnectionPurpose) {
280
+ return new Promise<AmqpConnectionManager | undefined | null>(async (resolve, reject) => {
281
+ const { connection, creatingConnection } = this.connectionsMap[connectionPurpose];
281
282
  if (this.blockReconnect) {
282
283
  debug('rabbit: block reconnect');
283
284
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
284
285
  // @ts-ignore
285
286
  return resolve();
286
287
  }
287
- if (this.connection !== null) {
288
- if (this.options?.disableReconnect || this.connection?.isConnected()) {
288
+ if (connection !== null) {
289
+ if (this.options?.disableReconnect || connection?.isConnected()) {
289
290
  debug('rabbit: connection - is connected');
290
291
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
291
292
  // @ts-ignore
292
- return resolve(this.connection);
293
+ return resolve(connection);
293
294
  }
294
295
  debug('rabbit: connection - reconnecting');
295
296
  }
296
- if (this.creatingConnection) {
297
+ if (creatingConnection) {
297
298
  debug('rabbit: creating connection emi');
298
299
  this.em.once(CONNECTION_CREATED_CONST, resolve);
299
300
  this.em.once(CONNECTION_FAILED_CONST, reject);
300
301
  return;
301
302
  }
302
- this.creatingConnection = true;
303
+ this.connectionsMap[connectionPurpose].creatingConnection = true;
303
304
  let isResolved = false;
304
305
 
305
306
  // It is import to use it as a function and not as a variable
@@ -316,12 +317,18 @@ class RabbitMq implements IAfRabbitMq {
316
317
  };
317
318
 
318
319
  const defaultUrls = findServers();
319
- const connection: AmqpConnectionManager = await connect(defaultUrls, {
320
+ const newConnection: AmqpConnectionManager = await connect(defaultUrls, {
320
321
  findServers,
321
322
  });
322
323
 
323
- this.connection = connection;
324
- this.connection.on('error', (err) => {
324
+ if (!newConnection) {
325
+ logger.error('rabbit: couldnt create a connection');
326
+ return resolve(connection);
327
+ }
328
+
329
+ this.connectionsMap[connectionPurpose].connection = newConnection;
330
+
331
+ newConnection.on('error', (err) => {
325
332
  logger.error('rabbit: connection error', { err });
326
333
  if (!isResolved) {
327
334
  isResolved = true;
@@ -330,7 +337,7 @@ class RabbitMq implements IAfRabbitMq {
330
337
  }
331
338
  });
332
339
 
333
- this.connection.on('connectFailed', (err) => {
340
+ newConnection.on('connectFailed', (err) => {
334
341
  this.consumersTags = [];
335
342
  logger.error('rabbit: connection connectFailed', { err });
336
343
  if (!isResolved) {
@@ -340,8 +347,7 @@ class RabbitMq implements IAfRabbitMq {
340
347
  }
341
348
  });
342
349
 
343
- this.connection.on('disconnect', ({ err }) => {
344
- // this.channel = null;
350
+ newConnection.on('disconnect', ({ err }) => {
345
351
  this.consumersTags = [];
346
352
  debug('rabbit: connection closed');
347
353
  if (this.options?.disableReconnect) {
@@ -351,25 +357,29 @@ class RabbitMq implements IAfRabbitMq {
351
357
  logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
352
358
  }
353
359
  });
354
-
355
- this.connection.once('connect', async () => {
360
+ newConnection.once('connect', async () => {
356
361
  debug('rabbit: connection established');
357
- this.creatingConnection = false;
358
- this.em.emit(CONNECTION_CREATED_CONST, connection);
362
+ this.connectionsMap[connectionPurpose].creatingConnection = false;
363
+ this.em.emit(CONNECTION_CREATED_CONST, newConnection);
359
364
  isResolved = true;
360
- resolve(connection);
365
+ resolve(newConnection);
361
366
  });
362
367
  });
363
368
  }
364
369
 
365
- async getNewChannel({ name = rand().toString(), onClose = null, options = {} }: newChannelOpts = {}) {
366
- let connection!: AmqpConnectionManager;
370
+ async getNewChannel({
371
+ name = rand().toString(), onClose = null, options = {}, connectionPurpose = ConnectionPurpose.Consume,
372
+ }: newChannelOpts): Promise<ChannelWrapper> {
373
+ let connection!: AmqpConnectionManager | undefined | null;
367
374
  try {
368
- connection = await this.getConnection();
375
+ connection = await this.getConnection(connectionPurpose);
369
376
  } catch (e) {
370
377
  logger.error(`rabbit: error on get connection for new channel ${name} `, { e });
371
378
  throw e;
372
379
  }
380
+ if (!connection) {
381
+ throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
382
+ }
373
383
  const channel = connection.createChannel({ ...options });
374
384
  once(channel, 'close').then((args) => {
375
385
  logger.error(`rabbit: channel ${name} closed`);
@@ -385,7 +395,7 @@ class RabbitMq implements IAfRabbitMq {
385
395
  }
386
396
  }
387
397
 
388
- async assertChannel({ force = false } : assertChannelOpts = {}): Promise<ChannelWrapper> {
398
+ async assertChannel({ force = false, connectionPurpose = ConnectionPurpose.Consume }: assertChannelOpts): Promise<ChannelWrapper> {
389
399
  if (!this.publishChannelSetupPromise) {
390
400
  this.publishChannelSetupPromise = new Promise<ChannelWrapper>(async (resolve, reject) => {
391
401
  if (this.channel && !force) {
@@ -393,7 +403,7 @@ class RabbitMq implements IAfRabbitMq {
393
403
  }
394
404
 
395
405
  try {
396
- const channel = await this.getNewChannel({});
406
+ const channel = await this.getNewChannel({ connectionPurpose });
397
407
  channel.on('error', (err) => {
398
408
  logger.error('rabbit: channel error', { err });
399
409
  });
@@ -407,102 +417,71 @@ class RabbitMq implements IAfRabbitMq {
407
417
  return this.publishChannelSetupPromise;
408
418
  }
409
419
 
410
- async assertExchange(exchangeName: string, options?: any) {
411
- const channel: ChannelWrapper = await this.assertChannel();
412
-
420
+ async assertExchange(exchangeName: string, options: any = { connectionPurpose: ConnectionPurpose.Consume }) {
421
+ const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose: options.connectionPurpose });
413
422
  if (this.exchanges[exchangeName]) {
414
- delete this.assertExchangePromises[exchangeName];
415
423
  return this.exchanges[exchangeName];
416
424
  }
417
-
418
- if (this.assertExchangePromises[exchangeName]) {
419
- return this.assertExchangePromises[exchangeName];
420
- }
421
-
422
- this.assertExchangePromises[exchangeName] = assertExchangeFanout(channel, exchangeName);
423
- this.exchanges[exchangeName] = await this.assertExchangePromises[exchangeName];
424
- return this.exchanges[exchangeName];
425
+ const exchange = await assertExchangeFanout(channel, exchangeName);
426
+ this.exchanges[exchangeName] = exchange;
427
+ return exchange;
425
428
  }
426
429
 
427
- async getQueueLength(queue: string) {
430
+ async getQueueLength(queue: string, connectionPurpose: ConnectionPurpose = ConnectionPurpose.Consume): Promise<Replies.AssertQueue> {
428
431
  RabbitMq.validateName('queue', queue);
432
+ const { connection } = this.connectionsMap[connectionPurpose];
429
433
  const { channel } = this;
430
434
  if (!channel) {
431
435
  throw new Error('channel is not defined');
432
436
  }
433
- debug('rabbit: getting queue length', { queue, connected: this.connection?.isConnected() });
437
+ debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
434
438
  return channel?.checkQueue(queue);
435
439
  }
436
440
 
437
- private async deleteQueue(queue: string) {
441
+ private async deleteQueue(queue: string, connectionPurpose: ConnectionPurpose) {
438
442
  RabbitMq.validateName('queue', queue);
439
- const channel: ChannelWrapper = await this.assertChannel();
443
+ const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
440
444
  logger.info('rabbit: deleting queue', { queue });
441
445
  const deleteQueueRes = await channel.deleteQueue(queue);
442
446
  debug('queue deleted', deleteQueueRes);
443
447
  return deleteQueueRes;
444
448
  }
445
449
 
446
- async bindQueue(queue: string, exchange: string) {
447
- const channel: ChannelWrapper = await this.assertChannel();
450
+ async bindQueue(queue: string, exchange: string, connectionPurpose: ConnectionPurpose) {
451
+ const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
448
452
  await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.bindQueue(queue, exchange, ''));
449
453
  return channel.bindQueue(queue, exchange, '');
450
454
  }
451
455
 
452
- async setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
456
+ async setupQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
453
457
  let queue: Replies.AssertQueue;
454
- const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
455
- const localeOptions = {
456
- ...options,
457
- durable: true,
458
- arguments: {
459
- ...options?.arguments,
460
- 'x-consumer-timeout': 1000 * 60 * 60 * 24,
461
- 'x-queue-type': shouldUseQuorum ? 'quorum' : 'classic',
462
- },
463
- };
464
458
  try {
465
- const channel: ChannelWrapper = await this.assertChannel();
459
+ const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
466
460
  debug('assertQueue->channel.addSetup', { queueName });
467
- await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
461
+ await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, options));
468
462
  debug('assertQueue->channel.assertQueue', { queueName });
469
- queue = await channel.assertQueue(queueName, localeOptions);
463
+ queue = await channel.assertQueue(queueName, options);
470
464
  } catch (e) {
471
465
  logger.error('rabbit: assertQueue error', { queueName, options, error: e });
472
466
  if (!this.options?.dontRetryAssert) {
473
467
  debug('retrying assertQueue', { queueName });
474
- const channel = await this.assertChannel({ force: true });
475
- await this.deleteQueue(queueName);
468
+ const channel = await this.assertChannel({ force: true, connectionPurpose });
469
+ await this.deleteQueue(queueName, connectionPurpose);
476
470
 
477
471
  debug('retrying assertQueue->channel.addSetup', { queueName });
478
- await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
472
+ await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, options));
479
473
  debug('retrying assertQueue->channel.assertQueue', { queueName });
480
- queue = await channel.assertQueue(queueName, localeOptions);
474
+ queue = await channel.assertQueue(queueName, options);
481
475
  } else {
482
476
  throw e;
483
477
  }
484
478
  }
485
479
 
486
- this.queues[queueName] = queue;
480
+ this.queues[queueName] = queueName;
487
481
  return queue;
488
482
  }
489
483
 
490
- static shouldUseQuorum(queueName: string): boolean {
491
- const envQuorumQueuesWhitelist = process.env.QUORUM_QUEUES_WHITELIST;
492
-
493
- if (envQuorumQueuesWhitelist === '*') {
494
- return true;
495
- }
496
-
497
- if (envQuorumQueuesWhitelist) {
498
- const whitelist = envQuorumQueuesWhitelist.split(',');
499
- return whitelist.includes(queueName);
500
- }
501
-
502
- return false;
503
- }
504
-
505
- async assertQueue(queueName: string, options?: Options.AssertQueue): Promise<any> {
484
+ async assertQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue) {
506
485
  RabbitMq.validateName('queue', queueName);
507
486
  if (this.queues[queueName]) {
508
487
  delete this.queueSetupPromises[queueName];
@@ -513,12 +492,12 @@ class RabbitMq implements IAfRabbitMq {
513
492
  return this.queueSetupPromises[queueName];
514
493
  }
515
494
 
516
- this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
495
+ this.queueSetupPromises[queueName] = this.setupQueue(queueName, connectionPurpose, options);
517
496
  return this.queueSetupPromises[queueName];
518
497
  }
519
498
 
520
499
  private saveConsumer(queue: string, callback: CallbackFunction, options: ConsumeOptions | undefined) {
521
- const isConsumerExist :boolean = this.consumers.some((consumer) => consumer.queue === queue);
500
+ const isConsumerExist: boolean = this.consumers.some((consumer) => consumer.queue === queue);
522
501
  if (!isConsumerExist) {
523
502
  logger.info(`rabbit: consumer: ${queue} saved in consumer array`);
524
503
  this.consumers.push({
@@ -567,10 +546,10 @@ class RabbitMq implements IAfRabbitMq {
567
546
  }
568
547
  logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
569
548
  }
570
- const channel = await this.getNewChannel({});
549
+ const channel = await this.getNewChannel({ connectionPurpose: ConnectionPurpose.Consume });
571
550
  return channel.addSetup(async (confirmChannel: ConfirmChannel) => {
572
- const q = await this.assertQueue(queue, optionsWithDefaults);
573
- await confirmChannel.prefetch(limit, false);
551
+ await confirmChannel.assertQueue(queue, options ? { messageTtl: options.messageTtl } : undefined);
552
+ await confirmChannel.prefetch(limit, true);
574
553
  const { consumerTag } = await confirmChannel.consume(
575
554
  queue,
576
555
  async (msg: ConsumeMessageOrNull) => {
@@ -653,13 +632,13 @@ class RabbitMq implements IAfRabbitMq {
653
632
  });
654
633
  }
655
634
 
656
- async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions) : Promise<any> {
635
+ async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
657
636
  const optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options };
658
637
  RabbitMq.validateName('exchange', exchange);
659
638
  RabbitMq.validateName('queue', queue);
660
639
  const { limit, deadMessageTtl } = optionsWithDefaults;
661
640
  await this.saveConsumer(queue, callback, options);
662
- const channel: ChannelWrapper = await this.getNewChannel({ name: `consume-exchange-${exchange}-queue-${queue}` });
641
+ const channel: ChannelWrapper = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}`, connectionPurpose: ConnectionPurpose.Consume });
663
642
 
664
643
  return channel.addSetup(async (c: ConfirmChannel) => {
665
644
  const assertExchange = await assertExchangeFanout(c, exchange);
@@ -677,11 +656,11 @@ class RabbitMq implements IAfRabbitMq {
677
656
  });
678
657
  }
679
658
 
680
- async publish(exchange: string, content: any, customHeaders?: any) : Promise<boolean> {
659
+ async publish(exchange: string, content: any, customHeaders?: any): Promise<boolean> {
681
660
  return wrapSetImmediate(async () => {
682
661
  RabbitMq.validateName('exchange', exchange);
683
- const channel: ChannelWrapper = await this.assertChannel();
684
- await this.assertExchange(exchange);
662
+ const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose: ConnectionPurpose.Publish });
663
+ await this.assertExchange(exchange, { connectionPurpose: ConnectionPurpose.Publish });
685
664
  await channel.publish(exchange, '',
686
665
  Buffer.from(JSON.stringify(content)),
687
666
  RabbitMq.getPublishOptions(customHeaders));
@@ -695,7 +674,7 @@ class RabbitMq implements IAfRabbitMq {
695
674
  customHeaders?: any,
696
675
  ): Promise<boolean | undefined> {
697
676
  try {
698
- await this.assertChannel();
677
+ await this.assertChannel({ connectionPurpose: ConnectionPurpose.Publish });
699
678
  } catch (e) {
700
679
  logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
701
680
  throw e;
@@ -703,7 +682,7 @@ class RabbitMq implements IAfRabbitMq {
703
682
 
704
683
  try {
705
684
  RabbitMq.validateName('queue', queue);
706
- await this.assertQueue(queue, options);
685
+ await this.assertQueue(queue, ConnectionPurpose.Publish, options);
707
686
  } catch (e) {
708
687
  logger.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
709
688
  throw e;
@@ -716,20 +695,24 @@ class RabbitMq implements IAfRabbitMq {
716
695
  debug(`rabbit: sending to queue ${queue}`, { res });
717
696
  return res;
718
697
  } catch (e) {
719
- const isConnected = await this.isConnected();
698
+ const isConnected = await this.isConnected(ConnectionPurpose.Publish);
720
699
  logger.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
721
700
  throw e;
722
701
  }
723
702
  }
724
703
 
725
- async isConnected() : Promise<boolean> {
726
- const connection = await this.getConnection();
704
+ async isConnected(connectionPurpose: ConnectionPurpose): Promise<boolean> {
705
+ const connection = await this.getConnection(connectionPurpose);
706
+ if (!connection) {
707
+ logger.error('rabbit: isConnected - false');
708
+ return false;
709
+ }
727
710
  const isConnected = connection.isConnected();
728
711
  if (!isConnected) {
729
712
  logger.error('rabbit: isConnected - false');
730
713
  return false;
731
714
  }
732
- const channel: any = await this.assertChannel();
715
+ const channel: any = await this.assertChannel({ connectionPurpose });
733
716
  try {
734
717
  await Promise.all([
735
718
  channel.waitForConnect(),
@@ -743,7 +726,7 @@ class RabbitMq implements IAfRabbitMq {
743
726
  return true;
744
727
  }
745
728
 
746
- async gracefulShutdown(signal: string) : Promise<void> {
729
+ async gracefulShutdown(signal: string): Promise<void> {
747
730
  const tagsNumber = this.consumersTags.length;
748
731
  logger.info(`rabbit: [gracefully-shutdown] received ${signal}! canceling #${tagsNumber} tags...`);
749
732
  const cancelTagPromises = this.consumersTags.map(([channel, tag]) => channel.cancel(tag));
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 {
@@ -12,10 +13,6 @@ export interface QueueSetupPromisesDictionary {
12
13
  [key: string]: Promise<Replies.AssertQueue> | undefined
13
14
  }
14
15
 
15
- export interface AssertExchangePromisesDictionary {
16
- [key: string]: Promise<Replies.AssertExchange> | undefined
17
- }
18
-
19
16
  export type CustomMessageHeaders = {
20
17
  redisTimestampValidationKey?: string;
21
18
  }
@@ -59,3 +56,13 @@ export const CONSUMER_DEFAULT_OPTIONS: Options.Consume = {
59
56
  [HA_PROMOTE_ON_SHUTDOWN]: 'always',
60
57
  },
61
58
  };
59
+
60
+ export type ConnectionData = {
61
+ connection: AmqpConnectionManager | null;
62
+ creatingConnection: boolean;
63
+ };
64
+
65
+ export enum ConnectionPurpose {
66
+ Consume = 'consume',
67
+ Publish = 'publish',
68
+ }
package/src/lib/utils.ts CHANGED
@@ -1,11 +1,7 @@
1
1
  import { ChannelWrapper } from 'amqp-connection-manager';
2
- import { ConfirmChannel, Replies } from 'amqplib';
3
-
4
- export const assertExchangeFanout = async (
5
- c: ChannelWrapper | ConfirmChannel,
6
- exchangeName: string,
7
- ): Promise<Replies.AssertExchange> => c.assertExchange(exchangeName, 'fanout');
2
+ import { ConfirmChannel } from 'amqplib';
8
3
 
4
+ export const assertExchangeFanout = async (c: ChannelWrapper | ConfirmChannel, exchangeName: string) => c.assertExchange(exchangeName, 'fanout');
9
5
  export const wrapSetImmediate = (callback: () => any) => new Promise<any>((resolve, reject) => {
10
6
  setImmediate(async () => {
11
7
  try {