@autofleet/rabbit 3.3.0-beta.11 → 3.3.0-beta.3

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.js CHANGED
@@ -56,57 +56,10 @@ class RabbitMq {
56
56
  },
57
57
  };
58
58
  }
59
- constructor(options = {}, redisConfig) {
59
+ constructor(options, redisConfig) {
60
60
  this.DISCONNECT_MSG = 'rabbit: connection disconnect';
61
61
  this.RECONNECT_MSG = 'rabbit: connection disconnect - reconnecting';
62
62
  this.consumers = [];
63
- this.doesVHostExist = false;
64
- this.vhost = 'quorum-vhost';
65
- this.oldConsumers = [];
66
- this.assertVHost = async () => {
67
- if (this.doesVHostExist) {
68
- return;
69
- }
70
- const username = process.env.RABBITMQ_USERNAME || 'guest';
71
- const password = process.env.RABBITMQ_PASSWORD || 'guest';
72
- const credentials = Buffer.from(`${username}:${password}`).toString('base64');
73
- const headers = {
74
- Authorization: `Basic ${credentials}`,
75
- 'Content-Type': 'application/json',
76
- };
77
- const rabbitHost = `http://${(this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost').split(':')[0]}:15672`;
78
- const url = `${rabbitHost}/api/vhosts/${encodeURIComponent(this.vhost)}`;
79
- try {
80
- const response = await fetch(url, {
81
- method: 'GET',
82
- headers,
83
- });
84
- if (response.status === 200) {
85
- this.doesVHostExist = true;
86
- logger_1.default.info('Vhost exists', { vhost: this.vhost });
87
- return;
88
- }
89
- if (response.status !== 404) {
90
- logger_1.default.error('Failed to check vhost', { response });
91
- throw new rabbitError_1.default('Failed to check vhost');
92
- }
93
- const createResponse = await fetch(url, {
94
- method: 'PUT',
95
- headers,
96
- body: JSON.stringify({ default_queue_type: 'quorum' }),
97
- });
98
- if (!createResponse.ok) {
99
- logger_1.default.error('Failed to create vhost', { response: createResponse });
100
- throw new rabbitError_1.default('Failed to create vhost');
101
- }
102
- this.doesVHostExist = true;
103
- logger_1.default.info('Vhost created', { vhost: this.vhost });
104
- }
105
- catch (error) {
106
- logger_1.default.error('Failed to check or create vhost', { error });
107
- throw error;
108
- }
109
- };
110
63
  this.shouldConsumeMessageByTimestamp = async (msg) => {
111
64
  if (msg) {
112
65
  const { properties: { headers } } = msg;
@@ -164,10 +117,24 @@ class RabbitMq {
164
117
  }
165
118
  };
166
119
  this.em = new events_1.EventEmitter();
167
- this.channel = null;
120
+ this.publishChannel = null;
168
121
  this.publishChannelSetupPromise = null;
169
- this.connection = null;
170
- this.creatingConnection = false;
122
+ this.connectionsMap = {
123
+ [consts_1.ConnectionPurpose.Consume]: {
124
+ connection: null,
125
+ creatingConnection: false,
126
+ connectionCreatedEventName: 'consumeConnectionCreated',
127
+ connectionFailedEventName: 'consumeConnectionFailed',
128
+ blockReconnect: false,
129
+ },
130
+ [consts_1.ConnectionPurpose.Publish]: {
131
+ connection: null,
132
+ creatingConnection: false,
133
+ connectionCreatedEventName: 'publishConnectionCreated',
134
+ connectionFailedEventName: 'publishConnectionFailed',
135
+ blockReconnect: false,
136
+ },
137
+ };
171
138
  this.exchanges = {};
172
139
  this.queues = {};
173
140
  this.queueSetupPromises = {};
@@ -188,43 +155,32 @@ class RabbitMq {
188
155
  await this.gracefulShutdown('SIGINT');
189
156
  });
190
157
  }
191
- // TODO: [QUORUM-PHASE-3] Delete the old properties that we use for the old consumers and publishers
192
- this.oldEm = new events_1.EventEmitter();
193
- this.oldChannel = null;
194
- this.oldPublishChannelSetupPromise = null;
195
- this.oldConnection = null;
196
- this.oldCreatingConnection = false;
197
- this.oldExchanges = {};
198
- this.oldQueues = {};
199
- this.oldQueueSetupPromises = {};
200
- this.oldAssertExchangePromises = {};
201
- this.oldConsumers = [];
202
- this.oldConsumersTags = [];
203
158
  }
