@onlineapps/mq-client-core 1.0.78 → 1.0.79
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/package.json +1 -1
- package/src/transports/rabbitmqClient.js +61 -15
package/package.json
CHANGED
|
@@ -80,6 +80,7 @@ class RabbitMQClient extends EventEmitter {
|
|
|
80
80
|
this._reconnecting = false;
|
|
81
81
|
this._disconnecting = false;
|
|
82
82
|
this._reconnectAttempts = 0;
|
|
83
|
+
this._activeTimers = new Set();
|
|
83
84
|
this._maxReconnectAttempts = this._config.maxReconnectAttempts || 10; // Max 10 attempts
|
|
84
85
|
this._reconnectBaseDelay = this._config.reconnectBaseDelay || 1000; // Start with 1 second
|
|
85
86
|
this._reconnectMaxDelay = this._config.reconnectMaxDelay || 30000; // Max 30 seconds
|
|
@@ -418,6 +419,41 @@ class RabbitMQClient extends EventEmitter {
|
|
|
418
419
|
* @returns {Promise<void>}
|
|
419
420
|
* @throws {Error} If connection or channel creation fails.
|
|
420
421
|
*/
|
|
422
|
+
/**
|
|
423
|
+
* Helper to set a trackable timeout
|
|
424
|
+
* @private
|
|
425
|
+
*/
|
|
426
|
+
_setTimeout(callback, ms) {
|
|
427
|
+
const timer = setTimeout(() => {
|
|
428
|
+
this._activeTimers.delete(timer);
|
|
429
|
+
callback();
|
|
430
|
+
}, ms);
|
|
431
|
+
this._activeTimers.add(timer);
|
|
432
|
+
return timer;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Helper to clear a trackable timeout
|
|
437
|
+
* @private
|
|
438
|
+
*/
|
|
439
|
+
_clearTimeout(timer) {
|
|
440
|
+
if (timer) {
|
|
441
|
+
clearTimeout(timer);
|
|
442
|
+
this._activeTimers.delete(timer);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Clear all active timers
|
|
448
|
+
* @private
|
|
449
|
+
*/
|
|
450
|
+
_clearAllTimers() {
|
|
451
|
+
for (const timer of this._activeTimers) {
|
|
452
|
+
clearTimeout(timer);
|
|
453
|
+
}
|
|
454
|
+
this._activeTimers.clear();
|
|
455
|
+
}
|
|
456
|
+
|
|
421
457
|
async connect() {
|
|
422
458
|
let connectTimeoutTimer = null;
|
|
423
459
|
try {
|
|
@@ -435,7 +471,7 @@ class RabbitMQClient extends EventEmitter {
|
|
|
435
471
|
|
|
436
472
|
const connectPromise = amqp.connect(...connectArgs);
|
|
437
473
|
const timeoutPromise = new Promise((_, reject) => {
|
|
438
|
-
connectTimeoutTimer =
|
|
474
|
+
connectTimeoutTimer = this._setTimeout(() => reject(new Error('Connection timeout after 10 seconds')), 10000);
|
|
439
475
|
if (connectTimeoutTimer && typeof connectTimeoutTimer.unref === 'function') {
|
|
440
476
|
connectTimeoutTimer.unref();
|
|
441
477
|
}
|
|
@@ -443,7 +479,7 @@ class RabbitMQClient extends EventEmitter {
|
|
|
443
479
|
console.log('[RabbitMQClient] Starting connection race...');
|
|
444
480
|
this._connection = await Promise.race([connectPromise, timeoutPromise]);
|
|
445
481
|
if (connectTimeoutTimer) {
|
|
446
|
-
|
|
482
|
+
this._clearTimeout(connectTimeoutTimer);
|
|
447
483
|
connectTimeoutTimer = null;
|
|
448
484
|
}
|
|
449
485
|
console.log('[RabbitMQClient] Connection established');
|
|
@@ -562,18 +598,18 @@ class RabbitMQClient extends EventEmitter {
|
|
|
562
598
|
|
|
563
599
|
// Wait for 'reconnected' event
|
|
564
600
|
return new Promise((resolve, reject) => {
|
|
565
|
-
const timeout =
|
|
601
|
+
const timeout = this._setTimeout(() => {
|
|
566
602
|
this.removeListener('reconnected', onReconnected);
|
|
567
603
|
this.removeListener('error', onError);
|
|
568
604
|
reject(new Error(`Reconnection timeout after ${this._reconnectWaitTimeout}ms`));
|
|
569
605
|
}, this._reconnectWaitTimeout);
|
|
570
606
|
|
|
571
607
|
const onReconnected = () => {
|
|
572
|
-
|
|
608
|
+
this._clearTimeout(timeout);
|
|
573
609
|
this.removeListener('reconnected', onReconnected);
|
|
574
610
|
this.removeListener('error', onError);
|
|
575
611
|
// Wait a bit for channels to be fully recreated
|
|
576
|
-
|
|
612
|
+
this._setTimeout(resolve, 500);
|
|
577
613
|
};
|
|
578
614
|
|
|
579
615
|
const onError = (error) => {
|
|
@@ -581,7 +617,7 @@ class RabbitMQClient extends EventEmitter {
|
|
|
581
617
|
if (error.message && error.message.includes('Connection closed unexpectedly')) {
|
|
582
618
|
return; // Expected during reconnection
|
|
583
619
|
}
|
|
584
|
-
|
|
620
|
+
this._clearTimeout(timeout);
|
|
585
621
|
this.removeListener('reconnected', onReconnected);
|
|
586
622
|
this.removeListener('error', onError);
|
|
587
623
|
reject(error);
|
|
@@ -1052,6 +1088,10 @@ class RabbitMQClient extends EventEmitter {
|
|
|
1052
1088
|
|
|
1053
1089
|
async disconnect() {
|
|
1054
1090
|
this._disconnecting = true;
|
|
1091
|
+
|
|
1092
|
+
// Clear all active timers (reconnection, waits, etc.)
|
|
1093
|
+
this._clearAllTimers();
|
|
1094
|
+
|
|
1055
1095
|
// Stop health monitoring
|
|
1056
1096
|
this._stopHealthMonitoring();
|
|
1057
1097
|
|
|
@@ -1324,13 +1364,13 @@ class RabbitMQClient extends EventEmitter {
|
|
|
1324
1364
|
// Use callback-based confirmation - kanály jsou spolehlivé, takže callback vždy dorazí
|
|
1325
1365
|
const confirmPromise = new Promise((resolve, reject) => {
|
|
1326
1366
|
// Set timeout for publish confirmation (configurable)
|
|
1327
|
-
const timeout =
|
|
1367
|
+
const timeout = this._setTimeout(() => {
|
|
1328
1368
|
reject(new Error(`Publish confirmation timeout for queue "${queue}" after ${this._publishConfirmationTimeout}ms`));
|
|
1329
1369
|
}, this._publishConfirmationTimeout);
|
|
1330
1370
|
|
|
1331
1371
|
// Check if channel is still valid before sending
|
|
1332
1372
|
if (!this._channel || this._channel.closed) {
|
|
1333
|
-
|
|
1373
|
+
this._clearTimeout(timeout);
|
|
1334
1374
|
reject(new Error(`Cannot publish: channel is closed for queue "${queue}"`));
|
|
1335
1375
|
return;
|
|
1336
1376
|
}
|
|
@@ -1344,7 +1384,7 @@ class RabbitMQClient extends EventEmitter {
|
|
|
1344
1384
|
try {
|
|
1345
1385
|
originalChannel.sendToQueue(queue, buffer, { persistent, headers }, (err, ok) => {
|
|
1346
1386
|
callbackInvoked = true;
|
|
1347
|
-
|
|
1387
|
+
this._clearTimeout(timeout);
|
|
1348
1388
|
|
|
1349
1389
|
// CRITICAL: Check if channel was closed or recreated during publish
|
|
1350
1390
|
// If channel was recreated, the delivery tag is invalid - ignore this callback
|
|
@@ -1398,11 +1438,11 @@ class RabbitMQClient extends EventEmitter {
|
|
|
1398
1438
|
});
|
|
1399
1439
|
|
|
1400
1440
|
// Set a safety timeout - if callback wasn't invoked and channel closed, retry
|
|
1401
|
-
|
|
1441
|
+
this._setTimeout(() => {
|
|
1402
1442
|
if (!callbackInvoked && (!this._channel || this._channel.closed || this._channel !== originalChannel)) {
|
|
1403
1443
|
console.warn(`[RabbitMQClient] [mq-client-core] [PUBLISH] Callback timeout and channel closed for queue "${queue}", will retry after reconnection`);
|
|
1404
1444
|
if (this._reconnecting) {
|
|
1405
|
-
|
|
1445
|
+
this._clearTimeout(timeout);
|
|
1406
1446
|
this._waitForReconnection().then(() => {
|
|
1407
1447
|
return this.publish(queue, buffer, options);
|
|
1408
1448
|
}).then(resolve).catch(reject);
|
|
@@ -1438,12 +1478,12 @@ class RabbitMQClient extends EventEmitter {
|
|
|
1438
1478
|
// Use callback-based confirmation - kanály jsou spolehlivé
|
|
1439
1479
|
const confirmPromise = new Promise((resolve, reject) => {
|
|
1440
1480
|
// Set timeout for exchange publish confirmation
|
|
1441
|
-
const timeout =
|
|
1481
|
+
const timeout = this._setTimeout(() => {
|
|
1442
1482
|
reject(new Error(`Exchange publish confirmation timeout for exchange "${exchange}" after ${this._publishConfirmationTimeout}ms`));
|
|
1443
1483
|
}, this._publishConfirmationTimeout);
|
|
1444
1484
|
|
|
1445
1485
|
this._channel.publish(exchange, routingKey, buffer, { persistent, headers }, (err, ok) => {
|
|
1446
|
-
|
|
1486
|
+
this._clearTimeout(timeout);
|
|
1447
1487
|
if (err) {
|
|
1448
1488
|
console.error(`[RabbitMQClient] [mq-client-core] [PUBLISH] Exchange publish callback error:`, err.message);
|
|
1449
1489
|
reject(err);
|
|
@@ -1731,7 +1771,13 @@ class RabbitMQClient extends EventEmitter {
|
|
|
1731
1771
|
);
|
|
1732
1772
|
|
|
1733
1773
|
console.log(`[RabbitMQClient] Waiting ${delay}ms before reconnection attempt ${this._reconnectAttempts + 1}...`);
|
|
1734
|
-
await new Promise(resolve =>
|
|
1774
|
+
await new Promise(resolve => this._setTimeout(resolve, delay));
|
|
1775
|
+
|
|
1776
|
+
// Check if we started disconnecting during the wait
|
|
1777
|
+
if (this._disconnecting) {
|
|
1778
|
+
console.log('[RabbitMQClient] Disconnecting during reconnection wait, aborting');
|
|
1779
|
+
return;
|
|
1780
|
+
}
|
|
1735
1781
|
|
|
1736
1782
|
// Attempt to reconnect
|
|
1737
1783
|
console.log(`[RabbitMQClient] Reconnection attempt ${this._reconnectAttempts + 1}...`);
|
|
@@ -1760,7 +1806,7 @@ class RabbitMQClient extends EventEmitter {
|
|
|
1760
1806
|
|
|
1761
1807
|
const connectPromise = amqp.connect(...connectArgs);
|
|
1762
1808
|
const timeoutPromise = new Promise((_, reject) => {
|
|
1763
|
-
|
|
1809
|
+
this._setTimeout(() => reject(new Error('Connection timeout after 10 seconds')), 10000);
|
|
1764
1810
|
});
|
|
1765
1811
|
|
|
1766
1812
|
this._connection = await Promise.race([connectPromise, timeoutPromise]);
|