@autofleet/rabbit 3.3.22-connection-test-beta → 3.4.0-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.sendCeleryTaskViaHttp = void 0;
7
8
  const events_1 = require("events");
8
9
  const util_1 = require("util");
9
10
  const moment_1 = __importDefault(require("moment"));
@@ -21,38 +22,6 @@ const types_1 = require("./lib/types");
21
22
  const debug = logger_1.default.debug.bind(logger_1.default);
22
23
  const PUBLISH_TIMEOUT = 1000 * 10;
23
24
  const HEARTBEAT = '60';
24
- const withTimeout = async (promise, timeoutMs) => new Promise((resolve, reject) => {
25
- const timer = setTimeout(() => {
26
- reject(new Error(`Promise timed out after ${timeoutMs} ms`));
27
- }, timeoutMs);
28
- promise
29
- .then((value) => {
30
- clearTimeout(timer);
31
- resolve(value);
32
- })
33
- .catch((err) => {
34
- clearTimeout(timer);
35
- reject(err);
36
- });
37
- });
38
- const withRetry = async (operation, retries, timeoutMs) => new Promise(async (resolve, reject) => {
39
- let lastError;
40
- for (let attempt = 1; attempt <= retries; attempt += 1) {
41
- try {
42
- // eslint-disable-next-line no-await-in-loop
43
- const result = await withTimeout(operation(), timeoutMs);
44
- return resolve(result);
45
- }
46
- catch (err) {
47
- lastError = err;
48
- logger_1.default.error(`Attempt ${attempt} failed: ${err}`);
49
- if (attempt < retries) {
50
- logger_1.default.info(`Retrying (${attempt}/${retries})...`);
51
- }
52
- }
53
- }
54
- reject(lastError);
55
- });
56
25
  class RabbitMq {
57
26
  static parseMsg(msg) {
58
27
  let { content } = msg;
@@ -87,10 +56,57 @@ class RabbitMq {
87
56
  },
88
57
  };
89
58
  }
90
- constructor(options, redisConfig) {
59
+ constructor(options = {}, redisConfig) {
91
60
  this.DISCONNECT_MSG = 'rabbit: connection disconnect';
92
61
  this.RECONNECT_MSG = 'rabbit: connection disconnect - reconnecting';
93
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
+ };
94
110
  this.shouldConsumeMessageByTimestamp = async (msg) => {
95
111
  if (msg) {
96
112
  const { properties: { headers } } = msg;
@@ -172,6 +188,18 @@ class RabbitMq {
172
188
  await this.gracefulShutdown('SIGINT');
173
189
  });
174
190
  }
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 = [];
175
203
  }