204
- async getConnection() {
159
+ async getConnection(connectionPurpose) {
205
160
  return new Promise(async (resolve, reject) => {
206
- if (this.blockReconnect) {
161
+ const { connection, creatingConnection, connectionCreatedEventName, connectionFailedEventName, blockReconnect, } = this.connectionsMap[connectionPurpose];
162
+ if (blockReconnect) {
207
163
  debug('rabbit: block reconnect');
208
164
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
209
165
  // @ts-ignore
210
166
  return resolve();
211
167
  }
212
- if (this.connection !== null) {
213
- if (this.options?.disableReconnect || this.connection?.isConnected()) {
168
+ if (connection !== null) {
169
+ if (this.options?.disableReconnect || connection?.isConnected()) {
214
170
  debug('rabbit: connection - is connected');
215
171
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
216
172
  // @ts-ignore
217
- return resolve(this.connection);
173
+ return resolve(connection);
218
174
  }
219
175
  debug('rabbit: connection - reconnecting');
220
176
  }
221
- if (this.creatingConnection) {
177
+ if (creatingConnection) {
222
178
  debug('rabbit: creating connection emi');
223
- this.em.once(consts_1.CONNECTION_CREATED_CONST, resolve);
224
- this.em.once(consts_1.CONNECTION_FAILED_CONST, reject);
179
+ this.em.once(connectionCreatedEventName, resolve);
180
+ this.em.once(connectionFailedEventName, reject);
225
181
  return;
226
182
  }
227
- this.creatingConnection = true;
183
+ this.connectionsMap[connectionPurpose].creatingConnection = true;
228
184
  let isResolved = false;
229
185
  // It is import to use it as a function and not as a variable
230
186
  // because of k8s changes the env variables
@@ -234,60 +190,62 @@ class RabbitMq {
234
190
  const password = process.env.RABBITMQ_PASSWORD || 'guest';
235
191
  const host = this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost';
236
192
  debug('rabbit: creating connection', { host, userName, HEARTBEAT });
237
- return [`amqp://${userName}:${password}@${host}/${this.vhost}?heartbeat=${HEARTBEAT}`];
193
+ return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
238
194
  };
239
195
  const defaultUrls = findServers();
240
- const connection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
196
+ const newConnection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
241
197
  findServers,
242
198
  });
243
- this.connection = connection;
244
- this.connection.on('error', (err) => {
199
+ this.connectionsMap[connectionPurpose].connection = newConnection;
200
+ logger_1.default.info(`rabbit: created new connection ${connectionPurpose}`);
201
+ newConnection.on('error', (err) => {
245
202
  logger_1.default.error('rabbit: connection error', { err });
246
203
  if (!isResolved) {
247
204
  isResolved = true;
248
205
  reject(err);
249
- this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
206
+ this.em.emit(connectionFailedEventName, err);
250
207
  }
251
208
  });
252
- this.connection.on('connectFailed', (err) => {
209
+ newConnection.on('connectFailed', (err) => {
253
210
  this.consumersTags = [];
254
- logger_1.default.error('rabbit: connection connectFailed', { err, advice: 'Check if the vhost exist', vhost: this.vhost });
211
+ logger_1.default.error('rabbit: connection connectFailed', { err });
255
212
  if (!isResolved) {
256
213
  isResolved = true;
257
214
  reject(err);
258
- this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
215
+ this.em.emit(connectionFailedEventName, err);
259
216
  }
260
217
  });
261
- this.connection.on('disconnect', ({ err }) => {
262
- // this.channel = null;
218
+ newConnection.on('disconnect', ({ err }) => {
263
219
  this.consumersTags = [];
264
220
  debug('rabbit: connection closed');
265
221
  if (this.options?.disableReconnect) {
266
222
  logger_1.default.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
267
- this.blockReconnect = true;
223
+ this.connectionsMap[connectionPurpose].blockReconnect = true;
268
224
  }
269
225
  else {
270
226
  logger_1.default.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
271
227
  }
272
228
  });
273
- this.connection.once('connect', async () => {
274
- debug('rabbit: connection established');
275
- this.creatingConnection = false;
276
- this.em.emit(consts_1.CONNECTION_CREATED_CONST, connection);
229
+ newConnection.once('connect', async () => {
230
+ this.connectionsMap[connectionPurpose].creatingConnection = false;
231
+ this.em.emit(connectionCreatedEventName, newConnection);
277
232
  isResolved = true;
278
- resolve(connection);
233
+ resolve(newConnection);
279
234
  });
280
235
  });
281
236
  }
282
- async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {} } = {}) {
237
+ async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {}, connectionPurpose = consts_1.ConnectionPurpose.Consume, }) {
283
238
  let connection;
284
239
  try {
285
- connection = await this.getConnection();
240
+ connection = await this.getConnection(connectionPurpose);
286
241
  }
287
242
  catch (e) {
288
243
  logger_1.default.error(`rabbit: error on get connection for new channel ${name} `, { e });
289
244
  throw e;
290
245
  }
246
+ if (!connection) {
247
+ throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
248
+ }
291
249
  const channel = connection.createChannel({ ...options });
292
250
  (0, events_1.once)(channel, 'close').then((args) => {
293
251
  logger_1.default.error(`rabbit: channel ${name} closed`);
@@ -303,18 +261,22 @@ class RabbitMq {
303
261
  throw err;
304
262
  }
305
263
  }
306
- async assertChannel({ force = false } = {}) {
264
+ async assertChannel({ force = false, connectionPurpose = consts_1.ConnectionPurpose.Consume }) {
265
+ debug('rabbit: start assert channel', { connectionPurpose, publishPromise: this.publishChannelSetupPromise, channel: this.publishChannel });
307
266
  if (!this.publishChannelSetupPromise) {
308
267
  this.publishChannelSetupPromise = new Promise(async (resolve, reject) => {
309
- if (this.channel && !force) {
310
- return resolve(this.channel);
268
+ if (this.publishChannel && !force) {
269
+ return resolve(this.publishChannel);
311
270
  }
312
271
  try {
313
- const channel = await this.getNewChannel({});
272
+ const channel = await this.getNewChannel({ connectionPurpose });
273
+ debug('rabbit: new channel got', { connectionPurpose, channel: this.publishChannel });
314
274
  channel.on('error', (err) => {
315
275
  logger_1.default.error('rabbit: channel error', { err });
316
276
  });
317
- this.channel = channel;
277
+ if (connectionPurpose === consts_1.ConnectionPurpose.Publish) {
278
+ this.publishChannel = channel;
279
+ }
318
280
  resolve(channel);
319
281
  }
320
282
  catch (e) {
@@ -324,8 +286,8 @@ class RabbitMq {
324
286
  }
325
287
  return this.publishChannelSetupPromise;
326
288
  }
327
- async assertExchange(exchangeName, options) {
328
- const channel = await this.assertChannel();
289
+ async assertExchange(exchangeName, options = { connectionPurpose: consts_1.ConnectionPurpose.Consume }) {
290
+ const channel = await this.assertChannel({ connectionPurpose: options.connectionPurpose });
329
291
  if (this.exchanges[exchangeName]) {
330
292
  delete this.assertExchangePromises[exchangeName];
331
293
  return this.exchanges[exchangeName];
@@ -337,47 +299,46 @@ class RabbitMq {
337
299
  this.exchanges[exchangeName] = await this.assertExchangePromises[exchangeName];
338
300
  return this.exchanges[exchangeName];
339
301
  }
340
- // TODO: [QUORUM-PHASE-2] Change the implementation to the new one
341
- async getQueueLength(queue) {
302
+ async getQueueLength(queue, connectionPurpose = consts_1.ConnectionPurpose.Consume) {
342
303
  RabbitMq.validateName('queue', queue);
343
- const { oldChannel: channel } = this;
344
- if (!channel) {
345
- throw new rabbitError_1.default('channel is not defined');
304
+ const { connection } = this.connectionsMap[connectionPurpose];
305
+ const { publishChannel } = this;
306
+ if (!publishChannel) {
307
+ throw new Error('channel is not defined');
346
308
  }
347
- debug('rabbit: getting queue length', { queue, connected: this.connection?.isConnected() });
348
- return channel?.checkQueue(queue);
309
+ debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
310
+ return publishChannel?.checkQueue(queue);
349
311
  }
350
- async deleteQueue(queue) {
312
+ async deleteQueue(queue, connectionPurpose) {
351
313
  RabbitMq.validateName('queue', queue);
352
- const channel = await this.assertChannel();
314
+ const channel = await this.assertChannel({ connectionPurpose });
353
315
  logger_1.default.info('rabbit: deleting queue', { queue });
354
316
  const deleteQueueRes = await channel.deleteQueue(queue);
355
317
  debug('queue deleted', deleteQueueRes);
356
318
  return deleteQueueRes;
357
319
  }
358
- // TODO: [QUORUM-PHASE-2] Change the implementation to the new one
359
320
  async bindQueue(queue, exchange) {
360
- const channel = await this.assertChannelOld();
321
+ const channel = await this.assertChannel({ connectionPurpose: consts_1.ConnectionPurpose.Publish });
361
322
  await channel.addSetup((setupChannel) => setupChannel.bindQueue(queue, exchange, ''));
362
323
  return channel.bindQueue(queue, exchange, '');
363
324
  }
364
325
  async setupQueue(queueName, options) {
365
326
  let queue;
327
+ const connectionPurpose = consts_1.ConnectionPurpose.Publish;
328
+ const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
366
329
  const localeOptions = {
367
330
  ...options,
368
331
  durable: true,
369
332
  arguments: {
370
333
  ...options?.arguments,
371
334
  'x-consumer-timeout': 1000 * 60 * 60 * 24,
372
- 'x-queue-type': 'quorum',
335
+ 'x-queue-type': shouldUseQuorum ? 'quorum' : 'classic',
373
336
  },
374
337
  };
375
338
  try {
376
- const channel = await this.assertChannel();
339
+ const channel = await this.assertChannel({ connectionPurpose });
377
340
  debug('assertQueue->channel.addSetup', { queueName });
378
- await channel.addSetup(async (setupChannel) => {
379
- await setupChannel.assertQueue(queueName, localeOptions);
380
- });
341
+ await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
381
342
  debug('assertQueue->channel.assertQueue', { queueName });
382
343
  queue = await channel.assertQueue(queueName, localeOptions);
383
344
  }
@@ -385,8 +346,8 @@ class RabbitMq {
385
346
  logger_1.default.error('rabbit: assertQueue error', { queueName, options, error: e });
386
347
  if (!this.options?.dontRetryAssert) {
387
348
  debug('retrying assertQueue', { queueName });
388
- const channel = await this.assertChannel({ force: true });
389
- await this.deleteQueue(queueName);
349
+ const channel = await this.assertChannel({ force: true, connectionPurpose });
350
+ await this.deleteQueue(queueName, connectionPurpose);
390
351
  debug('retrying assertQueue->channel.addSetup', { queueName });
391
352
  await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
392
353
  debug('retrying assertQueue->channel.assertQueue', { queueName });
@@ -399,7 +360,6 @@ class RabbitMq {
399
360
  this.queues[queueName] = queue;
400
361
  return queue;
401
362
  }
402
- // TODO: [QUORUM-PHASE-3] Can be deleted after deleting the old consumers and publishers because all the queues are created us quorum queues
403
363
  static shouldUseQuorum(queueName) {
404
364
  const envQuorumQueuesWhitelist = process.env.QUORUM_QUEUES_WHITELIST;
405
365
  if (envQuorumQueuesWhitelist === '*') {
@@ -412,6 +372,7 @@ class RabbitMq {
412
372
  return false;
413
373
  }
414
374
  async assertQueue(queueName, options) {
375
+ debug('rabbit: start assert queue', { queueName });
415
376
  RabbitMq.validateName('queue', queueName);
416
377
  if (this.queues[queueName]) {
417
378
  delete this.queueSetupPromises[queueName];
@@ -421,6 +382,7 @@ class RabbitMq {
421
382
  return this.queueSetupPromises[queueName];
422
383
  }
423
384
  this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
385
+ debug('rabbit: done assert queue', { queueName });
424
386
  return this.queueSetupPromises[queueName];
425
387
  }
426
388
  saveConsumer(queue, callback, options) {
@@ -434,23 +396,9 @@ class RabbitMq {
434
396
  });
435
397
  }
436
398
  }
437
- // Used by the microservices to consume messages from the queue
438
399
  async consume(queue, callback, options) {
439
- // TODO: [QUORUM-PHASE-3] Use only the implementation of consumeNew and delete consumeNew and consumeOld
440
- if (options?.isQuorumQueue !== false) {
441
- await this.assertVHost();
442
- await this.consumeNew(queue, callback, options);
443
- }
444
- await this.consumeOld(queue, callback, options);
445
- }
446
- // TODO: [QUORUM-PHASE-3] Delete consumeNew we do not use it anymore
447
- async consumeNew(queue, callback, options) {
448
400
  await this.consumeFromRabbit(queue, callback, options);
449
401
  }
450
- // TODO: [QUORUM-PHASE-3] Delete consumeOld we do not use it anymore
451
- async consumeOld(queue, callback, options) {
452
- await this.consumeFromRabbitOld(queue, callback, options);
453
- }
454
402
  async lockRedisIfNeeded(msg, options) {
455
403
  const { properties: { headers } } = msg;
456
404
  const timestamp = headers?.creationTimestamp;
@@ -473,13 +421,12 @@ class RabbitMq {
473
421
  const { limit, deadMessageTtl, useConsumeWithLock, lockTimeout, auditContext, enableRabbitTrace, } = optionsWithDefaults;
474
422
  if (useConsumeWithLock) {
475
423
  if (!this.redisLock) {
476
- throw new rabbitError_1.default('Usage of consumeWithLock requires RedisInstance');
424
+ throw new Error('Usage of consumeWithLock requires RedisInstance');
477
425
  }
478
426
  logger_1.default.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
479
427
  }
480
- const channel = await this.getNewChannel({});
428
+ const channel = await this.getNewChannel({ connectionPurpose: consts_1.ConnectionPurpose.Consume });
481
429
  return channel.addSetup(async (confirmChannel) => {
482
- throw new Error('Dummy assertQueue error');
483
430
  const q = await this.assertQueue(queue, optionsWithDefaults);
484
431
  await confirmChannel.prefetch(limit, false);
485
432
  const { consumerTag } = await confirmChannel.consume(queue, async (msg) => {
@@ -556,57 +503,36 @@ class RabbitMq {
556
503
  }
557
504
  });
558
505
  }
559
- // Used by the microservices to consume messages from the exchange
560
506
  async consumeFromExchange(queue, exchange, callback, options) {
561
507
  const optionsWithDefaults = { ...consts_1.DEFAULT_OPTIONS, ...options };
562
508
  RabbitMq.validateName('exchange', exchange);
563
509
  RabbitMq.validateName('queue', queue);
564
510
  const { limit, deadMessageTtl } = optionsWithDefaults;
565
- // TODO: [QUORUM-PHASE-3] Delete the if statement after all the queues are created as quorum queues
566
- if (options?.isQuorumQueue !== false) {
567
- await this.assertVHost();
568
- await this.saveConsumer(queue, callback, options);
569
- const channel = await this.getNewChannel({ name: `consume-exchange-${exchange}-queue-${queue}` });
570
- await channel.addSetup(async (c) => {
571
- const assertExchange = await (0, utils_1.assertExchangeFanout)(c, exchange);
572
- await c.assertQueue(queue);
573
- this.exchanges[exchange] = assertExchange;
574
- await c.prefetch(limit, false);
575
- return Promise.all([
576
- c.bindQueue(queue, exchange, ''),
577
- this.consumeNew(queue, callback, options),
578
- ]);
579
- });
580
- }
581
- // TODO: [QUORUM-PHASE-3] Delete the old implementation
582
- await this.saveConsumerOld(queue, callback, options);
583
- const channelOld = await this.getNewChannelOld({ name: `consume-exchange-${exchange}-queue-${queue}-old` });
584
- await channelOld.addSetup(async (c) => {
511
+ await this.saveConsumer(queue, callback, options);
512
+ const channel = await this.getNewChannel({ name: `consume-exchange-${exchange}-queue-${queue}` });
513
+ return channel.addSetup(async (c) => {
585
514
  const assertExchange = await (0, utils_1.assertExchangeFanout)(c, exchange);
586
515
  await c.assertQueue(queue);
587
- this.oldExchanges[exchange] = assertExchange;
516
+ this.exchanges[exchange] = assertExchange;
588
517
  await c.prefetch(limit, false);
589
518
  return Promise.all([
590
519
  c.bindQueue(queue, exchange, ''),
591
- this.consumeOld(queue, callback, options),
520
+ this.consume(queue, callback, options),
592
521
  ]);
593
522
  });
594
523
  }
595
- // Used by the microservices to publish messages to the exchange
596
- // TODO: [QUORUM-PHASE-2] Change the implementation to the new one
597
524
  async publish(exchange, content, customHeaders) {
525
+ debug('rabbit: start publish msg');
598
526
  return (0, utils_1.wrapSetImmediate)(async () => {
599
527
  RabbitMq.validateName('exchange', exchange);
600
- const channel = await this.assertChannelOld();
601
- await this.assertExchangeOld(exchange);
528
+ const channel = await this.assertChannel({ connectionPurpose: consts_1.ConnectionPurpose.Publish });
529
+ await this.assertExchange(exchange, { connectionPurpose: consts_1.ConnectionPurpose.Publish });
602
530
  await channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
603
531
  });
604
532
  }
605
- // Used by the microservices to send messages to the queue
606
- // TODO: [QUORUM-PHASE-2] Change the implementation to the new one
607
533
  async sendToQueue(queue, content, options, customHeaders) {
608
534
  try {
609
- await this.assertChannelOld();
535
+ await this.assertChannel({ connectionPurpose: consts_1.ConnectionPurpose.Publish });
610
536
  }
611
537
  catch (e) {
612
538
  logger_1.default.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
@@ -614,55 +540,56 @@ class RabbitMq {
614
540
  }
615
541
  try {
616
542
  RabbitMq.validateName('queue', queue);
617
- await this.assertQueueOld(queue, options);
543
+ await this.assertQueue(queue, options);
618
544
  }
619
545
  catch (e) {
620
546
  logger_1.default.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
621
547
  throw e;
622
548
  }
623
549
  try {
624
- const res = await this.oldChannel?.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
550
+ const res = await this.publishChannel?.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
625
551
  debug(`rabbit: sending to queue ${queue}`, { res });
626
552
  return res;
627
553
  }
628
554
  catch (e) {
629
- const isConnected = await this.isConnected();
630
- logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
555
+ logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queue}`, { e });
631
556
  throw e;
632
557
  }
633
558
  }
634
- // TODO: [QUORUM-PHASE-2] After changing the publish from old to new change from the old to the new implementation
635
559
  async isConnected() {
636
- const connection = await this.getConnectionOld();
637
- const isConnected = connection.isConnected();
638
- if (!isConnected) {
639
- logger_1.default.error('rabbit: isConnected - false');
640
- return false;
641
- }
642
- const channel = await this.assertChannelOld();
643
- try {
644
- await Promise.all([
645
- channel.waitForConnect(),
646
- ...this.oldConsumers.map((c) => channel.checkQueue(c.queue)),
647
- ]);
648
- }
649
- catch (e) {
650
- logger_1.default.error('rabbit: isConnected - false');
651
- return false;
652
- }
653
- logger_1.default.info('rabbit: isConnected - true');
654
- return true;
560
+ debug('rabbit: start is connected');
561
+ const isEachConnectionConnected = await Promise.all(Object.keys(this.connectionsMap).map(async (connectionPurpose) => {
562
+ const connection = await this.getConnection(connectionPurpose);
563
+ const isConnected = connection?.isConnected();
564
+ if (!isConnected) {
565
+ logger_1.default.error('rabbit: isConnected - false', { connectionPurpose });
566
+ return false;
567
+ }
568
+ if (connectionPurpose === consts_1.ConnectionPurpose.Publish) {
569
+ const channel = await this.assertChannel({ connectionPurpose: connectionPurpose });
570
+ try {
571
+ await Promise.all([
572
+ channel.waitForConnect(),
573
+ ...this.consumers.map((c) => channel.checkQueue(c.queue)),
574
+ ]);
575
+ }
576
+ catch (e) {
577
+ logger_1.default.error('rabbit: isConnected - false');
578
+ return false;
579
+ }
580
+ }
581
+ logger_1.default.info('rabbit: isConnected - true', { connectionPurpose });
582
+ return true;
583
+ }));
584
+ return isEachConnectionConnected.every((isConnected) => isConnected === true);
655
585
  }
656
586
  async gracefulShutdown(signal) {
657
- // TODO: [QUORUM-PHASE-2] After changing the publish from old to new change from the old to the new implementation
658
- const tagsNumber = this.consumersTags.length + this.oldConsumersTags.length;
587
+ const tagsNumber = this.consumersTags.length;
659
588
  logger_1.default.info(`rabbit: [gracefully-shutdown] received ${signal}! canceling #${tagsNumber} tags...`);
660
589
  const cancelTagPromises = this.consumersTags.map(([channel, tag]) => channel.cancel(tag));
661
- const cancelTagPromisesOld = this.oldConsumersTags.map(([channel, tag]) => channel.cancel(tag));
662
590
  // Clean the array to avoid race
663
591
  this.consumersTags = [];
664
- this.oldConsumersTags = [];
665
- const results = await Promise.allSettled([...cancelTagPromises, ...cancelTagPromisesOld]);
592
+ const results = await Promise.allSettled(cancelTagPromises);
666
593
  const rejected = results.filter((p) => p.status === 'rejected');
667
594
  if (rejected.length > 0) {
668
595
  logger_1.default.warn(`rabbit: [gracefully-shutdown] #${rejected.length}/${tagsNumber} tags failed to cancel: ${rejected}`);
@@ -671,300 +598,6 @@ class RabbitMq {
671
598
  logger_1.default.info('rabbit: [gracefully-shutdown] all tags successfully canceled.');
672
599
  }
673
600
  }
674
- async consumeFromRabbitOld(queue, callback, options) {
675
- const optionsWithDefaults = { ...consts_1.DEFAULT_OPTIONS, ...options };
676
- RabbitMq.validateName('queue', queue);
677
- this.saveConsumerOld(queue, callback, options);
678
- const uniqueId = (0, node_crypto_1.randomUUID)();
679
- const { limit, deadMessageTtl, useConsumeWithLock, lockTimeout, auditContext, enableRabbitTrace, } = optionsWithDefaults;
680
- if (useConsumeWithLock) {
681
- if (!this.redisLock) {
682
- throw new rabbitError_1.default('Usage of consumeWithLock requires RedisInstance');
683
- }
684
- logger_1.default.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
685
- }
686
- const channel = await this.getNewChannelOld({});
687
- return channel.addSetup(async (confirmChannel) => {
688
- const q = await this.assertQueueOld(queue, optionsWithDefaults);
689
- await confirmChannel.prefetch(limit, false);
690
- const { consumerTag } = await confirmChannel.consume(queue, async (msg) => {
691
- if (!msg) {
692
- return null;
693
- }
694
- const traceId = msg.properties.headers[consts_1.TRACING_HEADER];
695
- const userId = msg.properties.headers[consts_1.USER_TRACING_HEADER];
696
- const automationId = msg.properties.headers[consts_1.AUTOMATION_ID_HEADER];
697
- const parsedMessage = RabbitMq.parseMsg(msg);
698
- const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
699
- const trace = (0, zehut_1.newTrace)(zehut_1.traceTypes.RABBIT);
700
- // setting also outbreak trace as part of legacy code
701
- const outbreakTrace = zehut_1.outbreak.newTrace(zehut_1.traceTypes.RABBIT);
702
- // enableRabbitTrace is a flag to protect from different flows that doesn't work with permission
703
- // and we don't want to fail the flow because of it
704
- if (userId && enableRabbitTrace) {
705
- try {
706
- await Promise.all([
707
- (0, zehut_1.createOrSetRabbitTrace)(trace, userId),
708
- (0, zehut_1.createOrSetRabbitTrace)(outbreakTrace, userId),
709
- ]);
710
- }
711
- catch (e) {
712
- logger_1.default.error('rabbit: failed to setRabbitTrace', { userId, e });
713
- return this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg);
714
- }
715
- }
716
- if (traceId) {
717
- trace?.context?.set(consts_1.TRACING_HEADER, traceId);
718
- outbreakTrace?.context.set(consts_1.TRACING_HEADER, traceId);
719
- }
720
- if (auditContext) {
721
- await auditContext(queue, {
722
- userId,
723
- automationId,
724
- });
725
- }
726
- const shouldConsume = await this.shouldConsumeMessageByTimestamp(parsedMessage);
727
- if (!shouldConsume) {
728
- await this.unlockRedisIfNeeded(releaseLock);
729
- return this.ack(confirmChannel, msg)(msg);
730
- }
731
- let messageAcked = false;
732
- // setting the localAck function to be used in the callback
733
- const localAck = async () => {
734
- if (messageAcked) {
735
- return;
736
- }
737
- messageAcked = true;
738
- return this.ack(confirmChannel, msg, true, releaseLock)(msg);
739
- };
740
- const localNack = async (_, nackOptions = {}) => {
741
- if (messageAcked) {
742
- return;
743
- }
744
- debug('rabbit localNack', { messageAcked, uniqueId, deliveryTag: msg.fields.deliveryTag });
745
- messageAcked = true;
746
- return this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg, nackOptions);
747
- };
748
- try {
749
- await callback(parsedMessage, localAck, localNack);
750
- }
751
- catch (e) {
752
- await localNack(msg);
753
- }
754
- }, types_1.CONSUMER_DEFAULT_OPTIONS);
755
- if (!consumerTag) {
756
- logger_1.default.error(`rabbit: failed to consume from queue ${queue}`);
757
- }
758
- else {
759
- logger_1.default.info(`rabbit: adding tag ${consumerTag} to the array.`);
760
- this.oldConsumersTags.push([confirmChannel, consumerTag]);
761
- }
762
- });
763
- }
764
- // TODO: [QUORUM-PHASE-3] Delete all the function under this line (getNewChannelOld, getConnectionOld, assertQueueOld, setupQueueOld, saveConsumerOld)
765
- async getNewChannelOld({ name = (0, utils_1.rand)().toString(), onClose = null, options = {} } = {}) {
766
- let connection;
767
- try {
768
- connection = await this.getConnectionOld();
769
- }
770
- catch (e) {
771
- logger_1.default.error(`rabbit: error on get connection for new channel ${name} `, { e });
772
- throw e;
773
- }
774
- const channel = connection.createChannel({ ...options });
775
- (0, events_1.once)(channel, 'close').then((args) => {
776
- logger_1.default.error(`rabbit: channel ${name} closed`);
777
- onClose?.(args);
778
- });
779
- try {
780
- await (0, events_1.once)(channel, 'connect');
781
- debug(`rabbit: channel ${name} CONNECTED`);
782
- return channel;
783
- }
784
- catch (err) {
785
- logger_1.default.error(`rabbit: channel error ${name} error`, { err });
786
- throw err;
787
- }
788
- }
789
- async getConnectionOld() {
790
- return new Promise(async (resolve, reject) => {
791
- if (this.oldBlockReconnect) {
792
- debug('rabbit: block reconnect');
793
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
794
- // @ts-ignore
795
- return resolve();
796
- }
797
- if (this.oldConnection !== null) {
798
- if (this.options?.disableReconnect || this.oldConnection?.isConnected()) {
799
- debug('rabbit: connection - is connected');
800
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
801
- // @ts-ignore
802
- return resolve(this.oldConnection);
803
- }
804
- debug('rabbit: connection - reconnecting');
805
- }
806
- if (this.oldCreatingConnection) {
807
- debug('rabbit: creating connection emi');
808
- this.oldEm.once(consts_1.CONNECTION_CREATED_CONST, resolve);
809
- this.oldEm.once(consts_1.CONNECTION_FAILED_CONST, reject);
810
- return;
811
- }
812
- this.oldCreatingConnection = true;
813
- let isResolved = false;
814
- // It is import to use it as a function and not as a variable
815
- // because of k8s changes the env variables
816
- // and we want to use the new values
817
- const findServers = () => {
818
- const userName = process.env.RABBITMQ_USERNAME || 'guest';
819
- const password = process.env.RABBITMQ_PASSWORD || 'guest';
820
- const host = this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost';
821
- debug('rabbit: creating connection', { host, userName, HEARTBEAT });
822
- return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
823
- };
824
- const defaultUrls = findServers();
825
- const connection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
826
- findServers,
827
- });
828
- this.oldConnection = connection;
829
- this.oldConnection.on('error', (err) => {
830
- logger_1.default.error('rabbit: connection error', { err });
831
- if (!isResolved) {
832
- isResolved = true;
833
- reject(err);
834
- this.oldEm.emit(consts_1.CONNECTION_FAILED_CONST, err);
835
- }
836
- });
837
- this.oldConnection.on('connectFailed', (err) => {
838
- this.oldConsumersTags = [];
839
- logger_1.default.error('rabbit: connection connectFailed', { err });
840
- if (!isResolved) {
841
- isResolved = true;
842
- reject(err);
843
- this.oldEm.emit(consts_1.CONNECTION_FAILED_CONST, err);
844
- }
845
- });
846
- this.oldConnection.on('disconnect', ({ err }) => {
847
- this.oldConsumersTags = [];
848
- debug('rabbit: connection closed');
849
- if (this.options?.disableReconnect) {
850
- logger_1.default.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
851
- this.oldBlockReconnect = true;
852
- }
853
- else {
854
- logger_1.default.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
855
- }
856
- });
857
- this.oldConnection.once('connect', async () => {
858
- debug('rabbit: connection established');
859
- this.oldCreatingConnection = false;
860
- this.oldEm.emit(consts_1.CONNECTION_CREATED_CONST, connection);
861
- isResolved = true;
862
- resolve(connection);
863
- });
864
- });
865
- }
866
- saveConsumerOld(queue, callback, options) {
867
- const isConsumerExist = this.oldConsumers.some((consumer) => consumer.queue === queue);
868
- if (!isConsumerExist) {
869
- logger_1.default.info(`rabbit: consumer: ${queue} saved in consumer array`);
870
- this.oldConsumers.push({
871
- queue,
872
- callback,
873
- options,
874
- });
875
- }
876
- }
877
- async assertQueueOld(queueName, options) {
878
- RabbitMq.validateName('queue', queueName);
879
- if (this.oldQueues[queueName]) {
880
- delete this.oldQueueSetupPromises[queueName];
881
- return this.oldQueues[queueName];
882
- }
883
- if (this.oldQueueSetupPromises[queueName]) {
884
- return this.oldQueueSetupPromises[queueName];
885
- }
886
- this.oldQueueSetupPromises[queueName] = this.setupQueueOld(queueName, options);
887
- return this.oldQueueSetupPromises[queueName];
888
- }
889
- async setupQueueOld(queueName, options) {
890
- let queue;
891
- const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
892
- const localeOptions = {
893
- ...options,
894
- durable: true,
895
- arguments: {
896
- ...options?.arguments,
897
- 'x-consumer-timeout': 1000 * 60 * 60 * 24,
898
- 'x-queue-type': shouldUseQuorum ? 'quorum' : 'classic',
899
- },
900
- };
901
- try {
902
- const channel = await this.assertChannelOld();
903
- debug('assertQueue->channel.addSetup', { queueName });
904
- await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
905
- debug('assertQueue->channel.assertQueue', { queueName });
906
- queue = await channel.assertQueue(queueName, localeOptions);
907
- }
908
- catch (e) {
909
- logger_1.default.error('rabbit: assertQueue error', { queueName, options, error: e });
910
- if (!this.options?.dontRetryAssert) {
911
- debug('retrying assertQueue', { queueName });
912
- const channel = await this.assertChannelOld({ force: true });
913
- await this.deleteQueueOld(queueName);
914
- debug('retrying assertQueue->channel.addSetup', { queueName });
915
- await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
916
- debug('retrying assertQueue->channel.assertQueue', { queueName });
917
- queue = await channel.assertQueue(queueName, localeOptions);
918
- }
919
- else {
920
- throw e;
921
- }
922
- }
923
- this.oldQueues[queueName] = queue;
924
- return queue;
925
- }
926
- async assertChannelOld({ force = false } = {}) {
927
- if (!this.oldPublishChannelSetupPromise) {
928
- this.oldPublishChannelSetupPromise = new Promise(async (resolve, reject) => {
929
- if (this.oldChannel && !force) {
930
- return resolve(this.oldChannel);
931
- }
932
- try {
933
- const channel = await this.getNewChannelOld({});
934
- channel.on('error', (err) => {
935
- logger_1.default.error('rabbit: channel error', { err });
936
- });
937
- this.oldChannel = channel;
938
- resolve(channel);
939
- }
940
- catch (e) {
941
- reject(e);
942
- }
943
- });
944
- }
945
- return this.oldPublishChannelSetupPromise;
946
- }
947
- async deleteQueueOld(queue) {
948
- RabbitMq.validateName('queue', queue);
949
- const channel = await this.assertChannelOld();
950
- logger_1.default.info('rabbit: deleting queue', { queue });
951
- const deleteQueueRes = await channel.deleteQueue(queue);
952
- debug('queue deleted', deleteQueueRes);
953
- return deleteQueueRes;
954
- }
955
- async assertExchangeOld(exchangeName, options) {
956
- const channel = await this.assertChannelOld();
957
- if (this.oldExchanges[exchangeName]) {
958
- delete this.oldAssertExchangePromises[exchangeName];
959
- return this.oldExchanges[exchangeName];
960
- }
961
- if (this.oldAssertExchangePromises[exchangeName]) {
962
- return this.oldAssertExchangePromises[exchangeName];
963
- }
964
- this.oldAssertExchangePromises[exchangeName] = (0, utils_1.assertExchangeFanout)(channel, exchangeName);
965
- this.oldExchanges[exchangeName] = await this.oldAssertExchangePromises[exchangeName];
966
- return this.oldExchanges[exchangeName];
967
- }
968
601
  }
969
602
  exports.default = RabbitMq;
970
603
  var celery_1 = require("./lib/celery");