@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 +1 -1
- package/src/layers/QueueManager.js +157 -27
package/package.json
CHANGED
|
@@ -365,24 +365,132 @@ class QueueManager {
|
|
|
365
365
|
}
|
|
366
366
|
}
|
|
367
367
|
|
|
368
|
-
// CRITICAL:
|
|
369
|
-
//
|
|
370
|
-
//
|
|
371
|
-
let
|
|
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}
|
|
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 (
|
|
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,
|
|
401
|
-
//
|
|
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.
|
|
404
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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
|
-
|
|
522
|
+
channel.on('error', (err) => {
|
|
417
523
|
console.error('[RabbitMQClient] Recreated queue channel error:', err.message);
|
|
418
|
-
|
|
524
|
+
channel._closeReason = `Error: ${err.message} (code: ${err.code})`;
|
|
419
525
|
});
|
|
420
|
-
|
|
526
|
+
channel.on('close', () => {
|
|
421
527
|
console.error('[RabbitMQClient] Recreated queue channel closed');
|
|
422
|
-
console.error('[RabbitMQClient] Close reason:',
|
|
528
|
+
console.error('[RabbitMQClient] Close reason:', channel._closeReason || 'Unknown');
|
|
423
529
|
});
|
|
424
530
|
|
|
425
|
-
channel = newChannel;
|
|
426
531
|
transport._queueChannel = channel;
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
console.
|
|
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
|