@onlineapps/conn-infra-mq 1.1.43 → 1.1.44

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.43",
3
+ "version": "1.1.44",
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": {
@@ -365,24 +365,132 @@ class QueueManager {
365
365
  }
366
366
  }
367
367
 
368
- // CRITICAL: Use assertQueue() DIRECTLY - it's idempotent and safer than checkQueue() + assertQueue()
369
- // assertQueue() creates queue if it doesn't exist, or does nothing if it exists with same params
370
- // If queue exists with different params (406), channel closes but that's OK - we accept the queue
371
- let queueCreated = false;
368
+ // CRITICAL: Check if queue exists with different arguments BEFORE assertQueue
369
+ // If queue exists with wrong args, delete it and create with correct args
370
+ // This prevents 406 errors and channel closure
371
+ let queueNeedsRecreation = false;
372
+ let existingQueueInfo = null;
373
+
374
+ try {
375
+ // Use checkQueue to get existing queue info (this may close channel on 404, but that's OK)
376
+ // We use a separate channel for this check to avoid closing the main channel
377
+ let checkChannel = channel;
378
+ try {
379
+ existingQueueInfo = await checkChannel.checkQueue(queueName);
380
+ console.log(`[QueueManager] DEBUG: Queue ${queueName} exists, checking arguments...`);
381
+
382
+ // Compare existing queue arguments with expected arguments
383
+ const existingArgs = existingQueueInfo.arguments || {};
384
+ const expectedArgs = queueOptions.arguments || {};
385
+
386
+ // Check if arguments match (deep comparison of key values)
387
+ const argsMatch = Object.keys(expectedArgs).every(key => {
388
+ const existing = existingArgs[key];
389
+ const expected = expectedArgs[key];
390
+
391
+ // Handle special case: x-dead-letter-routing-key may have placeholder replaced
392
+ if (key === 'x-dead-letter-routing-key') {
393
+ const expectedRoutingKey = expected.replace('{service}', serviceName);
394
+ return existing === expectedRoutingKey || existing === expected;
395
+ }
396
+
397
+ return existing === expected;
398
+ }) && Object.keys(existingArgs).length === Object.keys(expectedArgs).length;
399
+
400
+ if (!argsMatch) {
401
+ console.warn(`[QueueManager] Queue ${queueName} exists with different arguments, will delete and recreate`);
402
+ console.warn(`[QueueManager] Existing args:`, JSON.stringify(existingArgs));
403
+ console.warn(`[QueueManager] Expected args:`, JSON.stringify(expectedArgs));
404
+ queueNeedsRecreation = true;
405
+ } else {
406
+ console.log(`[QueueManager] DEBUG: Queue ${queueName} exists with correct arguments`);
407
+ }
408
+ } catch (checkErr) {
409
+ if (checkErr.code === 404) {
410
+ // Queue doesn't exist - will be created
411
+ console.log(`[QueueManager] DEBUG: Queue ${queueName} does not exist, will create`);
412
+ existingQueueInfo = null;
413
+ } else {
414
+ // Other error - log but continue (will try assertQueue anyway)
415
+ console.warn(`[QueueManager] checkQueue failed for ${queueName}:`, checkErr.message);
416
+ // Channel may be closed, but we'll recreate it before assertQueue
417
+ }
418
+ }
419
+ } catch (checkError) {
420
+ // If checkQueue fails (e.g., channel closed), continue anyway
421
+ // assertQueue will handle it
422
+ console.warn(`[QueueManager] Failed to check queue ${queueName}, will try assertQueue:`, checkError.message);
423
+ }
424
+
425
+ // If queue exists with wrong args, delete it first
426
+ if (queueNeedsRecreation && existingQueueInfo) {
427
+ console.log(`[QueueManager] Deleting queue ${queueName} to recreate with correct arguments...`);
428
+ try {
429
+ // Recreate channel if needed (checkQueue may have closed it)
430
+ if (!channel || channel.closed) {
431
+ channel = await transport._connection.createChannel();
432
+ channel._createdAt = new Date().toISOString();
433
+ channel._closeReason = null;
434
+ channel._lastOperation = `Recreated before deleteQueue ${queueName}`;
435
+
436
+ channel.on('error', (err) => {
437
+ console.error('[RabbitMQClient] Recreated queue channel error:', err.message);
438
+ channel._closeReason = `Error: ${err.message} (code: ${err.code})`;
439
+ });
440
+ channel.on('close', () => {
441
+ console.error('[RabbitMQClient] Recreated queue channel closed');
442
+ console.error('[RabbitMQClient] Close reason:', channel._closeReason || 'Unknown');
443
+ });
444
+
445
+ transport._queueChannel = channel;
446
+ }
447
+
448
+ await channel.deleteQueue(queueName);
449
+ console.log(`[QueueManager] ✓ Deleted queue ${queueName} for recreation`);
450
+ } catch (deleteErr) {
451
+ // If delete fails, log warning but continue - assertQueue will handle it
452
+ console.warn(`[QueueManager] Failed to delete queue ${queueName}, will try assertQueue anyway:`, deleteErr.message);
453
+ }
454
+ }
455
+
456
+ // CRITICAL: Ensure channel is usable before assertQueue
457
+ // Recreate if closed (may have been closed by checkQueue or deleteQueue)
458
+ if (!channel || channel.closed) {
459
+ console.log(`[QueueManager] DEBUG: Channel is closed before assertQueue ${queueName}, recreating...`);
460
+ channel = await transport._connection.createChannel();
461
+ channel._createdAt = new Date().toISOString();
462
+ channel._closeReason = null;
463
+ channel._lastOperation = `Recreated before assertQueue ${queueName}`;
464
+
465
+ channel.on('error', (err) => {
466
+ console.error('[RabbitMQClient] Recreated queue channel error:', err.message);
467
+ channel._closeReason = `Error: ${err.message} (code: ${err.code})`;
468
+ });
469
+ channel.on('close', () => {
470
+ console.error('[RabbitMQClient] Recreated queue channel closed');
471
+ console.error('[RabbitMQClient] Close reason:', channel._closeReason || 'Unknown');
472
+ });
473
+
474
+ transport._queueChannel = channel;
475
+ console.log(`[QueueManager] DEBUG: Channel recreated before assertQueue ${queueName}`);
476
+ }
372
477
 
