@onlineapps/conn-infra-mq 1.1.15 → 1.1.17

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.15",
3
+ "version": "1.1.17",
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": {
@@ -82,43 +82,55 @@ class QueueManager {
82
82
  // Track managed queue
83
83
  this.managedQueues.add(queueName);
84
84
 
85
- // Use checkQueue first to avoid 406 PRECONDITION-FAILED closing the channel
86
- // If queue doesn't exist (404), then assertQueue to create it
87
85
  // IMPORTANT: Check if channel is still open before operations
88
86
  if (!channel || channel.closed) {
89
87
  throw new Error('Channel is closed - cannot ensure queue');
90
88
  }
91
89
 
90
+ // CRITICAL: amqplib's assertQueue() uses RPC pattern even on regular channels
91
+ // This causes channel closure if reply queue doesn't exist
92
+ // Solution: Use checkQueue to verify existence, and if queue doesn't exist,
93
+ // it will be created automatically on first sendToQueue() with passive: false
94
+ // For now, we'll use checkQueue and let the queue be created lazily on first publish
95
+
92
96
  try {
93
97
  const queueInfo = await channel.checkQueue(queueName);
94
98
  // Queue exists - return queue info
95
99
  return queueInfo;
96
100
  } catch (checkErr) {
97
- // Check if channel is still open before assertQueue
101
+ // Check if channel is still open
98
102
  if (!channel || channel.closed) {
99
- throw new Error('Channel closed during checkQueue - cannot create queue');
103
+ throw new Error('Channel closed during checkQueue - cannot verify queue');
100
104
  }
101
105
 
102
- // If queue doesn't exist (404), create it with provided options
106
+ // If queue doesn't exist (404), we'll let it be created lazily on first publish
107
+ // This avoids RPC reply queue issues with assertQueue()
103
108
  if (checkErr.code === 404) {
104
- try {
105
- return await channel.assertQueue(queueName, queueOptions);
106
- } catch (assertErr) {
107
- // If channel closed during assertQueue, throw descriptive error
108
- if (!channel || channel.closed) {
109
- throw new Error(`Channel closed during assertQueue for ${queueName} - likely due to RPC reply queue issue`);
110
- }
111
- throw assertErr;
112
- }
109
+ // Queue doesn't exist - will be created on first sendToQueue()
110
+ // Return a mock queue info to indicate queue will be created lazily
111
+ console.info(`[QueueManager] Queue ${queueName} doesn't exist - will be created on first publish`);
112
+ return {
113
+ queue: queueName,
114
+ messageCount: 0,
115
+ consumerCount: 0
116
+ };
113
117
  } else {
114
118
  // Other error (including 406) - queue exists with different args
115
119
  // Log warning and return queue info without asserting
116
120
  console.warn(`[QueueManager] Queue ${queueName} exists with different arguments, using as-is:`, checkErr.message);
117
- // Check channel again before checkQueue
121
+ // Try checkQueue again to get queue info (if channel still open)
118
122
  if (!channel || channel.closed) {
119
123
  throw new Error('Channel closed - cannot check queue');
120
124
  }
121
- return channel.checkQueue(queueName);
125
+ try {
126
+ return await channel.checkQueue(queueName);
127
+ } catch (retryErr) {
128
+ // If channel closed, throw descriptive error
129
+ if (!channel || channel.closed) {
130
+ throw new Error('Channel closed during checkQueue retry - RPC reply queue issue detected');
131
+ }
132
+ throw retryErr;
133
+ }
122
134
  }
123
135
  }
124
136
  }
