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