@onlineapps/infrastructure-tools 1.0.18 → 1.0.19
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/infrastructure-tools",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.19",
|
|
4
4
|
"description": "Infrastructure orchestration utilities for OA Drive infrastructure services (health tracking, queue initialization, service discovery)",
|
|
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/infrastructure-tools",
|
|
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
|
"infrastructure",
|
|
@@ -19,7 +21,7 @@
|
|
|
19
21
|
"license": "MIT",
|
|
20
22
|
"dependencies": {
|
|
21
23
|
"@onlineapps/mq-client-core": "^1.0.38",
|
|
22
|
-
"@onlineapps/service-common": "^1.0.
|
|
24
|
+
"@onlineapps/service-common": "^1.0.3",
|
|
23
25
|
"uuid": "^9.0.1"
|
|
24
26
|
},
|
|
25
27
|
"devDependencies": {
|
package/src/index.js
CHANGED
|
@@ -10,8 +10,25 @@
|
|
|
10
10
|
* Business services should NOT use this library.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
// Re-export
|
|
14
|
-
const {
|
|
13
|
+
// Re-export all utilities from service-common (infrastructure services should use infrastructure-tools, not service-common directly)
|
|
14
|
+
const {
|
|
15
|
+
// Infrastructure readiness utilities
|
|
16
|
+
waitForInfrastructureReady,
|
|
17
|
+
waitForHealthCheckQueueReady,
|
|
18
|
+
sendMonitoringFailFallbackEmail,
|
|
19
|
+
// Redis utilities
|
|
20
|
+
buildRedisUrl,
|
|
21
|
+
createRedisClient,
|
|
22
|
+
connectRedis,
|
|
23
|
+
// Runtime configuration helpers (NO FALLBACKS for critical infrastructure)
|
|
24
|
+
requireEnv,
|
|
25
|
+
optionalEnv,
|
|
26
|
+
optionalNumberEnv,
|
|
27
|
+
getCriticalConfig,
|
|
28
|
+
getOptionalInfraConfig,
|
|
29
|
+
getServiceConfig,
|
|
30
|
+
getInfrastructureHealthConfig
|
|
31
|
+
} = require('@onlineapps/service-common');
|
|
15
32
|
|
|
16
33
|
// Re-export MQ client core utilities
|
|
17
34
|
const BaseClient = require('@onlineapps/mq-client-core');
|
|
@@ -32,10 +49,23 @@ module.exports = {
|
|
|
32
49
|
MQClient: BaseClient, // Alias for compatibility
|
|
33
50
|
queueConfig,
|
|
34
51
|
|
|
35
|
-
// Service Common utilities (re-exported
|
|
52
|
+
// Service Common utilities (re-exported - infrastructure services should use infrastructure-tools, not service-common directly)
|
|
53
|
+
// Infrastructure readiness
|
|
36
54
|
waitForInfrastructureReady,
|
|
37
55
|
waitForHealthCheckQueueReady,
|
|
38
56
|
sendMonitoringFailFallbackEmail,
|
|
57
|
+
// Redis utilities
|
|
58
|
+
buildRedisUrl,
|
|
59
|
+
createRedisClient,
|
|
60
|
+
connectRedis,
|
|
61
|
+
// Runtime configuration helpers (NO FALLBACKS for critical infrastructure)
|
|
62
|
+
requireEnv,
|
|
63
|
+
optionalEnv,
|
|
64
|
+
optionalNumberEnv,
|
|
65
|
+
getCriticalConfig,
|
|
66
|
+
getOptionalInfraConfig,
|
|
67
|
+
getServiceConfig,
|
|
68
|
+
getInfrastructureHealthConfig,
|
|
39
69
|
|
|
40
70
|
// Orchestration utilities
|
|
41
71
|
initInfrastructureQueues,
|
|
@@ -96,8 +96,98 @@ async function initInfrastructureQueues(channel, options = {}) {
|
|
|
96
96
|
// Get unified configuration
|
|
97
97
|
const config = queueConfig.getInfrastructureQueueConfig(queueName);
|
|
98
98
|
|
|
99
|
-
// CRITICAL:
|
|
100
|
-
//
|
|
99
|
+
// CRITICAL: First check if queue exists and what parameters it has
|
|
100
|
+
// This prevents 406 errors by proactively deleting queues with wrong parameters
|
|
101
|
+
// Use a separate channel for checkQueue to avoid channel closure issues
|
|
102
|
+
// NOTE: checkQueue() does NOT create queue - it only checks if it exists
|
|
103
|
+
let queueExists = false;
|
|
104
|
+
let queueNeedsRecreation = false;
|
|
105
|
+
let checkChannelForDelete = null;
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
// Use a separate channel for checking queue (to avoid closing workingChannel)
|
|
109
|
+
// CRITICAL: checkQueue() does NOT create queue - it only checks existence
|
|
110
|
+
checkChannelForDelete = await connection.createChannel();
|
|
111
|
+
const queueInfo = await checkChannelForDelete.checkQueue(queueName);
|
|
112
|
+
queueExists = true;
|
|
113
|
+
|
|
114
|
+
// Check if queue has correct arguments
|
|
115
|
+
const currentArgs = queueInfo.arguments || {};
|
|
116
|
+
const expectedArgs = config.arguments || {};
|
|
117
|
+
|
|
118
|
+
// Compare arguments (TTL, max-length, etc.)
|
|
119
|
+
const argsMatch = Object.keys(expectedArgs).every(key => {
|
|
120
|
+
return currentArgs[key] === expectedArgs[key];
|
|
121
|
+
}) && Object.keys(currentArgs).every(key => {
|
|
122
|
+
// Allow extra args in current queue, but required args must match
|
|
123
|
+
return expectedArgs[key] === undefined || currentArgs[key] === expectedArgs[key];
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
if (!argsMatch) {
|
|
127
|
+
logger.warn(`[QueueInit] ⚠ Queue ${queueName} exists with different arguments - will delete and recreate`, {
|
|
128
|
+
current: currentArgs,
|
|
129
|
+
expected: expectedArgs
|
|
130
|
+
});
|
|
131
|
+
queueNeedsRecreation = true;
|
|
132
|
+
} else {
|
|
133
|
+
logger.log(`[QueueInit] ✓ Queue ${queueName} exists with correct parameters`);
|
|
134
|
+
// Close check channel if queue is OK
|
|
135
|
+
try {
|
|
136
|
+
await checkChannelForDelete.close();
|
|
137
|
+
checkChannelForDelete = null;
|
|
138
|
+
} catch (closeErr) {
|
|
139
|
+
// Ignore
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
} catch (checkError) {
|
|
143
|
+
// Queue doesn't exist - will be created
|
|
144
|
+
queueExists = false;
|
|
145
|
+
logger.log(`[QueueInit] Queue ${queueName} does not exist - will be created`);
|
|
146
|
+
// Close check channel if queue doesn't exist
|
|
147
|
+
if (checkChannelForDelete) {
|
|
148
|
+
try {
|
|
149
|
+
await checkChannelForDelete.close();
|
|
150
|
+
checkChannelForDelete = null;
|
|
151
|
+
} catch (closeErr) {
|
|
152
|
+
// Ignore
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// CRITICAL: If queue exists with wrong parameters, delete it first
|
|
158
|
+
// This must be done BEFORE assertQueue to avoid 406 errors
|
|
159
|
+
if (queueNeedsRecreation) {
|
|
160
|
+
try {
|
|
161
|
+
// Use the check channel for deletion (or create new one if check failed)
|
|
162
|
+
const deleteChannel = checkChannelForDelete || await connection.createChannel();
|
|
163
|
+
await deleteChannel.deleteQueue(queueName, { ifEmpty: false });
|
|
164
|
+
logger.log(`[QueueInit] ✓ Deleted queue ${queueName} with incorrect parameters`);
|
|
165
|
+
// Close delete channel after use
|
|
166
|
+
try {
|
|
167
|
+
await deleteChannel.close();
|
|
168
|
+
} catch (closeErr) {
|
|
169
|
+
// Ignore close errors
|
|
170
|
+
}
|
|
171
|
+
checkChannelForDelete = null;
|
|
172
|
+
|
|
173
|
+
// Wait to ensure deletion is processed and no frames are pending
|
|
174
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
175
|
+
queueExists = false; // Queue no longer exists, will be created
|
|
176
|
+
} catch (deleteError) {
|
|
177
|
+
logger.error(`[QueueInit] ✗ Failed to delete queue ${queueName}:`, deleteError.message);
|
|
178
|
+
// Clean up check channel if still open
|
|
179
|
+
if (checkChannelForDelete) {
|
|
180
|
+
try {
|
|
181
|
+
await checkChannelForDelete.close();
|
|
182
|
+
} catch (closeErr) {
|
|
183
|
+
// Ignore
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
throw new Error(`Cannot delete queue ${queueName} with incorrect parameters: ${deleteError.message}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// CRITICAL: Now create/verify queue with correct parameters
|
|
101
191
|
const ensureQueue = async () => {
|
|
102
192
|
const activeChannel = await getChannel();
|
|
103
193
|
return activeChannel.assertQueue(queueName, {
|
|
@@ -129,9 +219,26 @@ async function initInfrastructureQueues(channel, options = {}) {
|
|
|
129
219
|
`[QueueInit] ⚠ Queue ${queueName} exists with different arguments - deleting and recreating...`
|
|
130
220
|
);
|
|
131
221
|
try {
|
|
222
|
+
// CRITICAL: Use a fresh channel for delete operation to avoid closed channel errors
|
|
223
|
+
// Close the current channel first to prevent it from receiving frames during deletion
|
|
224
|
+
if (workingChannel && !workingChannel.__queueInitClosed) {
|
|
225
|
+
try {
|
|
226
|
+
await workingChannel.close();
|
|
227
|
+
workingChannel.__queueInitClosed = true;
|
|
228
|
+
} catch (closeErr) {
|
|
229
|
+
// Ignore - channel may already be closed
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
132
233
|
const deleteChannel = await getChannel(true);
|
|
133
|
-
await deleteChannel.deleteQueue(queueName);
|
|
234
|
+
await deleteChannel.deleteQueue(queueName, { ifEmpty: false });
|
|
134
235
|
logger.log(`[QueueInit] ✓ Deleted queue ${queueName} with incorrect parameters`);
|
|
236
|
+
// Close delete channel after use to avoid channel leaks
|
|
237
|
+
try {
|
|
238
|
+
await deleteChannel.close();
|
|
239
|
+
} catch (closeErr) {
|
|
240
|
+
// Ignore close errors - channel may already be closed
|
|
241
|
+
}
|
|
135
242
|
} catch (deleteError) {
|
|
136
243
|
logger.error(
|
|
137
244
|
`[QueueInit] ✗ Failed to delete queue ${queueName}:`,
|
|
@@ -142,12 +249,24 @@ async function initInfrastructureQueues(channel, options = {}) {
|
|
|
142
249
|
);
|
|
143
250
|
}
|
|
144
251
|
|
|
145
|
-
// Recreate with correct parameters
|
|
146
|
-
|
|
147
|
-
await
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
252
|
+
// CRITICAL: Recreate with correct parameters using a fresh channel
|
|
253
|
+
// Wait a brief moment to ensure queue deletion is fully processed and no frames are pending
|
|
254
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
255
|
+
const recreateChannel = await getChannel(true);
|
|
256
|
+
try {
|
|
257
|
+
await recreateChannel.assertQueue(queueName, {
|
|
258
|
+
durable: config.durable !== false,
|
|
259
|
+
arguments: { ...config.arguments }
|
|
260
|
+
});
|
|
261
|
+
logger.log(
|
|
262
|
+
`[QueueInit] ✓ Recreated infrastructure queue: ${queueName} with correct parameters`
|
|
263
|
+
);
|
|
264
|
+
// Update workingChannel to the new channel for subsequent operations
|
|
265
|
+
workingChannel = recreateChannel;
|
|
266
|
+
} catch (recreateError) {
|
|
267
|
+
logger.error(`[QueueInit] ✗ Failed to recreate ${queueName}:`, recreateError.message);
|
|
268
|
+
throw recreateError;
|
|
269
|
+
}
|
|
151
270
|
} else {
|
|
152
271
|
logger.error(`[QueueInit] ✗ Failed to create ${queueName}:`, assertError.message);
|
|
153
272
|
throw assertError;
|