373
478
  // Track operation on channel for debugging
374
479
  if (channel && channel._lastOperation !== undefined) {
375
480
  channel._lastOperation = `About to assertQueue ${queueName}`;
376
481
  }
377
482
 
483
+ let queueCreated = false;
484
+
378
485
  try {
379
- console.log(`[QueueManager] DEBUG: About to assertQueue ${queueName} directly (idempotent)`);
486
+ console.log(`[QueueManager] DEBUG: About to assertQueue ${queueName} with correct arguments`);
380
487
  console.log(`[QueueManager] DEBUG: Channel state: exists=${!!channel}, closed=${channel ? (channel.closed === true ? 'true' : channel.closed === false ? 'false' : 'undefined') : 'N/A'}`);
488
+ console.log(`[QueueManager] DEBUG: Queue options:`, JSON.stringify(queueOptions));
381
489
 
382
490
  // CRITICAL: Use assertQueue() DIRECTLY - it's idempotent
383
491
  // If queue doesn't exist → creates it
384
492
  // If queue exists with same params → does nothing (OK)
385
- // If queue exists with different params → 406, channel closes (but we accept the queue)
493
+ // If queue exists with different params → 406, channel closes (should not happen now, but handle it)
386
494
  await channel.assertQueue(queueName, queueOptions);
387
495
 
388
496
  // Queue created or already exists with same params
@@ -397,37 +505,59 @@ class QueueManager {
397
505
  console.log(`[QueueManager] DEBUG: assertQueue failed for ${queueName}:`, assertErr.message);
398
506
  console.log(`[QueueManager] DEBUG: Error code: ${assertErr.code}`);
399
507
 
400
- // If 406 PRECONDITION-FAILED, queue exists with different args - accept it
401
- // Channel is closed by server, but that's OK - queue exists and we can use it
508
+ // If 406 PRECONDITION-FAILED, this should not happen if we deleted the queue
509
+ // But handle it anyway - queue exists with different args
402
510
  if (assertErr.code === 406) {
403
- console.warn(`[QueueManager] Queue ${queueName} exists with different arguments (406), accepting as-is:`, assertErr.message);
404
- queues[queueInfo.type] = queueName;
405
- queueCreated = true;
406
- console.info(`[QueueManager] ✓ Business queue exists (different args, accepted): ${queueName}`);
511
+ console.error(`[QueueManager] ERROR: Queue ${queueName} still has different arguments after deletion attempt!`);
512
+ console.error(`[QueueManager] This should not happen - queue may have been recreated by another process`);
407
513
 
408
- // Channel is closed, but we need to recreate it for next queue
409
- // Don't throw error - queue exists, we just need new channel
514
+ // Try to delete and recreate one more time
410
515
  try {
411
- const newChannel = await transport._connection.createChannel();
412
- newChannel._createdAt = new Date().toISOString();
413
- newChannel._closeReason = null;
414
- newChannel._lastOperation = `Recreated after 406 for ${queueName}`;
516
+ // Recreate channel (closed by 406)
517
+ channel = await transport._connection.createChannel();
518
+ channel._createdAt = new Date().toISOString();
519
+ channel._closeReason = null;
520
+ channel._lastOperation = `Recreated after 406 for ${queueName}`;
415
521
 
416
- newChannel.on('error', (err) => {
522
+ channel.on('error', (err) => {
417
523
  console.error('[RabbitMQClient] Recreated queue channel error:', err.message);
418
- newChannel._closeReason = `Error: ${err.message} (code: ${err.code})`;
524
+ channel._closeReason = `Error: ${err.message} (code: ${err.code})`;
419
525
  });
420
- newChannel.on('close', () => {
526
+ channel.on('close', () => {
421
527
  console.error('[RabbitMQClient] Recreated queue channel closed');
422
- console.error('[RabbitMQClient] Close reason:', newChannel._closeReason || 'Unknown');
528
+ console.error('[RabbitMQClient] Close reason:', channel._closeReason || 'Unknown');
423
529
  });
424
530
 
425
- channel = newChannel;
426
531
  transport._queueChannel = channel;
427
- console.log(`[QueueManager] DEBUG: Channel recreated after 406 for ${queueName}`);
428
- } catch (recreateErr) {
429
- // Even if channel recreation fails, queue exists - log warning but continue
430
- console.warn(`[QueueManager] Failed to recreate channel after 406 for ${queueName}, but queue exists:`, recreateErr.message);
532
+
533
+ // Delete queue
534
+ await channel.deleteQueue(queueName);
535
+ console.log(`[QueueManager] Deleted queue ${queueName} after 406 error`);
536
+
537
+ // Recreate channel again (deleteQueue may close it)
538
+ channel = await transport._connection.createChannel();
539
+ channel._createdAt = new Date().toISOString();
540
+ channel._closeReason = null;
541
+ channel._lastOperation = `Recreated after deleteQueue ${queueName}`;
542
+
543
+ channel.on('error', (err) => {
544
+ console.error('[RabbitMQClient] Recreated queue channel error:', err.message);
545
+ channel._closeReason = `Error: ${err.message} (code: ${err.code})`;
546
+ });
547
+ channel.on('close', () => {
548
+ console.error('[RabbitMQClient] Recreated queue channel closed');
549
+ console.error('[RabbitMQClient] Close reason:', channel._closeReason || 'Unknown');
550
+ });
551
+
552
+ transport._queueChannel = channel;
553
+
554
+ // Try assertQueue again
555
+ await channel.assertQueue(queueName, queueOptions);
556
+ console.log(`[QueueManager] ✓ Recreated queue ${queueName} with correct arguments`);
557
+ queues[queueInfo.type] = queueName;
558
+ queueCreated = true;
559
+ } catch (retryErr) {
560
+ throw new Error(`Failed to recreate queue ${queueName} after 406 error: ${retryErr.message}`);
431
561
  }
432
562
  } else {
433
563
  // Other error - critical, cannot continue