176
204
  async getConnection() {
177
205
  return new Promise(async (resolve, reject) => {
@@ -206,7 +234,7 @@ class RabbitMq {
206
234
  const password = process.env.RABBITMQ_PASSWORD || 'guest';
207
235
  const host = this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost';
208
236
  debug('rabbit: creating connection', { host, userName, HEARTBEAT });
209
- return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
237
+ return [`amqp://${userName}:${password}@${host}/${this.vhost}?heartbeat=${HEARTBEAT}`];
210
238
  };
211
239
  const defaultUrls = findServers();
212
240
  const connection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
@@ -223,7 +251,7 @@ class RabbitMq {
223
251
  });
224
252
  this.connection.on('connectFailed', (err) => {
225
253
  this.consumersTags = [];
226
- logger_1.default.error('rabbit: connection connectFailed', { err });
254
+ logger_1.default.error('rabbit: connection connectFailed', { err, advice: 'Check if the vhost exist', vhost: this.vhost });
227
255
  if (!isResolved) {
228
256
  isResolved = true;
229
257
  reject(err);
@@ -309,11 +337,12 @@ class RabbitMq {
309
337
  this.exchanges[exchangeName] = await this.assertExchangePromises[exchangeName];
310
338
  return this.exchanges[exchangeName];
311
339
  }
340
+ // TODO: [QUORUM-PHASE-2] Change the implementation to the new one
312
341
  async getQueueLength(queue) {
313
342
  RabbitMq.validateName('queue', queue);
314
- const { channel } = this;
343
+ const { oldChannel: channel } = this;
315
344
  if (!channel) {
316
- throw new Error('channel is not defined');
345
+ throw new rabbitError_1.default('channel is not defined');
317
346
  }
318
347
  debug('rabbit: getting queue length', { queue, connected: this.connection?.isConnected() });
319
348
  return channel?.checkQueue(queue);
@@ -326,27 +355,29 @@ class RabbitMq {
326
355
  debug('queue deleted', deleteQueueRes);
327
356
  return deleteQueueRes;
328
357
  }
358
+ // TODO: [QUORUM-PHASE-2] Change the implementation to the new one
329
359
  async bindQueue(queue, exchange) {
330
- const channel = await this.assertChannel();
360
+ const channel = await this.assertChannelOld();
331
361
  await channel.addSetup((setupChannel) => setupChannel.bindQueue(queue, exchange, ''));
332
362
  return channel.bindQueue(queue, exchange, '');
333
363
  }
334
364
  async setupQueue(queueName, options) {
335
365
  let queue;
336
- const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
337
366
  const localeOptions = {
338
367
  ...options,
339
368
  durable: true,
340
369
  arguments: {
341
370
  ...options?.arguments,
342
371
  'x-consumer-timeout': 1000 * 60 * 60 * 24,
343
- 'x-queue-type': shouldUseQuorum ? 'quorum' : 'classic',
372
+ 'x-queue-type': 'quorum',
344
373
  },
345
374
  };
346
375
  try {
347
376
  const channel = await this.assertChannel();
348
377
  debug('assertQueue->channel.addSetup', { queueName });
349
- await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
378
+ await channel.addSetup(async (setupChannel) => {
379
+ await setupChannel.assertQueue(queueName, localeOptions);
380
+ });
350
381
  debug('assertQueue->channel.assertQueue', { queueName });
351
382
  queue = await channel.assertQueue(queueName, localeOptions);
352
383
  }
@@ -368,6 +399,7 @@ class RabbitMq {
368
399
  this.queues[queueName] = queue;
369
400
  return queue;
370
401
  }
402
+ // TODO: [QUORUM-PHASE-3] Can be deleted after deleting the old consumers and publishers because all the queues are created us quorum queues
371
403
  static shouldUseQuorum(queueName) {
372
404
  const envQuorumQueuesWhitelist = process.env.QUORUM_QUEUES_WHITELIST;
373
405
  if (envQuorumQueuesWhitelist === '*') {
@@ -402,9 +434,23 @@ class RabbitMq {
402
434
  });
403
435
  }
404
436
  }
437
+ // Used by the microservices to consume messages from the queue
405
438
  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) {
406
448
  await this.consumeFromRabbit(queue, callback, options);
407
449
  }
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
+ }
408
454
  async lockRedisIfNeeded(msg, options) {
409
455
  const { properties: { headers } } = msg;
410
456
  const timestamp = headers?.creationTimestamp;
@@ -427,7 +473,7 @@ class RabbitMq {
427
473
  const { limit, deadMessageTtl, useConsumeWithLock, lockTimeout, auditContext, enableRabbitTrace, } = optionsWithDefaults;
428
474
  if (useConsumeWithLock) {
429
475
  if (!this.redisLock) {
430
- throw new Error('Usage of consumeWithLock requires RedisInstance');
476
+ throw new rabbitError_1.default('Usage of consumeWithLock requires RedisInstance');
431
477
  }
432
478
  logger_1.default.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
433
479
  }
@@ -509,35 +555,57 @@ class RabbitMq {
509
555
  }
510
556
  });
511
557
  }
558
+ // Used by the microservices to consume messages from the exchange
512
559
  async consumeFromExchange(queue, exchange, callback, options) {
513
560
  const optionsWithDefaults = { ...consts_1.DEFAULT_OPTIONS, ...options };
514
561
  RabbitMq.validateName('exchange', exchange);
515
562
  RabbitMq.validateName('queue', queue);
516
563
  const { limit, deadMessageTtl } = optionsWithDefaults;
517
- await this.saveConsumer(queue, callback, options);
518
- const channel = await this.getNewChannel({ name: `consume-exchange-${exchange}-queue-${queue}` });
519
- return channel.addSetup(async (c) => {
564
+ // TODO: [QUORUM-PHASE-3] Delete the if statement after all the queues are created as quorum queues
565
+ if (options?.isQuorumQueue !== false) {
566
+ await this.assertVHost();
567
+ await this.saveConsumer(queue, callback, options);
568
+ const channel = await this.getNewChannel({ name: `consume-exchange-${exchange}-queue-${queue}` });
569
+ await channel.addSetup(async (c) => {
570
+ const assertExchange = await (0, utils_1.assertExchangeFanout)(c, exchange);
571
+ await c.assertQueue(queue);
572
+ this.exchanges[exchange] = assertExchange;
573
+ await c.prefetch(limit, false);
574
+ return Promise.all([
575
+ c.bindQueue(queue, exchange, ''),
576
+ this.consumeNew(queue, callback, options),
577
+ ]);
578
+ });
579
+ }
580
+ // TODO: [QUORUM-PHASE-3] Delete the old implementation
581
+ await this.saveConsumerOld(queue, callback, options);
582
+ const channelOld = await this.getNewChannelOld({ name: `consume-exchange-${exchange}-queue-${queue}-old` });
583
+ await channelOld.addSetup(async (c) => {
520
584
  const assertExchange = await (0, utils_1.assertExchangeFanout)(c, exchange);
521
585
  await c.assertQueue(queue);
522
- this.exchanges[exchange] = assertExchange;
523
- await c.prefetch(limit, true);
586
+ this.oldExchanges[exchange] = assertExchange;
587
+ await c.prefetch(limit, false);
524
588
  return Promise.all([
525
589
  c.bindQueue(queue, exchange, ''),
526
- this.consume(queue, callback, options),
590
+ this.consumeOld(queue, callback, options),
527
591
  ]);
528
592
  });
529
593
  }
594
+ // Used by the microservices to publish messages to the exchange
595
+ // TODO: [QUORUM-PHASE-2] Change the implementation to the new one
530
596
  async publish(exchange, content, customHeaders) {
531
597
  return (0, utils_1.wrapSetImmediate)(async () => {
532
598
  RabbitMq.validateName('exchange', exchange);
533
- const channel = await this.assertChannel();
534
- await this.assertExchange(exchange);
599
+ const channel = await this.assertChannelOld();
600
+ await this.assertExchangeOld(exchange);
535
601
  await channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
536
602
  });
537
603
  }
604
+ // Used by the microservices to send messages to the queue
605
+ // TODO: [QUORUM-PHASE-2] Change the implementation to the new one
538
606
  async sendToQueue(queue, content, options, customHeaders) {
539
607
  try {
540
- await this.assertChannel();
608
+ await this.assertChannelOld();
541
609
  }
542
610
  catch (e) {
543
611
  logger_1.default.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
@@ -545,14 +613,14 @@ class RabbitMq {
545
613
  }
546
614
  try {
547
615
  RabbitMq.validateName('queue', queue);
548
- await this.assertQueue(queue, options);
616
+ await this.assertQueueOld(queue, options);
549
617
  }
550
618
  catch (e) {
551
619
  logger_1.default.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
552
620
  throw e;
553
621
  }
554
622
  try {
555
- const res = await this.channel?.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
623
+ const res = await this.oldChannel?.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
556
624
  debug(`rabbit: sending to queue ${queue}`, { res });
557
625
  return res;
558
626
  }
@@ -562,59 +630,60 @@ class RabbitMq {
562
630
  throw e;
563
631
  }
564
632
  }
565
- async isConnected() {
633
+ async sendToCeleryQueue(data, taskName, queueName) {
566
634
  try {
567
- const connection = await this.getConnection();
568
- if (!connection.isConnected()) {
569
- logger_1.default.error('rabbit: isConnected - false');
570
- return false;
571
- }
572
- const channel = await this.assertChannel();
573
- const timeoutMs = 5000;
574
- const retries = 2;
575
- const chunkSize = 10;
576
- const processChunks = async (consumers) => {
577
- for (let i = 0; i < consumers.length; i += chunkSize) {
578
- const chunk = consumers.slice(i, i + chunkSize);
579
- // eslint-disable-next-line no-await-in-loop
580
- const chunkResults = await Promise.all(chunk.map((c) => withRetry(() => channel.checkQueue(c.queue), retries, timeoutMs).catch((err) => {
581
- logger_1.default.error(`rabbit: Error in isConnected (checkQueue) - ${err.message}`);
582
- return null;
583
- })));
584
- if (chunkResults.some((res) => res === null)) {
585
- return false;
586
- }
587
- }
588
- return true;
635
+ const taskPayload = {
636
+ task: taskName,
637
+ id: (0, node_crypto_1.randomUUID)(),
638
+ args: [data],
639
+ kwargs: {},
640
+ retries: 0,
641
+ eta: null,
589
642
  };
590
- const waitForConnectResult = await withRetry(() => channel.waitForConnect(), retries, timeoutMs).catch((err) => {
591
- logger_1.default.error(`rabbit: Error in isConnected (waitForConnect) - ${err.message}`);
592
- return null;
593
- });
594
- if (waitForConnectResult === null) {
595
- logger_1.default.error('rabbit: isConnected - false due to failed waitForConnect');
596
- return false;
597
- }
598
- const allConsumersConnected = await processChunks(this.consumers);
599
- if (!allConsumersConnected) {
600
- logger_1.default.error('rabbit: isConnected - false due to failed consumer checks');
601
- return false;
602
- }
603
- logger_1.default.info('rabbit: isConnected - true');
604
- return true;
643
+ const headers = {
644
+ 'content-type': 'application/json',
645
+ delivery_mode: 2,
646
+ };
647
+ return await this.sendToQueue(queueName, taskPayload, undefined, headers);
648
+ }
649
+ catch (e) {
650
+ const isConnected = await this.isConnected();
651
+ logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queueName}, isConnected: ${isConnected}`, { e });
652
+ throw e;
653
+ }
654
+ }
655
+ // TODO: [QUORUM-PHASE-2] After changing the publish from old to new change from the old to the new implementation
656
+ async isConnected() {
657
+ const connection = await this.getConnectionOld();
658
+ const isConnected = connection.isConnected();
659
+ if (!isConnected) {
660
+ logger_1.default.error('rabbit: isConnected - false');
661
+ return false;
662
+ }
663
+ const channel = await this.assertChannelOld();
664
+ try {
665
+ await Promise.all([
666
+ channel.waitForConnect(),
667
+ ...this.oldConsumers.map((c) => channel.checkQueue(c.queue)),
668
+ ]);
605
669
  }
606
670
  catch (e) {
607
- logger_1.default.error(`rabbit: isConnected - Exception occurred: ${e}`);
671
+ logger_1.default.error('rabbit: isConnected - false');
608
672
  return false;
609
673
  }
674
+ logger_1.default.info('rabbit: isConnected - true');
675
+ return true;
610
676
  }
611
677
  async gracefulShutdown(signal) {
612
- const tagsNumber = this.consumersTags.length;
678
+ // TODO: [QUORUM-PHASE-2] After changing the publish from old to new change from the old to the new implementation
679
+ const tagsNumber = this.consumersTags.length + this.oldConsumersTags.length;
613
680
  logger_1.default.info(`rabbit: [gracefully-shutdown] received ${signal}! canceling #${tagsNumber} tags...`);
614
681
  const cancelTagPromises = this.consumersTags.map(([channel, tag]) => channel.cancel(tag));
682
+ const cancelTagPromisesOld = this.oldConsumersTags.map(([channel, tag]) => channel.cancel(tag));
615
683
  // Clean the array to avoid race
616
684
  this.consumersTags = [];
617
- const results = await Promise.allSettled(cancelTagPromises);
685
+ this.oldConsumersTags = [];
686
+ const results = await Promise.allSettled([...cancelTagPromises, ...cancelTagPromisesOld]);
618
687
  const rejected = results.filter((p) => p.status === 'rejected');
619
688
  if (rejected.length > 0) {
620
689
  logger_1.default.warn(`rabbit: [gracefully-shutdown] #${rejected.length}/${tagsNumber} tags failed to cancel: ${rejected}`);
@@ -623,5 +692,301 @@ class RabbitMq {
623
692
  logger_1.default.info('rabbit: [gracefully-shutdown] all tags successfully canceled.');
624
693
  }
625
694
  }
695
+ async consumeFromRabbitOld(queue, callback, options) {
696
+ const optionsWithDefaults = { ...consts_1.DEFAULT_OPTIONS, ...options };
697
+ RabbitMq.validateName('queue', queue);
698
+ this.saveConsumerOld(queue, callback, options);
699
+ const uniqueId = (0, node_crypto_1.randomUUID)();
700
+ const { limit, deadMessageTtl, useConsumeWithLock, lockTimeout, auditContext, enableRabbitTrace, } = optionsWithDefaults;
701
+ if (useConsumeWithLock) {
702
+ if (!this.redisLock) {
703
+ throw new rabbitError_1.default('Usage of consumeWithLock requires RedisInstance');
704
+ }
705
+ logger_1.default.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
706
+ }
707
+ const channel = await this.getNewChannelOld({});
708
+ return channel.addSetup(async (confirmChannel) => {
709
+ const q = await this.assertQueueOld(queue, optionsWithDefaults);
710
+ await confirmChannel.prefetch(limit, false);
711
+ const { consumerTag } = await confirmChannel.consume(queue, async (msg) => {
712
+ if (!msg) {
713
+ return null;
714
+ }
715
+ const traceId = msg.properties.headers[consts_1.TRACING_HEADER];
716
+ const userId = msg.properties.headers[consts_1.USER_TRACING_HEADER];
717
+ const automationId = msg.properties.headers[consts_1.AUTOMATION_ID_HEADER];
718
+ const parsedMessage = RabbitMq.parseMsg(msg);
719
+ const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
720
+ const trace = (0, zehut_1.newTrace)(zehut_1.traceTypes.RABBIT);
721
+ // setting also outbreak trace as part of legacy code
722
+ const outbreakTrace = zehut_1.outbreak.newTrace(zehut_1.traceTypes.RABBIT);
723
+ // enableRabbitTrace is a flag to protect from different flows that doesn't work with permission
724
+ // and we don't want to fail the flow because of it
725
+ if (userId && enableRabbitTrace) {
726
+ try {
727
+ await Promise.all([
728
+ (0, zehut_1.createOrSetRabbitTrace)(trace, userId),
729
+ (0, zehut_1.createOrSetRabbitTrace)(outbreakTrace, userId),
730
+ ]);
731
+ }
732
+ catch (e) {
733
+ logger_1.default.error('rabbit: failed to setRabbitTrace', { userId, e });
734
+ return this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg);
735
+ }
736
+ }
737
+ if (traceId) {
738
+ trace?.context?.set(consts_1.TRACING_HEADER, traceId);
739
+ outbreakTrace?.context.set(consts_1.TRACING_HEADER, traceId);
740
+ }
741
+ if (auditContext) {
742
+ await auditContext(queue, {
743
+ userId,
744
+ automationId,
745
+ });
746
+ }
747
+ const shouldConsume = await this.shouldConsumeMessageByTimestamp(parsedMessage);
748
+ if (!shouldConsume) {
749
+ await this.unlockRedisIfNeeded(releaseLock);
750
+ return this.ack(confirmChannel, msg)(msg);
751
+ }
752
+ let messageAcked = false;
753
+ // setting the localAck function to be used in the callback
754
+ const localAck = async () => {
755
+ if (messageAcked) {
756
+ return;
757
+ }
758
+ messageAcked = true;
759
+ return this.ack(confirmChannel, msg, true, releaseLock)(msg);
760
+ };
761
+ const localNack = async (_, nackOptions = {}) => {
762
+ if (messageAcked) {
763
+ return;
764
+ }
765
+ debug('rabbit localNack', { messageAcked, uniqueId, deliveryTag: msg.fields.deliveryTag });
766
+ messageAcked = true;
767
+ return this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg, nackOptions);
768
+ };
769
+ try {
770
+ await callback(parsedMessage, localAck, localNack);
771
+ }
772
+ catch (e) {
773
+ await localNack(msg);
774
+ }
775
+ }, types_1.CONSUMER_DEFAULT_OPTIONS);
776
+ if (!consumerTag) {
777
+ logger_1.default.error(`rabbit: failed to consume from queue ${queue}`);
778
+ }
779
+ else {
780
+ logger_1.default.info(`rabbit: adding tag ${consumerTag} to the array.`);
781
+ this.oldConsumersTags.push([confirmChannel, consumerTag]);
782
+ }
783
+ });
784
+ }
785
+ // TODO: [QUORUM-PHASE-3] Delete all the function under this line (getNewChannelOld, getConnectionOld, assertQueueOld, setupQueueOld, saveConsumerOld)
786
+ async getNewChannelOld({ name = (0, utils_1.rand)().toString(), onClose = null, options = {} } = {}) {
787
+ let connection;
788
+ try {
789
+ connection = await this.getConnectionOld();
790
+ }
791
+ catch (e) {
792
+ logger_1.default.error(`rabbit: error on get connection for new channel ${name} `, { e });
793
+ throw e;
794
+ }
795
+ const channel = connection.createChannel({ ...options });
796
+ (0, events_1.once)(channel, 'close').then((args) => {
797
+ logger_1.default.error(`rabbit: channel ${name} closed`);
798
+ onClose?.(args);
799
+ });
800
+ try {
801
+ await (0, events_1.once)(channel, 'connect');
802
+ debug(`rabbit: channel ${name} CONNECTED`);
803
+ return channel;
804
+ }
805
+ catch (err) {
806
+ logger_1.default.error(`rabbit: channel error ${name} error`, { err });
807
+ throw err;
808
+ }
809
+ }
810
+ async getConnectionOld() {
811
+ return new Promise(async (resolve, reject) => {
812
+ if (this.oldBlockReconnect) {
813
+ debug('rabbit: block reconnect');
814
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
815
+ // @ts-ignore
816
+ return resolve();
817
+ }
818
+ if (this.oldConnection !== null) {
819
+ if (this.options?.disableReconnect || this.oldConnection?.isConnected()) {
820
+ debug('rabbit: connection - is connected');
821
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
822
+ // @ts-ignore
823
+ return resolve(this.oldConnection);
824
+ }
825
+ debug('rabbit: connection - reconnecting');
826
+ }
827
+ if (this.oldCreatingConnection) {
828
+ debug('rabbit: creating connection emi');
829
+ this.oldEm.once(consts_1.CONNECTION_CREATED_CONST, resolve);
830
+ this.oldEm.once(consts_1.CONNECTION_FAILED_CONST, reject);
831
+ return;
832
+ }
833
+ this.oldCreatingConnection = true;
834
+ let isResolved = false;
835
+ // It is import to use it as a function and not as a variable
836
+ // because of k8s changes the env variables
837
+ // and we want to use the new values
838
+ const findServers = () => {
839
+ const userName = process.env.RABBITMQ_USERNAME || 'guest';
840
+ const password = process.env.RABBITMQ_PASSWORD || 'guest';
841
+ const host = this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost';
842
+ debug('rabbit: creating connection', { host, userName, HEARTBEAT });
843
+ return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
844
+ };
845
+ const defaultUrls = findServers();
846
+ const connection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
847
+ findServers,
848
+ });
849
+ this.oldConnection = connection;
850
+ this.oldConnection.on('error', (err) => {
851
+ logger_1.default.error('rabbit: connection error', { err });
852
+ if (!isResolved) {
853
+ isResolved = true;
854
+ reject(err);
855
+ this.oldEm.emit(consts_1.CONNECTION_FAILED_CONST, err);
856
+ }
857
+ });
858
+ this.oldConnection.on('connectFailed', (err) => {
859
+ this.oldConsumersTags = [];
860
+ logger_1.default.error('rabbit: connection connectFailed', { err });
861
+ if (!isResolved) {
862
+ isResolved = true;
863
+ reject(err);
864
+ this.oldEm.emit(consts_1.CONNECTION_FAILED_CONST, err);
865
+ }
866
+ });
867
+ this.oldConnection.on('disconnect', ({ err }) => {
868
+ this.oldConsumersTags = [];
869
+ debug('rabbit: connection closed');
870
+ if (this.options?.disableReconnect) {
871
+ logger_1.default.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
872
+ this.oldBlockReconnect = true;
873
+ }
874
+ else {
875
+ logger_1.default.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
876
+ }
877
+ });
878
+ this.oldConnection.once('connect', async () => {
879
+ debug('rabbit: connection established');
880
+ this.oldCreatingConnection = false;
881
+ this.oldEm.emit(consts_1.CONNECTION_CREATED_CONST, connection);
882
+ isResolved = true;
883
+ resolve(connection);
884
+ });
885
+ });
886
+ }
887
+ saveConsumerOld(queue, callback, options) {
888
+ const isConsumerExist = this.oldConsumers.some((consumer) => consumer.queue === queue);
889
+ if (!isConsumerExist) {
890
+ logger_1.default.info(`rabbit: consumer: ${queue} saved in consumer array`);
891
+ this.oldConsumers.push({
892
+ queue,
893
+ callback,
894
+ options,
895
+ });
896
+ }
897
+ }
898
+ async assertQueueOld(queueName, options) {
899
+ RabbitMq.validateName('queue', queueName);
900
+ if (this.oldQueues[queueName]) {
901
+ delete this.oldQueueSetupPromises[queueName];
902
+ return this.oldQueues[queueName];
903
+ }
904
+ if (this.oldQueueSetupPromises[queueName]) {
905
+ return this.oldQueueSetupPromises[queueName];
906
+ }
907
+ this.oldQueueSetupPromises[queueName] = this.setupQueueOld(queueName, options);
908
+ return this.oldQueueSetupPromises[queueName];
909
+ }
910
+ async setupQueueOld(queueName, options) {
911
+ let queue;
912
+ const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
913
+ const localeOptions = {
914
+ ...options,
915
+ durable: true,
916
+ arguments: {
917
+ ...options?.arguments,
918
+ 'x-consumer-timeout': 1000 * 60 * 60 * 24,
919
+ 'x-queue-type': shouldUseQuorum ? 'quorum' : 'classic',
920
+ },
921
+ };
922
+ try {
923
+ const channel = await this.assertChannelOld();
924
+ debug('assertQueue->channel.addSetup', { queueName });
925
+ await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
926
+ debug('assertQueue->channel.assertQueue', { queueName });
927
+ queue = await channel.assertQueue(queueName, localeOptions);
928
+ }
929
+ catch (e) {
930
+ logger_1.default.error('rabbit: assertQueue error', { queueName, options, error: e });
931
+ if (!this.options?.dontRetryAssert) {
932
+ debug('retrying assertQueue', { queueName });
933
+ const channel = await this.assertChannelOld({ force: true });
934
+ await this.deleteQueueOld(queueName);
935
+ debug('retrying assertQueue->channel.addSetup', { queueName });
936
+ await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
937
+ debug('retrying assertQueue->channel.assertQueue', { queueName });
938
+ queue = await channel.assertQueue(queueName, localeOptions);
939
+ }
940
+ else {
941
+ throw e;
942
+ }
943
+ }
944
+ this.oldQueues[queueName] = queue;
945
+ return queue;
946
+ }
947
+ async assertChannelOld({ force = false } = {}) {
948
+ if (!this.oldPublishChannelSetupPromise) {
949
+ this.oldPublishChannelSetupPromise = new Promise(async (resolve, reject) => {
950
+ if (this.oldChannel && !force) {
951
+ return resolve(this.oldChannel);
952
+ }
953
+ try {
954
+ const channel = await this.getNewChannelOld({});
955
+ channel.on('error', (err) => {
956
+ logger_1.default.error('rabbit: channel error', { err });
957
+ });
958
+ this.oldChannel = channel;
959
+ resolve(channel);
960
+ }
961
+ catch (e) {
962
+ reject(e);
963
+ }
964
+ });
965
+ }
966
+ return this.oldPublishChannelSetupPromise;
967
+ }
968
+ async deleteQueueOld(queue) {
969
+ RabbitMq.validateName('queue', queue);
970
+ const channel = await this.assertChannelOld();
971
+ logger_1.default.info('rabbit: deleting queue', { queue });
972
+ const deleteQueueRes = await channel.deleteQueue(queue);
973
+ debug('queue deleted', deleteQueueRes);
974
+ return deleteQueueRes;
975
+ }
976
+ async assertExchangeOld(exchangeName, options) {
977
+ const channel = await this.assertChannelOld();
978
+ if (this.oldExchanges[exchangeName]) {
979
+ delete this.oldAssertExchangePromises[exchangeName];
980
+ return this.oldExchanges[exchangeName];
981
+ }
982
+ if (this.oldAssertExchangePromises[exchangeName]) {
983
+ return this.oldAssertExchangePromises[exchangeName];
984
+ }
985
+ this.oldAssertExchangePromises[exchangeName] = (0, utils_1.assertExchangeFanout)(channel, exchangeName);
986
+ this.oldExchanges[exchangeName] = await this.oldAssertExchangePromises[exchangeName];
987
+ return this.oldExchanges[exchangeName];
988
+ }
626
989
  }
627
990
  exports.default = RabbitMq;
991
+ var celery_1 = require("./lib/celery");
992
+ Object.defineProperty(exports, "sendCeleryTaskViaHttp", { enumerable: true, get: function () { return celery_1.sendCeleryTaskViaHttp; } });