@onlineapps/conn-infra-mq 1.1.49 → 1.1.51

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/conn-infra-mq",
3
- "version": "1.1.49",
3
+ "version": "1.1.51",
4
4
  "description": "A promise-based, broker-agnostic client for sending and receiving messages via RabbitMQ",
5
5
  "main": "src/index.js",
6
6
  "repository": {
@@ -294,23 +294,89 @@ class RabbitMQClient extends EventEmitter {
294
294
  // Skip assertQueue for reply queues (they're already created with specific settings)
295
295
  // Reply queues start with 'rpc.reply.' and are created as non-durable
296
296
  if (!queue.startsWith('rpc.reply.')) {
297
- // Check if this is an infrastructure queue - services should NEVER create these
297
+ // CRITICAL: amqplib's channel.consume() may internally call assertQueue() without parameters
298
+ // This causes 406 errors if queue exists with different arguments
299
+ // Solution: Explicitly assertQueue() with correct parameters BEFORE consume()
298
300
  const isInfraQueue = queueConfig.isInfrastructureQueue(queue);
301
+ const isBusinessQueue = queueConfig.isBusinessQueue(queue);
302
+
303
+ let queueOptions = { durable };
299
304
 
300
305
  if (isInfraQueue) {
301
- // Infrastructure queue - should already exist, created by infrastructure initialization
302
- // CRITICAL: Do NOT use checkQueue() - it closes channel on 406 if queue exists with different args
303
- // Just proceed to consume() - if queue doesn't exist, consume() will fail with clear error
304
- // This avoids channel closure from checkQueue() on infrastructure queues
305
- console.log(`[RabbitMQClient] DEBUG: Infrastructure queue ${queue}, skipping checkQueue() - queue should already exist (created by infrastructure initialization)`);
306
- } else {
307
- // Business queue - should already be created by setupServiceQueues() BEFORE consume is called
308
- // CRITICAL: Do NOT use checkQueue() - it closes channel on 404!
309
- // If queue doesn't exist, that's a programming error (setupServiceQueues() should have created it)
310
- // Just proceed to consume - if queue doesn't exist, consume will fail with a clear error
311
- // This avoids channel closure from checkQueue() on non-existent queues
312
- console.log(`[RabbitMQClient] DEBUG: Business queue ${queue}, skipping checkQueue() - queue should already exist`);
306
+ // Infrastructure queue - use central config
307
+ try {
308
+ const infraConfig = queueConfig.getInfrastructureQueueConfig(queue);
309
+ queueOptions = {
310
+ durable: infraConfig.durable !== false,
311
+ arguments: { ...infraConfig.arguments }
312
+ };
313
+ console.log(`[RabbitMQClient] [CONSUMER] Asserting infrastructure queue ${queue} with config from queueConfig`);
314
+ } catch (configErr) {
315
+ console.warn(`[RabbitMQClient] [CONSUMER] Infrastructure queue config not found for ${queue}, using default:`, configErr.message);
316
+ }
317
+ } else if (isBusinessQueue) {
318
+ // Business queue - use central config
319
+ try {
320
+ const parsed = queueConfig.parseBusinessQueue(queue);
321
+ if (parsed) {
322
+ const businessConfig = queueConfig.getBusinessQueueConfig(parsed.queueType, parsed.serviceName);
323
+ queueOptions = {
324
+ durable: businessConfig.durable !== false,
325
+ arguments: { ...businessConfig.arguments }
326
+ };
327
+ console.log(`[RabbitMQClient] [CONSUMER] Asserting business queue ${queue} with config from queueConfig`);
328
+ }
329
+ } catch (configErr) {
330
+ console.warn(`[RabbitMQClient] [CONSUMER] Business queue config not found for ${queue}, using default:`, configErr.message);
331
+ }
313
332
  }
333
+
334
+ // CRITICAL: Assert queue with correct parameters BEFORE consume()
335
+ // This prevents amqplib from calling assertQueue() internally without parameters
336
+ const assertStartTime = Date.now();
337
+ console.log(`[RabbitMQClient] [CONSUMER] Asserting queue ${queue} before consume() at ${new Date().toISOString()}`);
338
+ console.log(`[RabbitMQClient] [CONSUMER] Queue options:`, JSON.stringify(queueOptions, null, 2));
339
+
340
+ // CRITICAL: Ensure _queueChannel is open before using it
341
+ // If it's closed (e.g., due to 406 error), recreate it
342
+ if (!this._queueChannel || this._queueChannel.closed) {
343
+ console.warn(`[RabbitMQClient] [CONSUMER] _queueChannel is closed or null, recreating...`);
344
+ try {
345
+ this._queueChannel = await this._connection.createChannel();
346
+ this._queueChannel._createdAt = new Date().toISOString();
347
+ this._queueChannel._closeReason = null;
348
+ this._queueChannel._lastOperation = null;
349
+
350
+ // Re-attach event listeners
351
+ this._queueChannel.on('error', (err) => {
352
+ console.error('[RabbitMQClient] Queue channel error:', err.message);
353
+ console.error('[RabbitMQClient] Error code:', err.code);
354
+ console.error('[RabbitMQClient] Channel created at:', this._queueChannel._createdAt);
355
+ console.error('[RabbitMQClient] Last operation:', this._queueChannel._lastOperation);
356
+ this._queueChannel._closeReason = `Error: ${err.message} (code: ${err.code})`;
357
+ });
358
+ this._queueChannel.on('close', () => {
359
+ console.error('[RabbitMQClient] Queue channel closed');
360
+ console.error('[RabbitMQClient] Channel created at:', this._queueChannel._createdAt);
361
+ console.error('[RabbitMQClient] Close reason:', this._queueChannel._closeReason || 'Unknown');
362
+ console.error('[RabbitMQClient] Last operation:', this._queueChannel._lastOperation);
363
+ });
364
+
365
+ console.log(`[RabbitMQClient] [CONSUMER] ✓ _queueChannel recreated`);
366
+ } catch (recreateErr) {
367
+ console.error(`[RabbitMQClient] [CONSUMER] ✗ Failed to recreate _queueChannel:`, recreateErr.message);
368
+ throw new Error(`Cannot assertQueue: _queueChannel is closed and recreation failed: ${recreateErr.message}`);
369
+ }
370
+ }
371
+
372
+ // Use queueChannel for assertQueue to avoid RPC reply queue issues
373
+ const channelForAssert = this._queueChannel;
374
+ this._queueChannel._lastOperation = `About to assertQueue ${queue}`;
375
+ await channelForAssert.assertQueue(queue, queueOptions);
376
+ this._queueChannel._lastOperation = `assertQueue ${queue} succeeded`;
377
+
378
+ const assertEndTime = Date.now();
379
+ console.log(`[RabbitMQClient] [CONSUMER] ✓ Queue ${queue} asserted (took ${assertEndTime - assertStartTime}ms)`);
314
380
  }
315
381
  // Set prefetch if provided
316
382
  if (typeof prefetch === 'number') {
@@ -322,6 +388,15 @@ class RabbitMQClient extends EventEmitter {
322
388
  console.log(`[RabbitMQClient] [CONSUMER] Options: prefetch=${prefetch}, noAck=${noAck}, durable=${durable}`);
323
389
  console.log(`[RabbitMQClient] [CONSUMER] Channel state before consume: closed=${this._channel.closed}`);
324
390
 
391
+ // CRITICAL WARNING: amqplib's channel.consume() may internally call assertQueue() WITHOUT parameters
392
+ // This happens if the queue doesn't exist or if amqplib needs to verify queue existence
393
+ // If queue exists with different arguments (e.g., x-message-ttl), this will cause 406 PRECONDITION-FAILED
394
+ // and close the channel. That's why we assertQueue() with correct parameters BEFORE consume() above.
395
+ console.log(`[RabbitMQClient] [CONSUMER] ⚠ WARNING: About to call amqplib's channel.consume() for queue: ${queue}`);
396
+ console.log(`[RabbitMQClient] [CONSUMER] ⚠ WARNING: amqplib may internally call assertQueue() WITHOUT parameters if queue doesn't exist`);
397
+ console.log(`[RabbitMQClient] [CONSUMER] ⚠ WARNING: If queue exists with different arguments, this will cause 406 PRECONDITION-FAILED and close channel`);
398
+ console.log(`[RabbitMQClient] [CONSUMER] ⚠ WARNING: We already asserted queue with correct parameters above - this should prevent 406`);
399
+
325
400
  const consumeResult = await this._channel.consume(
326
401
  queue,
327
402
  async (msg) => {