@onlineapps/mq-client-core 1.0.31 → 1.0.32

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlineapps/mq-client-core",
3
- "version": "1.0.31",
3
+ "version": "1.0.32",
4
4
  "description": "Core MQ client library for RabbitMQ - shared by infrastructure services and connectors",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -32,8 +32,9 @@ class RabbitMQClient extends EventEmitter {
32
32
  );
33
33
 
34
34
  this._connection = null;
35
- this._channel = null;
36
- this._queueChannel = null;
35
+ this._channel = null; // ConfirmChannel for publish operations
36
+ this._queueChannel = null; // Regular channel for queue operations (assertQueue, checkQueue)
37
+ this._consumerChannel = null; // Dedicated channel for consume operations
37
38
  }
38
39
 
39
40
  /**
@@ -109,6 +110,18 @@ class RabbitMQClient extends EventEmitter {
109
110
  this._queueChannel.on('close', () => {
110
111
  console.warn('[RabbitMQClient] Queue channel closed');
111
112
  });
113
+
114
+ // Create a dedicated channel for consume operations
115
+ // This prevents channel conflicts between publish (ConfirmChannel) and consume operations
116
+ this._consumerChannel = await this._connection.createChannel();
117
+ this._consumerChannel.on('error', (err) => {
118
+ console.warn('[RabbitMQClient] Consumer channel error:', err.message);
119
+ this.emit('error', err);
120
+ });
121
+ this._consumerChannel.on('close', () => {
122
+ console.warn('[RabbitMQClient] Consumer channel closed');
123
+ this.emit('error', new Error('RabbitMQ consumer channel closed unexpectedly'));
124
+ });
112
125
  } catch (err) {
113
126
  // Cleanup partially created resources
114
127
  if (this._connection) {
@@ -128,6 +141,14 @@ class RabbitMQClient extends EventEmitter {
128
141
  * @returns {Promise<void>}
129
142
  */
