@onlineapps/mq-client-core 1.0.81 → 1.0.82
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 +17 -10
package/package.json
CHANGED
|
@@ -1716,25 +1716,23 @@ class RabbitMQClient extends EventEmitter {
|
|
|
1716
1716
|
try {
|
|
1717
1717
|
await onMessage(msg);
|
|
1718
1718
|
// Acknowledge message after successful processing
|
|
1719
|
-
if (!noAck) {
|
|
1720
|
-
// Only ack on the original channel instance; if it's gone/closed, do not attempt ack.
|
|
1719
|
+
if (!noAck && !msg._mqProcessed) {
|
|
1721
1720
|
if (!channelForMsg || channelForMsg.closed) {
|
|
1722
1721
|
console.warn('[RabbitMQClient] [mq-client-core] [CONSUMER] Cannot ack - consumer channel is closed/recreated (message will be requeued by broker)');
|
|
1723
1722
|
return;
|
|
1724
1723
|
}
|
|
1725
1724
|
try {
|
|
1726
1725
|
channelForMsg.ack(msg);
|
|
1726
|
+
msg._mqProcessed = true;
|
|
1727
1727
|
} catch (ackErr) {
|
|
1728
|
-
// If the channel was closed/recreated during processing, delivery tag becomes invalid.
|
|
1729
|
-
// Do NOT send further acks that would close the channel with 406.
|
|
1730
1728
|
const m = ackErr && ackErr.message ? ackErr.message : '';
|
|
1731
1729
|
if (m.includes('unknown delivery tag') || m.includes('PRECONDITION_FAILED') || m.includes('Channel closed')) {
|
|
1730
|
+
msg._mqProcessed = true;
|
|
1732
1731
|
console.warn('[RabbitMQClient] [mq-client-core] [CONSUMER] Ack failed due to invalid delivery tag / closed channel (message delivery will be handled by broker)', { error: m });
|
|
1733
1732
|
return;
|
|
1734
1733
|
}
|
|
1735
1734
|
throw ackErr;
|
|
1736
1735
|
}
|
|
1737
|
-
// Structured log: message acknowledged
|
|
1738
1736
|
this._log.output(wfId, 'MSG_ACKED', {
|
|
1739
1737
|
handler: 'consume',
|
|
1740
1738
|
function: 'RabbitMQClient.consume',
|
|
@@ -1742,7 +1740,6 @@ class RabbitMQClient extends EventEmitter {
|
|
|
1742
1740
|
output: { status: 'acknowledged', queue }
|
|
1743
1741
|
});
|
|
1744
1742
|
|
|
1745
|
-
// Track ack (decrement in-flight)
|
|
1746
1743
|
if (tracking) {
|
|
1747
1744
|
tracking.inFlight = Math.max(0, tracking.inFlight - 1);
|
|
1748
1745
|
tracking.lastCheck = Date.now();
|
|
@@ -1750,16 +1747,16 @@ class RabbitMQClient extends EventEmitter {
|
|
|
1750
1747
|
}
|
|
1751
1748
|
} catch (handlerErr) {
|
|
1752
1749
|
// Negative acknowledge and requeue by default
|
|
1753
|
-
//
|
|
1754
|
-
if (channelForMsg && !channelForMsg.closed) {
|
|
1750
|
+
// Skip if already acked/nacked by the handler (prevents double ack → 406)
|
|
1751
|
+
if (!msg._mqProcessed && channelForMsg && !channelForMsg.closed) {
|
|
1755
1752
|
try {
|
|
1756
1753
|
channelForMsg.nack(msg, false, true);
|
|
1754
|
+
msg._mqProcessed = true;
|
|
1757
1755
|
} catch (nackErr) {
|
|
1758
|
-
|
|
1756
|
+
msg._mqProcessed = true;
|
|
1759
1757
|
console.warn(`[RabbitMQClient] [mq-client-core] Failed to nack message (channel may be closed): ${nackErr.message}`);
|
|
1760
1758
|
}
|
|
1761
1759
|
}
|
|
1762
|
-
// Track nack (decrement in-flight)
|
|
1763
1760
|
if (tracking) {
|
|
1764
1761
|
tracking.inFlight = Math.max(0, tracking.inFlight - 1);
|
|
1765
1762
|
tracking.lastCheck = Date.now();
|
|
@@ -2219,14 +2216,19 @@ class RabbitMQClient extends EventEmitter {
|
|
|
2219
2216
|
* @param {Object} msg - RabbitMQ message object.
|
|
2220
2217
|
*/
|
|
2221
2218
|
async ack(msg) {
|
|
2219
|
+
if (msg._mqProcessed) {
|
|
2220
|
+
return; // Already acked/nacked — idempotent
|
|
2221
|
+
}
|
|
2222
2222
|
if (!this._consumerChannel) {
|
|
2223
2223
|
throw new Error('Cannot ack: consumer channel is not initialized');
|
|
2224
2224
|
}
|
|
2225
2225
|
try {
|
|
2226
2226
|
this._consumerChannel.ack(msg);
|
|
2227
|
+
msg._mqProcessed = true;
|
|
2227
2228
|
} catch (err) {
|
|
2228
2229
|
const m = err && err.message ? err.message : '';
|
|
2229
2230
|
if (m.includes('unknown delivery tag') || m.includes('PRECONDITION_FAILED') || m.includes('Channel closed')) {
|
|
2231
|
+
msg._mqProcessed = true;
|
|
2230
2232
|
console.warn('[RabbitMQClient] [mq-client-core] Cannot ack - consumer channel is closed/recreated (delivery tag invalid)', { error: m });
|
|
2231
2233
|
return;
|
|
2232
2234
|
}
|
|
@@ -2241,15 +2243,20 @@ class RabbitMQClient extends EventEmitter {
|
|
|
2241
2243
|
* @param {Object} [options] - { requeue: boolean }.
|
|
2242
2244
|
*/
|
|
2243
2245
|
async nack(msg, options = {}) {
|
|
2246
|
+
if (msg._mqProcessed) {
|
|
2247
|
+
return; // Already acked/nacked — idempotent
|
|
2248
|
+
}
|
|
2244
2249
|
if (!this._consumerChannel) {
|
|
2245
2250
|
throw new Error('Cannot nack: consumer channel is not initialized');
|
|
2246
2251
|
}
|
|
2247
2252
|
const requeue = options.requeue !== undefined ? options.requeue : true;
|
|
2248
2253
|
try {
|
|
2249
2254
|
this._consumerChannel.nack(msg, false, requeue);
|
|
2255
|
+
msg._mqProcessed = true;
|
|
2250
2256
|
} catch (err) {
|
|
2251
2257
|
const m = err && err.message ? err.message : '';
|
|
2252
2258
|
if (m.includes('unknown delivery tag') || m.includes('PRECONDITION_FAILED') || m.includes('Channel closed')) {
|
|
2259
|
+
msg._mqProcessed = true;
|
|
2253
2260
|
console.warn('[RabbitMQClient] [mq-client-core] Cannot nack - consumer channel is closed/recreated (delivery tag invalid)', { error: m });
|
|
2254
2261
|
return;
|
|
2255
2262
|
}
|