@onlineapps/service-common 1.0.0
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/README.md +77 -0
- package/package.json +29 -0
- package/src/index.js +18 -0
- package/src/infrastructure/waitForInfrastructureReady.js +138 -0
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# @onlineapps/service-common
|
|
2
|
+
|
|
3
|
+
Common utilities for both infrastructure services and business services in OA Drive.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
This library provides shared functionality that is used across the entire system, by both infrastructure services (Gateway, Registry, Validator, etc.) and business services (hello-service, etc.).
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @onlineapps/service-common
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
### Wait for Infrastructure Ready
|
|
18
|
+
|
|
19
|
+
Wait for all infrastructure services to be ready before creating queues:
|
|
20
|
+
|
|
21
|
+
**Infrastructure Services:**
|
|
22
|
+
```javascript
|
|
23
|
+
const { waitForInfrastructureReady } = require('@onlineapps/service-common');
|
|
24
|
+
|
|
25
|
+
await waitForInfrastructureReady({
|
|
26
|
+
redisUrl: 'redis://api_node_cache:6379',
|
|
27
|
+
maxWait: 300000, // 5 minutes
|
|
28
|
+
checkInterval: 5000, // 5 seconds
|
|
29
|
+
logger: logger
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Now safe to create infrastructure queues
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Business Services:**
|
|
36
|
+
```javascript
|
|
37
|
+
const { waitForInfrastructureReady } = require('@onlineapps/service-common');
|
|
38
|
+
|
|
39
|
+
await waitForInfrastructureReady({
|
|
40
|
+
redisUrl: process.env.REDIS_URL,
|
|
41
|
+
maxWait: 60000, // 1 minute
|
|
42
|
+
checkInterval: 5000, // 5 seconds
|
|
43
|
+
logger: logger
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Now safe to create business queues
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## API
|
|
50
|
+
|
|
51
|
+
### `waitForInfrastructureReady(options)`
|
|
52
|
+
|
|
53
|
+
Waits for all infrastructure services to be reported as healthy by Registry.
|
|
54
|
+
|
|
55
|
+
**Options:**
|
|
56
|
+
- `redisUrl` (string): Redis URL (default: from ENV or `redis://api_node_cache:6379`)
|
|
57
|
+
- `maxWait` (number): Maximum wait time in ms (default: 300000 = 5 minutes)
|
|
58
|
+
- `checkInterval` (number): Check interval in ms (default: 5000 = 5 seconds)
|
|
59
|
+
- `logger` (Object): Logger instance (default: console)
|
|
60
|
+
|
|
61
|
+
**Returns:** `Promise<boolean>` - True if all infrastructure services are ready
|
|
62
|
+
|
|
63
|
+
**Throws:** `Error` - If timeout is reached
|
|
64
|
+
|
|
65
|
+
**Environment Variables:**
|
|
66
|
+
- `REDIS_URL` - Redis connection URL
|
|
67
|
+
- `INFRASTRUCTURE_HEALTH_WAIT_MAX_TIME` - Maximum wait time in ms
|
|
68
|
+
- `INFRASTRUCTURE_HEALTH_WAIT_CHECK_INTERVAL` - Check interval in ms
|
|
69
|
+
|
|
70
|
+
## Architecture
|
|
71
|
+
|
|
72
|
+
This library is used by:
|
|
73
|
+
- **Infrastructure services** (via `@onlineapps/infrastructure-tools` which re-exports from here)
|
|
74
|
+
- **Business services** (via `@onlineapps/service-wrapper` or directly)
|
|
75
|
+
|
|
76
|
+
This ensures no duplication and consistent behavior across all services.
|
|
77
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@onlineapps/service-common",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Common utilities for both infrastructure services and business services",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "jest",
|
|
8
|
+
"test:unit": "jest tests/unit",
|
|
9
|
+
"test:integration": "jest tests/integration"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"microservices",
|
|
13
|
+
"infrastructure",
|
|
14
|
+
"common",
|
|
15
|
+
"utilities"
|
|
16
|
+
],
|
|
17
|
+
"author": "OA Drive Team",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"redis": "^4.6.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"jest": "^29.7.0"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=14.0.0"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
package/src/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @onlineapps/service-common
|
|
5
|
+
*
|
|
6
|
+
* Common utilities for both infrastructure services and business services.
|
|
7
|
+
* Provides shared functionality that is used across the entire system.
|
|
8
|
+
*
|
|
9
|
+
* This library is for ALL services (infrastructure and business).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const { waitForInfrastructureReady } = require('./infrastructure/waitForInfrastructureReady');
|
|
13
|
+
|
|
14
|
+
module.exports = {
|
|
15
|
+
// Infrastructure readiness utilities (used by both infrastructure and business services)
|
|
16
|
+
waitForInfrastructureReady
|
|
17
|
+
};
|
|
18
|
+
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* waitForInfrastructureReady.js
|
|
5
|
+
*
|
|
6
|
+
* Waits for all infrastructure services to be verified as running and healthy.
|
|
7
|
+
* Used by BOTH infrastructure services and business services before creating queues
|
|
8
|
+
* to ensure system consistency.
|
|
9
|
+
*
|
|
10
|
+
* This prevents race conditions where queues are created before all services are ready.
|
|
11
|
+
*
|
|
12
|
+
* **How it works:**
|
|
13
|
+
* 1. Connects to Redis (where Registry stores infrastructure health status)
|
|
14
|
+
* 2. Checks `infrastructure:health:all` key every 5 seconds
|
|
15
|
+
* 3. If key is `"true"` → all infrastructure services are UP → return success
|
|
16
|
+
* 4. If key is `"false"` or missing → wait and retry
|
|
17
|
+
* 5. If timeout reached → throw error
|
|
18
|
+
*
|
|
19
|
+
* **Why Redis (not HTTP):**
|
|
20
|
+
* - Fast: In-memory lookup, no network overhead
|
|
21
|
+
* - Reliable: Redis is already required infrastructure
|
|
22
|
+
* - Low latency: Direct key lookup, no HTTP parsing
|
|
23
|
+
* - Consistent: Same data source as Registry uses
|
|
24
|
+
* - No single point of failure: Redis can be clustered
|
|
25
|
+
*
|
|
26
|
+
* **Usage:**
|
|
27
|
+
* - Infrastructure services: Wait before creating infrastructure queues
|
|
28
|
+
* - Business services: Wait before creating business queues
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Wait for all infrastructure services to be ready
|
|
33
|
+
* @param {Object} options - Options
|
|
34
|
+
* @param {string} [options.redisUrl] - Redis URL (default: REDIS_URL env or redis://api_node_cache:6379)
|
|
35
|
+
* @param {number} [options.maxWait] - Maximum wait time in ms (default: INFRASTRUCTURE_HEALTH_WAIT_MAX_TIME env or 300000 = 5 minutes)
|
|
36
|
+
* @param {number} [options.checkInterval] - Check interval in ms (default: INFRASTRUCTURE_HEALTH_WAIT_CHECK_INTERVAL env or 5000 = 5 seconds)
|
|
37
|
+
* @param {Object} [options.logger] - Logger instance (default: console)
|
|
38
|
+
* @returns {Promise<boolean>} - True if all infrastructure services are ready
|
|
39
|
+
* @throws {Error} - If timeout is reached
|
|
40
|
+
*
|
|
41
|
+
* Note: Default values can be overridden via ENV variables or options parameter.
|
|
42
|
+
* Registry config (config.infrastructureHealth) is the source of truth for these defaults.
|
|
43
|
+
*/
|
|
44
|
+
async function waitForInfrastructureReady(options = {}) {
|
|
45
|
+
const redisUrl = options.redisUrl || process.env.REDIS_URL || 'redis://api_node_cache:6379';
|
|
46
|
+
const maxWait = options.maxWait || parseInt(process.env.INFRASTRUCTURE_HEALTH_WAIT_MAX_TIME) || 300000; // 5 minutes
|
|
47
|
+
const checkInterval = options.checkInterval || parseInt(process.env.INFRASTRUCTURE_HEALTH_WAIT_CHECK_INTERVAL) || 5000; // 5 seconds
|
|
48
|
+
const logger = options.logger || console;
|
|
49
|
+
|
|
50
|
+
// Helper to log messages (compatible with both console and winston)
|
|
51
|
+
const log = (messageStr) => {
|
|
52
|
+
if (logger && typeof logger.log === 'function') {
|
|
53
|
+
// Winston-style logger
|
|
54
|
+
logger.log({ message: messageStr, level: 'info' });
|
|
55
|
+
} else if (logger && typeof logger.info === 'function') {
|
|
56
|
+
// Standard logger
|
|
57
|
+
logger.info(messageStr);
|
|
58
|
+
} else if (logger && typeof logger === 'function') {
|
|
59
|
+
// console.log or similar
|
|
60
|
+
logger(messageStr);
|
|
61
|
+
} else {
|
|
62
|
+
console.log(messageStr);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const startTime = Date.now();
|
|
67
|
+
let attemptCount = 0;
|
|
68
|
+
let redis = null;
|
|
69
|
+
|
|
70
|
+
log('[InfrastructureReady] Waiting for all infrastructure services to be ready...');
|
|
71
|
+
log(`[InfrastructureReady] Redis URL: ${redisUrl}`);
|
|
72
|
+
log(`[InfrastructureReady] Max wait: ${maxWait}ms, Check interval: ${checkInterval}ms`);
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
// Connect to Redis
|
|
76
|
+
const { createClient } = require('redis');
|
|
77
|
+
redis = createClient({ url: redisUrl });
|
|
78
|
+
|
|
79
|
+
redis.on('error', (err) => {
|
|
80
|
+
log(`[InfrastructureReady] Redis error: ${err.message}`);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
await redis.connect();
|
|
84
|
+
log('[InfrastructureReady] Connected to Redis');
|
|
85
|
+
|
|
86
|
+
while (Date.now() - startTime < maxWait) {
|
|
87
|
+
attemptCount++;
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
// Check Redis key: infrastructure:health:all
|
|
91
|
+
const allHealthy = await redis.get('infrastructure:health:all');
|
|
92
|
+
|
|
93
|
+
if (allHealthy === 'true') {
|
|
94
|
+
// All services are UP, we can proceed
|
|
95
|
+
const elapsed = Date.now() - startTime;
|
|
96
|
+
log(`[InfrastructureReady] ✓ All infrastructure services are ready (took ${elapsed}ms, ${attemptCount} attempts)`);
|
|
97
|
+
|
|
98
|
+
// Optionally log individual service status
|
|
99
|
+
// Note: Service names should match config.infrastructureServices in Registry
|
|
100
|
+
const serviceKeys = ['gateway', 'registry', 'validator', 'delivery', 'monitoring'];
|
|
101
|
+
const statuses = {};
|
|
102
|
+
for (const serviceName of serviceKeys) {
|
|
103
|
+
const status = await redis.get(`infrastructure:health:${serviceName}`);
|
|
104
|
+
if (status) {
|
|
105
|
+
statuses[serviceName] = JSON.parse(status);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (Object.keys(statuses).length > 0) {
|
|
109
|
+
log(`[InfrastructureReady] Individual service status: ${JSON.stringify(statuses, null, 2)}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
log(`[InfrastructureReady] Attempt ${attemptCount}: Not all services ready (current status: ${allHealthy || 'unknown'}). Waiting ${checkInterval}ms...`);
|
|
116
|
+
|
|
117
|
+
} catch (error) {
|
|
118
|
+
log(`[InfrastructureReady] Attempt ${attemptCount}: Failed to get infrastructure health from Redis (${error.message}). Waiting ${checkInterval}ms...`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
await new Promise(resolve => setTimeout(resolve, checkInterval));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const elapsed = Date.now() - startTime;
|
|
125
|
+
throw new Error(
|
|
126
|
+
`Infrastructure services not ready within ${maxWait}ms (${elapsed}ms elapsed, ${attemptCount} attempts). ` +
|
|
127
|
+
`Check Redis key 'infrastructure:health:all' and individual service health keys.`
|
|
128
|
+
);
|
|
129
|
+
} finally {
|
|
130
|
+
if (redis && redis.isReady) {
|
|
131
|
+
await redis.quit();
|
|
132
|
+
log('[InfrastructureReady] Disconnected from Redis.');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = { waitForInfrastructureReady };
|
|
138
|
+
|