@onlineapps/mq-client-core 1.0.41 → 1.0.45
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,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlineapps/mq-client-core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.45",
|
|
4
4
|
"description": "Core MQ client library for RabbitMQ - shared by infrastructure services and connectors",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "jest",
|
|
8
8
|
"test:unit": "jest --testPathPattern=tests/unit",
|
|
9
9
|
"test:component": "jest --testPathPattern=tests/component",
|
|
10
|
-
"test:integration": "jest --testPathPattern=tests/integration"
|
|
10
|
+
"test:integration": "jest --testPathPattern=tests/integration",
|
|
11
|
+
"prepublishOnly": "cd $(git rev-parse --show-toplevel) && bash scripts/pre-publish-compatibility-check.sh shared/mq-client-core",
|
|
12
|
+
"postpublish": "cd $(git rev-parse --show-toplevel) && bash scripts/update-manifest-from-npm.sh && bash scripts/update-all-services.sh"
|
|
11
13
|
},
|
|
12
14
|
"keywords": [
|
|
13
15
|
"rabbitmq",
|
package/src/BaseClient.js
CHANGED
|
@@ -32,6 +32,20 @@ class BaseClient {
|
|
|
32
32
|
|
|
33
33
|
// Merge user config with defaults
|
|
34
34
|
this._config = merge({}, defaultConfig, config || {});
|
|
35
|
+
|
|
36
|
+
// EXCEPTION: Fallback for infrastructure client configuration
|
|
37
|
+
// ENV variable name: RABBITMQ_URL (has priority over config.host and defaultConfig.host)
|
|
38
|
+
// Priority: config.host (explicit) → RABBITMQ_URL ENV → defaultConfig.host fallback
|
|
39
|
+
// Only use ENV if host was not explicitly provided in config
|
|
40
|
+
const userProvidedHost = (config && config.host);
|
|
41
|
+
if (!userProvidedHost) {
|
|
42
|
+
// ENV has priority over defaultConfig fallback
|
|
43
|
+
// Check ENV at runtime (not at module load time)
|
|
44
|
+
if (process.env.RABBITMQ_URL && process.env.RABBITMQ_URL.trim() !== '') {
|
|
45
|
+
this._config.host = process.env.RABBITMQ_URL;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
35
49
|
this._config.heartbeat =
|
|
36
50
|
this._config.heartbeat ?? Number(process.env.RABBITMQ_HEARTBEAT || 30);
|
|
37
51
|
this._config.connectionName =
|
|
@@ -6,14 +6,28 @@
|
|
|
6
6
|
* Provides default configuration values for MQ Client Core.
|
|
7
7
|
* Users can override any of these by passing a custom config object
|
|
8
8
|
* or by supplying overrides to connect().
|
|
9
|
+
*
|
|
10
|
+
* EXCEPTION: Fallbacks are allowed ONLY for infrastructure client configuration.
|
|
11
|
+
* This is the ONLY place where fallbacks are acceptable.
|
|
12
|
+
*
|
|
13
|
+
* Environment variable (checked at runtime, has priority):
|
|
14
|
+
* - RABBITMQ_URL: Full RabbitMQ URL (e.g., amqp://guest:guest@api_services_queuer:5672)
|
|
15
|
+
*
|
|
16
|
+
* Fallback (used only if RABBITMQ_URL is not set):
|
|
17
|
+
* - amqp://guest:guest@api_services_queuer:5672 (docker-compose default)
|
|
18
|
+
*
|
|
19
|
+
* NOTE: host is resolved at runtime in BaseClient constructor, not at module load time.
|
|
9
20
|
*/
|
|
10
21
|
|
|
11
22
|
module.exports = {
|
|
12
23
|
// Transport type: currently only 'rabbitmq' is fully supported.
|
|
13
24
|
type: 'rabbitmq',
|
|
14
25
|
|
|
15
|
-
// RabbitMQ connection URI or hostname
|
|
16
|
-
|
|
26
|
+
// RabbitMQ connection URI or hostname.
|
|
27
|
+
// NOTE: This is a fallback value. BaseClient constructor will check RABBITMQ_URL ENV variable
|
|
28
|
+
// and override this if ENV is set. ENV variable name: RABBITMQ_URL
|
|
29
|
+
// Fallback: amqp://guest:guest@api_services_queuer:5672 (docker-compose default)
|
|
30
|
+
host: 'amqp://guest:guest@api_services_queuer:5672',
|
|
17
31
|
|
|
18
32
|
// Default queue name; can be overridden per call to publish/consume.
|
|
19
33
|
// Optional for infrastructure services (they may not need a default queue).
|
|
@@ -23,12 +23,22 @@ module.exports = {
|
|
|
23
23
|
/**
|
|
24
24
|
* workflow.init - Entry point for all workflows
|
|
25
25
|
* All services listen as competing consumers
|
|
26
|
+
*
|
|
27
|
+
* TTL: 30 seconds - if no business service processes message within 30s, it expires
|
|
28
|
+
* DLQ routing: Expired messages go to workflow.failed (via x-dead-letter-exchange)
|
|
29
|
+
*
|
|
30
|
+
* Rationale:
|
|
31
|
+
* - Fast failure detection (30s is enough for service discovery + processing start)
|
|
32
|
+
* - Prevents message accumulation when no business services are running
|
|
33
|
+
* - Failed messages go to workflow.failed for monitoring/alerting
|
|
26
34
|
*/
|
|
27
35
|
init: {
|
|
28
36
|
durable: true,
|
|
29
37
|
arguments: {
|
|
30
|
-
'x-message-ttl':
|
|
31
|
-
'x-max-length': 10000
|
|
38
|
+
'x-message-ttl': 30000, // 30 seconds TTL (fast failure if no service available)
|
|
39
|
+
'x-max-length': 10000, // Max 10k messages
|
|
40
|
+
'x-dead-letter-exchange': '', // Use default exchange for DLQ routing
|
|
41
|
+
'x-dead-letter-routing-key': 'workflow.failed' // Route expired messages to workflow.failed
|
|
32
42
|
}
|
|
33
43
|
},
|
|
34
44
|
|
|
@@ -197,14 +207,20 @@ module.exports = {
|
|
|
197
207
|
|
|
198
208
|
/**
|
|
199
209
|
* Monitoring infrastructure queue configurations
|
|
200
|
-
* These are dedicated queues for Monitoring service (
|
|
210
|
+
* These are dedicated queues for Monitoring service (infrastructure service)
|
|
211
|
+
*
|
|
212
|
+
* CRITICAL: Monitoring is an infrastructure service, monitoring queues are infrastructure queues!
|
|
213
|
+
* All services (both infrastructure and business) publish monitoring events here.
|
|
201
214
|
*/
|
|
202
215
|
monitoring: {
|
|
203
216
|
/**
|
|
204
|
-
* monitoring.workflow
|
|
205
|
-
*
|
|
217
|
+
* monitoring.workflow - Unified workflow lifecycle monitoring
|
|
218
|
+
* Delivery Dispatcher publishes 'completed'/'failed' events after processing workflow.completed/workflow.failed
|
|
219
|
+
* Business services publish 'progress' events during workflow step processing
|
|
220
|
+
* Message format includes event_type: 'completed' | 'failed' | 'progress'
|
|
221
|
+
* Includes full cookbook and context snapshots for trace storage
|
|
206
222
|
*/
|
|
207
|
-
'workflow
|
|
223
|
+
'workflow': {
|
|
208
224
|
durable: true,
|
|
209
225
|
arguments: {
|
|
210
226
|
'x-message-ttl': 300000, // 5 minutes TTL
|
|
@@ -213,36 +229,19 @@ module.exports = {
|
|
|
213
229
|
},
|
|
214
230
|
|
|
215
231
|
/**
|
|
216
|
-
* monitoring.
|
|
217
|
-
*
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
'x-max-length': 10000
|
|
224
|
-
}
|
|
225
|
-
},
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* monitoring.registry.events - Registry events for monitoring
|
|
229
|
-
*/
|
|
230
|
-
'registry.events': {
|
|
231
|
-
durable: true,
|
|
232
|
-
arguments: {
|
|
233
|
-
'x-message-ttl': 60000, // 1 minute TTL
|
|
234
|
-
'x-max-length': 5000
|
|
235
|
-
}
|
|
236
|
-
},
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* monitoring.service.heartbeats - Service heartbeats for monitoring
|
|
232
|
+
* monitoring.services - Service lifecycle events monitoring
|
|
233
|
+
* Registry publishes service lifecycle events (registered, validation.completed, version.changed, status.changed)
|
|
234
|
+
* InfrastructureHealthTracker publishes service status changes
|
|
235
|
+
* Message format includes event_type: 'service.registered' | 'service.validation.completed' |
|
|
236
|
+
* 'service.version.changed' | 'service.deregistered' | 'service.status.changed'
|
|
237
|
+
* NOTE: Heartbeats are NOT published here (too frequent, every 10s)
|
|
238
|
+
* NOTE: Healthcheck messages go to infrastructure.health.events exchange
|
|
240
239
|
*/
|
|
241
|
-
'
|
|
240
|
+
'services': {
|
|
242
241
|
durable: true,
|
|
243
242
|
arguments: {
|
|
244
|
-
'x-message-ttl':
|
|
245
|
-
'x-max-length':
|
|
243
|
+
'x-message-ttl': 600000, // 10 minutes TTL (longer for service tracking)
|
|
244
|
+
'x-max-length': 20000
|
|
246
245
|
}
|
|
247
246
|
}
|
|
248
247
|
},
|
|
@@ -252,27 +251,28 @@ module.exports = {
|
|
|
252
251
|
*/
|
|
253
252
|
registry: {
|
|
254
253
|
/**
|
|
255
|
-
* registry.register -
|
|
254
|
+
* registry.register - Main queue for ALL service communication with Registry
|
|
255
|
+
*
|
|
256
|
+
* CRITICAL: This is the ONLY queue used for service-to-Registry communication.
|
|
257
|
+
* Registry listener processes different message types based on msg.type:
|
|
258
|
+
* - type: 'register' - Service registration requests
|
|
259
|
+
* - type: 'heartbeat' - Periodic health check messages (sent every 10s)
|
|
260
|
+
* - type: 'apiDescription' - API specification responses
|
|
261
|
+
*
|
|
262
|
+
* Architecture rationale:
|
|
263
|
+
* - Single consumer on registry.register handles all message types
|
|
264
|
+
* - Simplifies queue management (one queue instead of multiple)
|
|
265
|
+
* - Registry can process messages in order and maintain state consistency
|
|
266
|
+
* - All business services send both registration AND heartbeats to this queue
|
|
256
267
|
*/
|
|
257
268
|
register: {
|
|
258
269
|
durable: true,
|
|
259
270
|
arguments: {
|
|
260
|
-
'x-message-ttl': 60000, // 1 minute TTL
|
|
271
|
+
'x-message-ttl': 60000, // 1 minute TTL (for registration requests)
|
|
261
272
|
'x-max-length': 1000
|
|
262
273
|
}
|
|
263
274
|
},
|
|
264
275
|
|
|
265
|
-
/**
|
|
266
|
-
* registry.heartbeats - Service heartbeats
|
|
267
|
-
*/
|
|
268
|
-
heartbeats: {
|
|
269
|
-
durable: true,
|
|
270
|
-
arguments: {
|
|
271
|
-
'x-message-ttl': 120000, // 2 minutes TTL
|
|
272
|
-
'x-max-length': 5000
|
|
273
|
-
}
|
|
274
|
-
},
|
|
275
|
-
|
|
276
276
|
/**
|
|
277
277
|
* registry.changes - Registry event fanout exchange
|
|
278
278
|
* (This is an exchange, not a queue, but included for reference)
|
|
@@ -122,6 +122,7 @@ class RabbitMQClient extends EventEmitter {
|
|
|
122
122
|
client: this,
|
|
123
123
|
scope: this._config.recoveryScope || 'infrastructure',
|
|
124
124
|
queueCreationFilter: this._config.queueCreationFilter || null,
|
|
125
|
+
queueCreationCallback: this._config.queueCreationCallback || null, // Delegates to QueueManager if provided
|
|
125
126
|
logger: console,
|
|
126
127
|
});
|
|
127
128
|
|
|
@@ -8,12 +8,18 @@ const { QueueNotFoundError } = require('../utils/publishErrors');
|
|
|
8
8
|
* Scope:
|
|
9
9
|
* - infrastructure: connection recovery, channel recreation, consumer re-registration (NENÍ queue creation)
|
|
10
10
|
* - business: connection recovery, channel recreation, consumer re-registration, queue creation (pokud není infra queue)
|
|
11
|
+
*
|
|
12
|
+
* Queue Creation:
|
|
13
|
+
* - For business queues: delegates to queueCreationCallback if provided (e.g., QueueManager.setupServiceQueues)
|
|
14
|
+
* - Falls back to direct queue creation if callback not provided (for backward compatibility)
|
|
11
15
|
*/
|
|
12
16
|
class RecoveryWorker {
|
|
13
17
|
/**
|
|
14
18
|
* @param {Object} options
|
|
15
19
|
* @param {Object} options.client - RabbitMQClient instance
|
|
16
20
|
* @param {string} [options.scope='infrastructure'] - 'infrastructure' nebo 'business'
|
|
21
|
+
* @param {Function} [options.queueCreationCallback] - Callback for queue creation: async (queueName, options) => void
|
|
22
|
+
* Should delegate to QueueManager (e.g., setupServiceQueues or ensureQueue)
|
|
17
23
|
* @param {Object} [options.logger] - Logger
|
|
18
24
|
*/
|
|
19
25
|
constructor(options = {}) {
|
|
@@ -28,6 +34,9 @@ class RecoveryWorker {
|
|
|
28
34
|
// Scope configuration
|
|
29
35
|
this._queueCreationEnabled = this._scope === 'business';
|
|
30
36
|
this._queueCreationFilter = options.queueCreationFilter || null;
|
|
37
|
+
|
|
38
|
+
// Queue creation callback - delegates to QueueManager if provided
|
|
39
|
+
this._queueCreationCallback = options.queueCreationCallback || null;
|
|
31
40
|
}
|
|
32
41
|
|
|
33
42
|
/**
|
|
@@ -66,8 +75,17 @@ class RecoveryWorker {
|
|
|
66
75
|
}
|
|
67
76
|
|
|
68
77
|
try {
|
|
69
|
-
|
|
70
|
-
|
|
78
|
+
// Use queue creation callback if provided (delegates to QueueManager)
|
|
79
|
+
// Otherwise fall back to direct queue creation (backward compatibility)
|
|
80
|
+
if (this._queueCreationCallback) {
|
|
81
|
+
this._logger?.info?.(`[RecoveryWorker] Delegating queue creation to callback for '${error.queueName}'`);
|
|
82
|
+
await this._queueCreationCallback(error.queueName, context.options);
|
|
83
|
+
this._logger?.info?.(`[RecoveryWorker] ✓ Queue '${error.queueName}' created via callback`);
|
|
84
|
+
} else {
|
|
85
|
+
// Fallback: direct queue creation (for backward compatibility)
|
|
86
|
+
await this.createQueue(error.queueName, context.options);
|
|
87
|
+
this._logger?.info?.(`[RecoveryWorker] ✓ Created queue '${error.queueName}' (direct)`);
|
|
88
|
+
}
|
|
71
89
|
} catch (createErr) {
|
|
72
90
|
this._logger?.error?.(`[RecoveryWorker] Failed to create queue '${error.queueName}': ${createErr.message}`);
|
|
73
91
|
throw error;
|