130
143
  async disconnect() {
144
+ try {
145
+ if (this._consumerChannel) {
146
+ await this._consumerChannel.close();
147
+ this._consumerChannel = null;
148
+ }
149
+ } catch (err) {
150
+ console.warn('[RabbitMQClient] Error closing consumer channel:', err.message);
151
+ }
131
152
  try {
132
153
  if (this._queueChannel) {
133
154
  await this._queueChannel.close();
@@ -270,8 +291,40 @@ class RabbitMQClient extends EventEmitter {
270
291
  * @throws {Error} If consume setup fails or channel is not available.
271
292
  */
272
293
  async consume(queue, onMessage, options = {}) {
273
- if (!this._channel) {
274
- throw new Error('Cannot consume: channel is not initialized');
294
+ // Use dedicated consumer channel instead of ConfirmChannel
295
+ // ConfirmChannel is optimized for publish operations, not consume
296
+ if (!this._consumerChannel) {
297
+ // Recreate consumer channel if closed
298
+ if (this._connection && !this._connection.closed) {
299
+ this._consumerChannel = await this._connection.createChannel();
300
+ this._consumerChannel.on('error', (err) => {
301
+ console.warn('[RabbitMQClient] Consumer channel error:', err.message);
302
+ this.emit('error', err);
303
+ });
304
+ this._consumerChannel.on('close', () => {
305
+ console.warn('[RabbitMQClient] Consumer channel closed');
306
+ this.emit('error', new Error('RabbitMQ consumer channel closed unexpectedly'));
307
+ });
308
+ } else {
309
+ throw new Error('Cannot consume: consumer channel is not initialized and connection is closed');
310
+ }
311
+ }
312
+
313
+ if (this._consumerChannel.closed) {
314
+ // Recreate consumer channel if closed
315
+ if (this._connection && !this._connection.closed) {
316
+ this._consumerChannel = await this._connection.createChannel();
317
+ this._consumerChannel.on('error', (err) => {
318
+ console.warn('[RabbitMQClient] Consumer channel error:', err.message);
319
+ this.emit('error', err);
320
+ });
321
+ this._consumerChannel.on('close', () => {
322
+ console.warn('[RabbitMQClient] Consumer channel closed');
323
+ this.emit('error', new Error('RabbitMQ consumer channel closed unexpectedly'));
324
+ });
325
+ } else {
326
+ throw new Error('Cannot consume: consumer channel is closed and connection is closed');
327
+ }
275
328
  }
276
329
 
277
330
  const durable = options.durable !== undefined ? options.durable : this._config.durable;
@@ -368,32 +421,21 @@ class RabbitMQClient extends EventEmitter {
368
421
  }
369
422
  }
370
423
  }
371
- // Set prefetch if provided
424
+ // Set prefetch if provided (on consumer channel)
372
425
  if (typeof prefetch === 'number') {
373
- this._channel.prefetch(prefetch);
426
+ this._consumerChannel.prefetch(prefetch);
374
427
  }
375
428
 
376
429
  // CRITICAL: Log before calling amqplib's consume()
377
430
  // amqplib's consume() may internally call assertQueue() without parameters if queue doesn't exist
378
431
  // This would create queue with default arguments (no TTL), causing 406 errors later
379
- console.log(`[RabbitMQClient] [mq-client-core] [CONSUMER] About to call amqplib's channel.consume(${queue})`);
432
+ console.log(`[RabbitMQClient] [mq-client-core] [CONSUMER] About to call amqplib's consumerChannel.consume(${queue})`);
380
433
  console.log(`[RabbitMQClient] [mq-client-core] [CONSUMER] ⚠ WARNING: amqplib's consume() may internally call assertQueue() WITHOUT parameters if queue doesn't exist`);
381
434
  console.log(`[RabbitMQClient] [mq-client-core] [CONSUMER] ⚠ WARNING: We already asserted queue with correct parameters above - this should prevent auto-creation`);
382
435
 
383
- // CRITICAL: Wrap amqplib's channel.consume() to intercept any internal assertQueue() calls
384
- // This will help us identify if amqplib is creating the queue without TTL
385
- const originalConsume = this._channel.consume.bind(this._channel);
386
- const originalAssertQueue = this._channel.assertQueue.bind(this._channel);
387
-
388
- // Intercept assertQueue() calls to log them
389
- this._channel.assertQueue = async function(queueName, options) {
390
- console.log(`[RabbitMQClient] [mq-client-core] [INTERCEPT] assertQueue() called for: ${queueName}`);
391
- console.log(`[RabbitMQClient] [mq-client-core] [INTERCEPT] Options:`, JSON.stringify(options || {}, null, 2));
392
- console.log(`[RabbitMQClient] [mq-client-core] [INTERCEPT] Stack trace:`, new Error().stack.split('\n').slice(1, 10).join('\n'));
393
- return originalAssertQueue.call(this, queueName, options);
394
- };
395
-
396
- await this._channel.consume(
436
+ // Use dedicated consumer channel for consume operations
437
+ // This prevents conflicts with ConfirmChannel used for publish operations
438
+ const consumeResult = await this._consumerChannel.consume(
397
439
  queue,
398
440
  async (msg) => {
399
441
  if (msg === null) {
@@ -402,15 +444,18 @@ class RabbitMQClient extends EventEmitter {
402
444
  try {
403
445
  await onMessage(msg);
404
446
  if (!noAck) {
405
- this._channel.ack(msg);
447
+ this._consumerChannel.ack(msg);
406
448
  }
407
449
  } catch (handlerErr) {
408
450
  // Negative acknowledge and requeue by default
409
- this._channel.nack(msg, false, true);
451
+ this._consumerChannel.nack(msg, false, true);
410
452
  }
411
453
  },
412
454
  { noAck }
413
455
  );
456
+
457
+ // Return consumer tag for cancellation
458
+ return consumeResult.consumerTag;
414
459
  } catch (err) {
415
460
  this.emit('error', err);
416
461
  throw err;