@onlineapps/mq-client-core 1.0.5 → 1.0.7

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/mq-client-core",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Core MQ client library for RabbitMQ - shared by infrastructure services and connectors",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
package/src/BaseClient.js CHANGED
@@ -146,7 +146,14 @@ class BaseClient {
146
146
  if (typeof noAck === 'boolean') consumeOptions.noAck = noAck;
147
147
  if (options.queueOptions) consumeOptions.queueOptions = options.queueOptions;
148
148
 
149
+ // CRITICAL: Log all consume() calls for transparency
150
+ console.log(`[BaseClient] [CONSUMER] Starting consume() for queue: ${queue} at ${new Date().toISOString()}`);
151
+ console.log(`[BaseClient] [CONSUMER] Options:`, JSON.stringify(consumeOptions, null, 2));
152
+ console.log(`[BaseClient] [CONSUMER] Transport type: ${this._transport?.constructor?.name || 'unknown'}`);
153
+ console.log(`[BaseClient] [CONSUMER] Connected: ${this._connected}`);
154
+
149
155
  try {
156
+ const consumeStartTime = Date.now();
150
157
  await this._transport.consume(
151
158
  queue,
152
159
  async (msg) => {
@@ -162,7 +169,10 @@ class BaseClient {
162
169
  },
163
170
  consumeOptions
164
171
  );
172
+ const consumeEndTime = Date.now();
173
+ console.log(`[BaseClient] [CONSUMER] ✓ consume() completed for queue: ${queue} (took ${consumeEndTime - consumeStartTime}ms)`);
165
174
  } catch (err) {
175
+ console.error(`[BaseClient] [CONSUMER] ✗ consume() failed for queue: ${queue}`, err.message);
166
176
  throw new ConsumeError(`Failed to start consumer for queue "${queue}"`, queue, err);
167
177
  }
168
178
  }
@@ -33,14 +33,24 @@ class RabbitMQClient extends EventEmitter {
33
33
 
34
34
  this._connection = null;
35
35
  this._channel = null;
36
+ this._queueChannel = null;
36
37
  }
37
38
 
38
39
  /**
39
40
  * Getter for channel - provides compatibility with QueueManager
41
+ * Returns ConfirmChannel for publish operations
40
42
  */
41
43
  get channel() {
42
44
  return this._channel;
43
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
+ }
44
54
 
45
55
  /**
46
56
  * Connects to RabbitMQ server and creates a confirm channel.
@@ -56,13 +66,24 @@ class RabbitMQClient extends EventEmitter {
56
66
  this.emit('error', new Error('RabbitMQ connection closed unexpectedly'));
57
67
  });
58
68
 
59
- // Use ConfirmChannel to enable publisher confirms
69
+ // Use ConfirmChannel to enable publisher confirms for publish operations
60
70
  this._channel = await this._connection.createConfirmChannel();
61
71
  this._channel.on('error', (err) => this.emit('error', err));
62
72
  this._channel.on('close', () => {
63
73
  // Emit a channel close error
64
74
  this.emit('error', new Error('RabbitMQ channel closed unexpectedly'));
65
75
  });
76
+
77
+ // Create a separate regular channel for queue operations (assertQueue, checkQueue)
78
+ // This avoids RPC reply queue issues with ConfirmChannel.assertQueue()
79
+ this._queueChannel = await this._connection.createChannel();
80
+ this._queueChannel.on('error', (err) => {
81
+ // Log but don't emit - queue channel errors are less critical
82
+ console.warn('[RabbitMQClient] Queue channel error:', err.message);
83
+ });
84
+ this._queueChannel.on('close', () => {
85
+ console.warn('[RabbitMQClient] Queue channel closed');
86
+ });
66
87
  } catch (err) {
67
88
  // Cleanup partially created resources
68
89
  if (this._connection) {
@@ -82,6 +103,15 @@ class RabbitMQClient extends EventEmitter {
82
103
  * @returns {Promise<void>}
83
104
  */
84
105
  async disconnect() {
106
+ try {
107
+ if (this._queueChannel) {
108
+ await this._queueChannel.close();
109
+ this._queueChannel = null;
110
+ }
111
+ } catch (err) {
112
+ // Log but don't emit - queue channel errors are less critical
113
+ console.warn('[RabbitMQClient] Error closing queue channel:', err.message);
114
+ }
85
115
  try {
86
116
  if (this._channel) {
87
117
  await this._channel.close();
@@ -122,22 +152,22 @@ class RabbitMQClient extends EventEmitter {
122
152
  // Ensure queue exists if publishing directly to queue and using default exchange
123
153
  if (!exchange) {
124
154
  // For infrastructure queues, they should already exist with specific arguments
125
- // Use checkQueue instead of assertQueue to avoid 406 PRECONDITION-FAILED
155
+ // Use queueChannel (regular channel) for checkQueue/assertQueue to avoid RPC reply queue issues
126
156
  // If queue doesn't exist (404), try to create it with default options
127
157
  try {
128
- await this._channel.checkQueue(queue);
158
+ await this._queueChannel.checkQueue(queue);
129
159
  // Queue exists - proceed to publish
130
160
  } catch (checkErr) {
131
161
  // If queue doesn't exist (404), create it with default options
132
162
  if (checkErr.code === 404) {
133
163
  const queueOptions = options.queueOptions || { durable: this._config.durable };
134
- await this._channel.assertQueue(queue, queueOptions);
164
+ await this._queueChannel.assertQueue(queue, queueOptions);
135
165
  } else {
136
166
  // Other error - rethrow
137
167
  throw checkErr;
138
168
  }
139
169
  }
140
- // Publish to queue (works even if queue has different arguments)
170
+ // Publish to queue using ConfirmChannel (for publisher confirms)
141
171
  this._channel.sendToQueue(queue, buffer, { persistent, headers });
142
172
  } else {
143
173
  // If exchange is specified, assert exchange and publish to it
@@ -173,23 +203,74 @@ class RabbitMQClient extends EventEmitter {
173
203
  // Skip assertQueue for reply queues (they're already created with specific settings)
174
204
  // Reply queues start with 'rpc.reply.' and are created as non-durable
175
205
  if (!queue.startsWith('rpc.reply.')) {
176
- // Simple queue assertion - infrastructure services should ensure queues exist
177
- // If queue doesn't exist, assertQueue will create it with default options
178
- // If it exists with different args (406), log warning and proceed
179
- const queueOptions = options.queueOptions || { durable };
180
- try {
181
- await this._channel.assertQueue(queue, queueOptions);
182
- } catch (assertErr) {
183
- // If queue exists with different arguments (406), use it as-is
184
- if (assertErr.code === 406) {
185
- console.warn(`[RabbitMQClient] Queue ${queue} exists with different arguments, using as-is:`, assertErr.message);
186
- // Don't try to re-assert - just proceed to consume
187
- } else {
188
- // Other error - rethrow
189
- throw assertErr;
206
+ // CRITICAL: Use queueConfig.js to get correct parameters (TTL, max-length, etc.)
207
+ // This prevents 406 PRECONDITION-FAILED errors from TTL mismatches
208
+ // Note: mq-client-core is for infrastructure services, but we need queueConfig from conn-infra-mq
209
+ // Try to load it - if it fails, we'll use defaults
210
+ let queueConfig = null;
211
+ try {
212
+ queueConfig = require('@onlineapps/conn-infra-mq/src/config/queueConfig');
213
+ } catch (requireErr) {
214
+ console.warn(`[RabbitMQClient] [mq-client-core] [CONSUMER] Cannot load queueConfig from @onlineapps/conn-infra-mq:`, requireErr.message);
215
+ console.warn(`[RabbitMQClient] [mq-client-core] [CONSUMER] Using default queue options (this may cause 406 errors if queue exists with different args)`);
216
+ }
217
+ const isInfraQueue = queueConfig.isInfrastructureQueue(queue);
218
+ const isBusinessQueue = queueConfig.isBusinessQueue(queue);
219
+
220
+ let queueOptions = options.queueOptions || { durable };
221
+
222
+ if (queueConfig) {
223
+ if (isInfraQueue) {
224
+ // Infrastructure queue - use central config
225
+ try {
226
+ const infraConfig = queueConfig.getInfrastructureQueueConfig(queue);
227
+ queueOptions = {
228
+ durable: infraConfig.durable !== false,
229
+ arguments: { ...infraConfig.arguments }
230
+ };
231
+ console.log(`[RabbitMQClient] [mq-client-core] [CONSUMER] Asserting infrastructure queue ${queue} with config from queueConfig`);
232
+ } catch (configErr) {
233
+ console.warn(`[RabbitMQClient] [mq-client-core] [CONSUMER] Infrastructure queue config not found for ${queue}, using default:`, configErr.message);
234
+ }
235
+ } else if (isBusinessQueue) {
236
+ // Business queue - use central config
237
+ try {
238
+ const parsed = queueConfig.parseBusinessQueue(queue);
239
+ if (parsed) {
240
+ const businessConfig = queueConfig.getBusinessQueueConfig(parsed.queueType, parsed.serviceName);
241
+ queueOptions = {
242
+ durable: businessConfig.durable !== false,
243
+ arguments: { ...businessConfig.arguments }
244
+ };
245
+ console.log(`[RabbitMQClient] [mq-client-core] [CONSUMER] Asserting business queue ${queue} with config from queueConfig`);
246
+ }
247
+ } catch (configErr) {
248
+ console.warn(`[RabbitMQClient] [mq-client-core] [CONSUMER] Business queue config not found for ${queue}, using default:`, configErr.message);
190
249
  }
191
250
  }
192
251
  }
252
+
253
+ console.log(`[RabbitMQClient] [mq-client-core] [CONSUMER] Asserting queue ${queue} before consume() at ${new Date().toISOString()}`);
254
+ console.log(`[RabbitMQClient] [mq-client-core] [CONSUMER] Queue options:`, JSON.stringify(queueOptions, null, 2));
255
+
256
+ try {
257
+ await this._queueChannel.assertQueue(queue, queueOptions);
258
+ console.log(`[RabbitMQClient] [mq-client-core] [CONSUMER] ✓ Queue ${queue} asserted successfully`);
259
+ } catch (assertErr) {
260
+ // If queue exists with different arguments (406), this is a CRITICAL ERROR
261
+ // We should NOT proceed - the root cause must be fixed
262
+ if (assertErr.code === 406) {
263
+ console.error(`[RabbitMQClient] [mq-client-core] [CONSUMER] ✗ CRITICAL: Queue ${queue} exists with different arguments!`);
264
+ console.error(`[RabbitMQClient] [mq-client-core] [CONSUMER] Error:`, assertErr.message);
265
+ console.error(`[RabbitMQClient] [mq-client-core] [CONSUMER] Expected options:`, JSON.stringify(queueOptions, null, 2));
266
+ console.error(`[RabbitMQClient] [mq-client-core] [CONSUMER] This means assertQueue() was called without parameters somewhere else. Root cause must be fixed!`);
267
+ throw new Error(`Cannot assertQueue ${queue}: queue exists with different arguments. Root cause: assertQueue() was called without parameters. Fix the root cause instead of proceeding.`);
268
+ } else {
269
+ // Other error - rethrow
270
+ throw assertErr;
271
+ }
272
+ }
273
+ }
193
274
  // Set prefetch if provided
194
275
  if (typeof prefetch === 'number') {
195
276
  this._channel.prefetch(prefetch);