@onlineapps/conn-infra-mq 1.1.48 → 1.1.50
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
|
@@ -209,6 +209,12 @@ class QueueManager {
|
|
|
209
209
|
* @param {Object} options - Configuration options
|
|
210
210
|
*/
|
|
211
211
|
async setupServiceQueues(serviceName, options = {}) {
|
|
212
|
+
const setupStartTime = Date.now();
|
|
213
|
+
const setupStartTimeISO = new Date().toISOString();
|
|
214
|
+
|
|
215
|
+
console.log(`[QueueManager] [SETUP] Starting setupServiceQueues() for service: ${serviceName} at ${setupStartTimeISO}`);
|
|
216
|
+
console.log(`[QueueManager] [SETUP] Options:`, JSON.stringify(options));
|
|
217
|
+
|
|
212
218
|
const transport = this.client._transport;
|
|
213
219
|
if (!transport || !transport.channel) {
|
|
214
220
|
throw new Error('MQ client not connected');
|
|
@@ -218,6 +224,9 @@ class QueueManager {
|
|
|
218
224
|
// If channel is closed, we cannot continue - this is a critical error
|
|
219
225
|
let channel = transport.queueChannel || transport.channel;
|
|
220
226
|
|
|
227
|
+
console.log(`[QueueManager] [SETUP] Channel state: exists=${!!channel}, closed=${channel ? channel.closed : 'N/A'}`);
|
|
228
|
+
console.log(`[QueueManager] [SETUP] QueueChannel exists: ${!!transport.queueChannel}, MainChannel exists: ${!!transport.channel}`);
|
|
229
|
+
|
|
221
230
|
if (!channel || channel.closed) {
|
|
222
231
|
throw new Error('Queue channel is not available or closed - cannot create business queues');
|
|
223
232
|
}
|
|
@@ -231,6 +240,8 @@ class QueueManager {
|
|
|
231
240
|
if (options.includeWorkflow !== false) {
|
|
232
241
|
queueNames.push({ name: `${serviceName}.workflow`, type: 'workflow' });
|
|
233
242
|
}
|
|
243
|
+
|
|
244
|
+
console.log(`[QueueManager] [SETUP] Will create ${queueNames.length} queues:`, queueNames.map(q => q.name).join(', '));
|
|
234
245
|
|
|
235
246
|
// CRITICAL: Create ALL queues using assertQueue directly (no checkQueue first to avoid channel closure)
|
|
236
247
|
// This ensures all queues are created atomically
|
|
@@ -384,9 +395,13 @@ class QueueManager {
|
|
|
384
395
|
let queueCreated = false;
|
|
385
396
|
|
|
386
397
|
try {
|
|
387
|
-
|
|
388
|
-
console.log(`[QueueManager]
|
|
389
|
-
console.log(`[QueueManager]
|
|
398
|
+
const assertStartTime = Date.now();
|
|
399
|
+
console.log(`[QueueManager] [QUEUE] Starting assertQueue for: ${queueName} at ${new Date().toISOString()}`);
|
|
400
|
+
console.log(`[QueueManager] [QUEUE] Queue type: ${queueInfo.type}`);
|
|
401
|
+
console.log(`[QueueManager] [QUEUE] Channel state: exists=${!!channel}, closed=${channel ? (channel.closed === true ? 'true' : channel.closed === false ? 'false' : 'undefined') : 'N/A'}`);
|
|
402
|
+
console.log(`[QueueManager] [QUEUE] Channel created at: ${channel._createdAt || 'N/A'}`);
|
|
403
|
+
console.log(`[QueueManager] [QUEUE] Channel last operation: ${channel._lastOperation || 'N/A'}`);
|
|
404
|
+
console.log(`[QueueManager] [QUEUE] Queue options:`, JSON.stringify(queueOptions, null, 2));
|
|
390
405
|
|
|
391
406
|
// CRITICAL: Use assertQueue() DIRECTLY - it's idempotent
|
|
392
407
|
// If queue doesn't exist → creates it
|
|
@@ -394,8 +409,10 @@ class QueueManager {
|
|
|
394
409
|
// If queue exists with different params → 406, channel closes (should not happen now, but handle it)
|
|
395
410
|
await channel.assertQueue(queueName, queueOptions);
|
|
396
411
|
|
|
412
|
+
const assertEndTime = Date.now();
|
|
397
413
|
// Queue created or already exists with same params
|
|
398
|
-
console.log(`[QueueManager]
|
|
414
|
+
console.log(`[QueueManager] [QUEUE] ✓ assertQueue succeeded for ${queueName} (took ${assertEndTime - assertStartTime}ms)`);
|
|
415
|
+
console.log(`[QueueManager] [QUEUE] Queue created/verified at: ${new Date().toISOString()}`);
|
|
399
416
|
if (channel && channel._lastOperation !== undefined) {
|
|
400
417
|
channel._lastOperation = `assertQueue ${queueName} succeeded`;
|
|
401
418
|
}
|
|
@@ -528,6 +545,10 @@ class QueueManager {
|
|
|
528
545
|
}
|
|
529
546
|
}
|
|
530
547
|
|
|
548
|
+
const setupEndTime = Date.now();
|
|
549
|
+
console.log(`[QueueManager] [SETUP] ✓ setupServiceQueues completed successfully at ${new Date().toISOString()} (total time: ${setupEndTime - setupStartTime}ms)`);
|
|
550
|
+
console.log(`[QueueManager] [SETUP] Created queues:`, Object.keys(queues).map(k => `${k}=${queues[k]}`).join(', '));
|
|
551
|
+
console.log(`[QueueManager] [SETUP] Channel state after setup: closed=${channel ? channel.closed : 'N/A'}`);
|
|
531
552
|
console.log(`[QueueManager] DEBUG: setupServiceQueues completed successfully, channel closed=${channel.closed}`);
|
|
532
553
|
return queues;
|
|
533
554
|
}
|
|
@@ -68,22 +68,39 @@ class RabbitMQClient extends EventEmitter {
|
|
|
68
68
|
|
|
69
69
|
// Use ConfirmChannel to enable publisher confirms for publish operations
|
|
70
70
|
// BUT: Create a separate regular channel for queue operations to avoid RPC reply queue issues
|
|
71
|
+
const channelStartTime = Date.now();
|
|
71
72
|
this._channel = await this._connection.createConfirmChannel();
|
|
72
|
-
|
|
73
|
+
const channelEndTime = Date.now();
|
|
74
|
+
const channelCreatedAt = new Date().toISOString();
|
|
75
|
+
|
|
76
|
+
console.log(`[RabbitMQClient] [CHANNEL] Created ConfirmChannel at ${channelCreatedAt} (took ${channelEndTime - channelStartTime}ms)`);
|
|
77
|
+
console.log(`[RabbitMQClient] [CHANNEL] ConfirmChannel state: closed=${this._channel.closed}`);
|
|
78
|
+
|
|
79
|
+
this._channel.on('error', (err) => {
|
|
80
|
+
console.error(`[RabbitMQClient] [CHANNEL] ConfirmChannel error at ${new Date().toISOString()}:`, err.message, err.code);
|
|
81
|
+
this.emit('error', err);
|
|
82
|
+
});
|
|
73
83
|
this._channel.on('close', () => {
|
|
84
|
+
console.error(`[RabbitMQClient] [CHANNEL] ConfirmChannel closed at ${new Date().toISOString()}`);
|
|
85
|
+
console.error(`[RabbitMQClient] [CHANNEL] Channel was created at: ${channelCreatedAt}`);
|
|
74
86
|
// Emit a channel close error
|
|
75
87
|
this.emit('error', new Error('RabbitMQ channel closed unexpectedly'));
|
|
76
88
|
});
|
|
77
89
|
|
|
78
90
|
// Create a separate regular channel for queue operations (assertQueue, checkQueue)
|
|
79
91
|
// This avoids RPC reply queue issues with ConfirmChannel.assertQueue()
|
|
92
|
+
const queueChannelStartTime = Date.now();
|
|
80
93
|
this._queueChannel = await this._connection.createChannel();
|
|
94
|
+
const queueChannelEndTime = Date.now();
|
|
81
95
|
|
|
82
96
|
// CRITICAL: Track channel state and close reason
|
|
83
97
|
this._queueChannel._createdAt = new Date().toISOString();
|
|
84
98
|
this._queueChannel._closeReason = null;
|
|
85
99
|
this._queueChannel._lastOperation = null;
|
|
86
100
|
|
|
101
|
+
console.log(`[RabbitMQClient] [CHANNEL] Created queueChannel at ${this._queueChannel._createdAt} (took ${queueChannelEndTime - queueChannelStartTime}ms)`);
|
|
102
|
+
console.log(`[RabbitMQClient] [CHANNEL] QueueChannel state: closed=${this._queueChannel.closed}, connection=${this._connection ? 'connected' : 'null'}`);
|
|
103
|
+
|
|
87
104
|
this._queueChannel.on('error', (err) => {
|
|
88
105
|
// Log but don't emit - queue channel errors are less critical
|
|
89
106
|
console.error('[RabbitMQClient] Queue channel error:', err.message);
|
|
@@ -277,33 +294,71 @@ class RabbitMQClient extends EventEmitter {
|
|
|
277
294
|
// Skip assertQueue for reply queues (they're already created with specific settings)
|
|
278
295
|
// Reply queues start with 'rpc.reply.' and are created as non-durable
|
|
279
296
|
if (!queue.startsWith('rpc.reply.')) {
|
|
280
|
-
//
|
|
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()
|
|
281
300
|
const isInfraQueue = queueConfig.isInfrastructureQueue(queue);
|
|
301
|
+
const isBusinessQueue = queueConfig.isBusinessQueue(queue);
|
|
302
|
+
|
|
303
|
+
let queueOptions = { durable };
|
|
282
304
|
|
|
283
305
|
if (isInfraQueue) {
|
|
284
|
-
// Infrastructure queue -
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
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
|
+
}
|
|
296
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
|
+
// Use queueChannel for assertQueue to avoid RPC reply queue issues
|
|
341
|
+
const channelForAssert = this._queueChannel || this._channel;
|
|
342
|
+
await channelForAssert.assertQueue(queue, queueOptions);
|
|
343
|
+
|
|
344
|
+
const assertEndTime = Date.now();
|
|
345
|
+
console.log(`[RabbitMQClient] [CONSUMER] ✓ Queue ${queue} asserted (took ${assertEndTime - assertStartTime}ms)`);
|
|
297
346
|
}
|
|
298
347
|
// Set prefetch if provided
|
|
299
348
|
if (typeof prefetch === 'number') {
|
|
300
349
|
this._channel.prefetch(prefetch);
|
|
301
350
|
}
|
|
302
351
|
|
|
303
|
-
|
|
352
|
+
const consumeStartTime = Date.now();
|
|
353
|
+
console.log(`[RabbitMQClient] [CONSUMER] Starting consume() for queue: ${queue} at ${new Date().toISOString()}`);
|
|
354
|
+
console.log(`[RabbitMQClient] [CONSUMER] Options: prefetch=${prefetch}, noAck=${noAck}, durable=${durable}`);
|
|
355
|
+
console.log(`[RabbitMQClient] [CONSUMER] Channel state before consume: closed=${this._channel.closed}`);
|
|
356
|
+
|
|
357
|
+
const consumeResult = await this._channel.consume(
|
|
304
358
|
queue,
|
|
305
359
|
async (msg) => {
|
|
306
360
|
if (msg === null) {
|
|
361
|
+
console.log(`[RabbitMQClient] [CONSUMER] Received null message (queue ${queue} was deleted or connection closed)`);
|
|
307
362
|
return;
|
|
308
363
|
}
|
|
309
364
|
try {
|
|
@@ -312,12 +367,31 @@ class RabbitMQClient extends EventEmitter {
|
|
|
312
367
|
this._channel.ack(msg);
|
|
313
368
|
}
|
|
314
369
|
} catch (handlerErr) {
|
|
370
|
+
console.error(`[RabbitMQClient] [CONSUMER] Error processing message from ${queue}:`, handlerErr.message);
|
|
315
371
|
// Negative acknowledge and requeue by default
|
|
316
372
|
this._channel.nack(msg, false, true);
|
|
317
373
|
}
|
|
318
374
|
},
|
|
319
375
|
{ noAck }
|
|
320
376
|
);
|
|
377
|
+
|
|
378
|
+
const consumeEndTime = Date.now();
|
|
379
|
+
const consumerTag = consumeResult.consumerTag;
|
|
380
|
+
console.log(`[RabbitMQClient] [CONSUMER] ✓ Consumer created for queue: ${queue} at ${new Date().toISOString()} (took ${consumeEndTime - consumeStartTime}ms)`);
|
|
381
|
+
console.log(`[RabbitMQClient] [CONSUMER] ConsumerTag: ${consumerTag}`);
|
|
382
|
+
console.log(`[RabbitMQClient] [CONSUMER] Channel state after consume: closed=${this._channel.closed}`);
|
|
383
|
+
|
|
384
|
+
// Store consumerTag for cleanup tracking
|
|
385
|
+
if (!this._consumers) {
|
|
386
|
+
this._consumers = new Map();
|
|
387
|
+
}
|
|
388
|
+
this._consumers.set(queue, {
|
|
389
|
+
consumerTag,
|
|
390
|
+
createdAt: new Date().toISOString(),
|
|
391
|
+
queue,
|
|
392
|
+
prefetch,
|
|
393
|
+
noAck
|
|
394
|
+
});
|
|
321
395
|
} catch (err) {
|
|
322
396
|
this.emit('error', err);
|
|
323
397
|
throw err;
|