@@ -188,7 +200,8 @@ class QueueManager {
188
200
  if (!transport || !transport.channel) {
189
201
  throw new Error('MQ client not connected');
190
202
  }
191
- const channel = transport.channel;
203
+ // Use queueChannel for queue operations to avoid RPC reply queue issues
204
+ const channel = transport.queueChannel || transport.channel;
192
205
 
193
206
  const queues = {};
194
207
 
@@ -302,7 +315,8 @@ class QueueManager {
302
315
  if (!transport || !transport.channel) {
303
316
  throw new Error('MQ client not connected');
304
317
  }
305
- const channel = transport.channel;
318
+ // Use queueChannel for queue operations
319
+ const channel = transport.queueChannel || transport.channel;
306
320
 
307
321
  this.managedQueues.delete(queueName);
308
322
  return channel.deleteQueue(queueName, options);
@@ -354,7 +368,8 @@ class QueueManager {
354
368
  if (!transport || !transport.channel) {
355
369
  throw new Error('MQ client not connected');
356
370
  }
357
- const channel = transport.channel;
371
+ // Exchange operations can use queueChannel (regular channel) - no RPC issues
372
+ const channel = transport.queueChannel || transport.channel;
358
373
 
359
374
  return channel.assertExchange(exchangeName, type, {
360
375
  durable: options.durable !== false,
@@ -373,7 +388,8 @@ class QueueManager {
373
388
  if (!transport || !transport.channel) {
374
389
  throw new Error('MQ client not connected');
375
390
  }
376
- const channel = transport.channel;
391
+ // Bind operations can use queueChannel (regular channel) - no RPC issues
392
+ const channel = transport.queueChannel || transport.channel;
377
393
 
378
394
  return channel.bindQueue(queue, exchange, routingKey);
379
395
  }
@@ -38,10 +38,19 @@ class RabbitMQClient extends EventEmitter {
38
38
 
39
39
  /**
40
40
  * Getter for channel - provides compatibility with QueueManager
41
+ * Returns ConfirmChannel for publish operations
41
42
  */
42
43
  get channel() {
43
44
  return this._channel;
44
45
  }
46
+
47
+ /**
48
+ * Getter for queue channel - regular channel for queue operations
49
+ * Use this for assertQueue, checkQueue to avoid RPC reply queue issues
50
+ */
51
+ get queueChannel() {
52
+ return this._queueChannel || this._channel; // Fallback to main channel if queueChannel not available
53
+ }
45
54
 
46
55
  /**
47
56
  * Connects to RabbitMQ server and creates a confirm channel.
@@ -95,6 +104,15 @@ class RabbitMQClient extends EventEmitter {
95
104
  * @returns {Promise<void>}
96
105
  */
97
106
  async disconnect() {
107
+ try {
108
+ if (this._queueChannel) {
109
+ await this._queueChannel.close();
110
+ this._queueChannel = null;
111
+ }
112
+ } catch (err) {
113
+ // Log but don't emit - queue channel errors are less critical
114
+ console.warn('[RabbitMQClient] Error closing queue channel:', err.message);
115
+ }
98
116
  try {
99
117
  if (this._channel) {
100
118
  await this._channel.close();
@@ -134,9 +152,22 @@ class RabbitMQClient extends EventEmitter {
134
152
  try {
135
153
  // Ensure queue exists if publishing directly to queue and using default exchange
136
154
  if (!exchange) {
155
+ // Use queueChannel (regular channel) for queue operations to avoid RPC reply queue issues
137
156
  // Use central config for infrastructure queues
138
157
  const queueOptions = this._getQueueOptions(queue);
139
- await this._channel.assertQueue(queue, queueOptions);
158
+ try {
159
+ await this._queueChannel.checkQueue(queue);
160
+ // Queue exists - proceed to publish
161
+ } catch (checkErr) {
162
+ // If queue doesn't exist (404), create it
163
+ if (checkErr.code === 404) {
164
+ await this._queueChannel.assertQueue(queue, queueOptions);
165
+ } else {
166
+ // Other error (including 406) - queue exists with different args, proceed
167
+ console.warn(`[RabbitMQClient] Queue ${queue} exists with different arguments, using as-is`);
168
+ }
169
+ }
170
+ // Publish using ConfirmChannel (for publisher confirms)
140
171
  this._channel.sendToQueue(queue, buffer, { persistent, headers, routingKey });
141
172
  } else {
142
173
  // If exchange is specified, assert exchange and publish to it
@@ -177,9 +208,10 @@ class RabbitMQClient extends EventEmitter {
177
208
 
178
209
  if (isInfraQueue) {
179
210
  // Infrastructure queue - should already exist, created by infrastructure initialization
211
+ // Use queueChannel (regular channel) for queue operations to avoid RPC reply queue issues
180
212
  // Only check if it exists, don't try to create or assert with arguments
181
213
  try {
182
- await this._channel.checkQueue(queue);
214
+ await this._queueChannel.checkQueue(queue);
183
215
  // Queue exists - proceed to consume
184
216
  } catch (checkErr) {
185
217
  if (checkErr.code === 404) {
@@ -191,13 +223,14 @@ class RabbitMQClient extends EventEmitter {
191
223
  }
192
224
  } else {
193
225
  // Business queue - may need to be created, but use unified config
226
+ // Use queueChannel (regular channel) for queue operations to avoid RPC reply queue issues
194
227
  const queueOptions = this._getQueueOptions(queue, { durable });
195
228
 
196
229
  // Try to assert queue with our config
197
230
  // If it fails with 406 (PRECONDITION-FAILED), queue exists with different args - use it as-is
198
231
  // IMPORTANT: Don't try to re-assert after 406, as it will close the channel
199
232
  try {
200
- await this._channel.assertQueue(queue, queueOptions);
233
+ await this._queueChannel.assertQueue(queue, queueOptions);
201
234
  } catch (assertErr) {
202
235
  // If queue exists with different arguments (406), use it as-is without re-asserting
203
236
  if (assertErr.code === 406) {