@adalo/metrics 0.1.123 → 0.1.125
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/lib/health/healthCheckCache.d.ts +57 -0
- package/lib/health/healthCheckCache.d.ts.map +1 -0
- package/lib/health/healthCheckCache.js +200 -0
- package/lib/health/healthCheckCache.js.map +1 -0
- package/lib/{healthCheckClient.d.ts → health/healthCheckClient.d.ts} +54 -12
- package/lib/health/healthCheckClient.d.ts.map +1 -0
- package/lib/{healthCheckClient.js → health/healthCheckClient.js} +173 -27
- package/lib/health/healthCheckClient.js.map +1 -0
- package/lib/health/healthCheckUtils.d.ts +54 -0
- package/lib/health/healthCheckUtils.d.ts.map +1 -0
- package/lib/health/healthCheckUtils.js +142 -0
- package/lib/health/healthCheckUtils.js.map +1 -0
- package/lib/health/healthCheckWorker.d.ts +2 -0
- package/lib/health/healthCheckWorker.d.ts.map +1 -0
- package/lib/health/healthCheckWorker.js +64 -0
- package/lib/health/healthCheckWorker.js.map +1 -0
- package/lib/index.d.ts +8 -6
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +28 -6
- package/lib/index.js.map +1 -1
- package/lib/metrics/baseMetricsClient.d.ts.map +1 -0
- package/lib/metrics/baseMetricsClient.js.map +1 -0
- package/lib/metrics/metricsClient.d.ts.map +1 -0
- package/lib/metrics/metricsClient.js.map +1 -0
- package/lib/metrics/metricsDatabaseClient.d.ts.map +1 -0
- package/lib/metrics/metricsDatabaseClient.js.map +1 -0
- package/lib/metrics/metricsQueueRedisClient.d.ts.map +1 -0
- package/lib/{metricsQueueRedisClient.js → metrics/metricsQueueRedisClient.js} +2 -2
- package/lib/metrics/metricsQueueRedisClient.js.map +1 -0
- package/lib/metrics/metricsRedisClient.d.ts.map +1 -0
- package/lib/{metricsRedisClient.js → metrics/metricsRedisClient.js} +1 -1
- package/lib/metrics/metricsRedisClient.js.map +1 -0
- package/package.json +1 -1
- package/src/health/healthCheckCache.js +237 -0
- package/src/{healthCheckClient.js → health/healthCheckClient.js} +171 -31
- package/src/health/healthCheckUtils.js +143 -0
- package/src/health/healthCheckWorker.js +61 -0
- package/src/index.ts +8 -6
- package/src/{metricsQueueRedisClient.js → metrics/metricsQueueRedisClient.js} +2 -2
- package/src/{metricsRedisClient.js → metrics/metricsRedisClient.js} +1 -1
- package/lib/baseMetricsClient.d.ts.map +0 -1
- package/lib/baseMetricsClient.js.map +0 -1
- package/lib/healthCheckClient.d.ts.map +0 -1
- package/lib/healthCheckClient.js.map +0 -1
- package/lib/metricsClient.d.ts.map +0 -1
- package/lib/metricsClient.js.map +0 -1
- package/lib/metricsDatabaseClient.d.ts.map +0 -1
- package/lib/metricsDatabaseClient.js.map +0 -1
- package/lib/metricsQueueRedisClient.d.ts.map +0 -1
- package/lib/metricsQueueRedisClient.js.map +0 -1
- package/lib/metricsRedisClient.d.ts.map +0 -1
- package/lib/metricsRedisClient.js.map +0 -1
- /package/lib/{baseMetricsClient.d.ts → metrics/baseMetricsClient.d.ts} +0 -0
- /package/lib/{baseMetricsClient.js → metrics/baseMetricsClient.js} +0 -0
- /package/lib/{metricsClient.d.ts → metrics/metricsClient.d.ts} +0 -0
- /package/lib/{metricsClient.js → metrics/metricsClient.js} +0 -0
- /package/lib/{metricsDatabaseClient.d.ts → metrics/metricsDatabaseClient.d.ts} +0 -0
- /package/lib/{metricsDatabaseClient.js → metrics/metricsDatabaseClient.js} +0 -0
- /package/lib/{metricsQueueRedisClient.d.ts → metrics/metricsQueueRedisClient.d.ts} +0 -0
- /package/lib/{metricsRedisClient.d.ts → metrics/metricsRedisClient.d.ts} +0 -0
- /package/src/{baseMetricsClient.js → metrics/baseMetricsClient.js} +0 -0
- /package/src/{metricsClient.js → metrics/metricsClient.js} +0 -0
- /package/src/{metricsDatabaseClient.js → metrics/metricsDatabaseClient.js} +0 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HealthCheckCache provides a shared cache layer for health check results.
|
|
3
|
+
* It uses Redis if available for cross-process sharing, with graceful fallback
|
|
4
|
+
* to in-memory cache if Redis is not configured or unavailable.
|
|
5
|
+
*/
|
|
6
|
+
export class HealthCheckCache {
|
|
7
|
+
/**
|
|
8
|
+
* @param {Object} options
|
|
9
|
+
* @param {any} [options.redisClient] - Redis client instance (optional)
|
|
10
|
+
* @param {string} [options.appName] - Application name for cache key
|
|
11
|
+
* @param {number} [options.cacheTtlMs=60000] - Cache TTL in milliseconds
|
|
12
|
+
*/
|
|
13
|
+
constructor(options?: {
|
|
14
|
+
redisClient?: any;
|
|
15
|
+
appName?: string | undefined;
|
|
16
|
+
cacheTtlMs?: number | undefined;
|
|
17
|
+
});
|
|
18
|
+
redisClient: any;
|
|
19
|
+
appName: string;
|
|
20
|
+
cacheTtlMs: number;
|
|
21
|
+
cacheKey: string;
|
|
22
|
+
/** In-memory fallback cache */
|
|
23
|
+
_memoryCache: any;
|
|
24
|
+
_memoryCacheTimestamp: any;
|
|
25
|
+
_redisClientType: string | undefined;
|
|
26
|
+
_redisAvailable: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Checks if Redis is available and working.
|
|
29
|
+
* @returns {Promise<boolean>}
|
|
30
|
+
* @private
|
|
31
|
+
*/
|
|
32
|
+
private _checkRedisAvailable;
|
|
33
|
+
/**
|
|
34
|
+
* Gets cached health check result from Redis (if available) or in-memory cache.
|
|
35
|
+
* Throws error if Redis is configured but read fails (so caller can return proper error format).
|
|
36
|
+
* @returns {Promise<Object | null>} Cached result or null
|
|
37
|
+
* @throws {Error} If Redis is configured but read fails
|
|
38
|
+
*/
|
|
39
|
+
get(): Promise<Object | null>;
|
|
40
|
+
/**
|
|
41
|
+
* Sets cached health check result in Redis (if available) and in-memory.
|
|
42
|
+
* @param {Object} result - Health check result to cache
|
|
43
|
+
* @returns {Promise<void>}
|
|
44
|
+
*/
|
|
45
|
+
set(result: Object): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Clears the cache (both Redis and in-memory).
|
|
48
|
+
* @returns {Promise<void>}
|
|
49
|
+
*/
|
|
50
|
+
clear(): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Checks if Redis is configured and available.
|
|
53
|
+
* @returns {boolean}
|
|
54
|
+
*/
|
|
55
|
+
isRedisAvailable(): boolean;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=healthCheckCache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"healthCheckCache.d.ts","sourceRoot":"","sources":["../../src/health/healthCheckCache.js"],"names":[],"mappings":"AAOA;;;;GAIG;AACH;IACE;;;;;OAKG;IACH;QAJyB,WAAW,GAAzB,GAAG;QACc,OAAO;QACP,UAAU;OAqBrC;IAlBC,iBAA8C;IAC9C,gBAA6E;IAC7E,mBAAiD;IACjD,iBAA6C;IAE7C,+BAA+B;IAC/B,kBAAwB;IACxB,2BAAiC;IAG/B,qCAA4D;IAC5D,yBAA2B;IAS/B;;;;OAIG;IACH,6BAiCC;IAED;;;;;OAKG;IACH,OAHa,QAAQ,MAAM,GAAG,IAAI,CAAC,CA2DlC;IAED;;;;OAIG;IACH,YAHW,MAAM,GACJ,QAAQ,IAAI,CAAC,CA6CzB;IAED;;;OAGG;IACH,SAFa,QAAQ,IAAI,CAAC,CA4BzB;IAED;;;OAGG;IACH,oBAFa,OAAO,CAInB;CACF"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
getRedisClientType,
|
|
5
|
+
REDIS_V4,
|
|
6
|
+
IOREDIS,
|
|
7
|
+
REDIS_V3
|
|
8
|
+
} = require('../redisUtils');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* HealthCheckCache provides a shared cache layer for health check results.
|
|
12
|
+
* It uses Redis if available for cross-process sharing, with graceful fallback
|
|
13
|
+
* to in-memory cache if Redis is not configured or unavailable.
|
|
14
|
+
*/
|
|
15
|
+
class HealthCheckCache {
|
|
16
|
+
/**
|
|
17
|
+
* @param {Object} options
|
|
18
|
+
* @param {any} [options.redisClient] - Redis client instance (optional)
|
|
19
|
+
* @param {string} [options.appName] - Application name for cache key
|
|
20
|
+
* @param {number} [options.cacheTtlMs=60000] - Cache TTL in milliseconds
|
|
21
|
+
*/
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
this.redisClient = options.redisClient || null;
|
|
24
|
+
this.appName = options.appName || process.env.BUILD_APP_NAME || 'unknown-app';
|
|
25
|
+
this.cacheTtlMs = options.cacheTtlMs ?? 60 * 1000;
|
|
26
|
+
this.cacheKey = `healthcheck:${this.appName}`;
|
|
27
|
+
|
|
28
|
+
/** In-memory fallback cache */
|
|
29
|
+
this._memoryCache = null;
|
|
30
|
+
this._memoryCacheTimestamp = null;
|
|
31
|
+
if (this.redisClient) {
|
|
32
|
+
this._redisClientType = getRedisClientType(this.redisClient);
|
|
33
|
+
this._redisAvailable = true;
|
|
34
|
+
} else {
|
|
35
|
+
this._redisAvailable = false;
|
|
36
|
+
console.warn(`[HealthCheckCache] Redis not configured for ${this.appName}, using in-memory cache only (not shared across processes)`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Checks if Redis is available and working.
|
|
42
|
+
* @returns {Promise<boolean>}
|
|
43
|
+
* @private
|
|
44
|
+
*/
|
|
45
|
+
async _checkRedisAvailable() {
|
|
46
|
+
if (!this.redisClient || !this._redisAvailable) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
let pong;
|
|
51
|
+
if (this._redisClientType === REDIS_V3) {
|
|
52
|
+
pong = await new Promise((resolve, reject) => {
|
|
53
|
+
this.redisClient.ping((err, result) => {
|
|
54
|
+
if (err) reject(err);else resolve(result);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
} else if (this._redisClientType === REDIS_V4 || this._redisClientType === IOREDIS) {
|
|
58
|
+
pong = await this.redisClient.ping();
|
|
59
|
+
} else {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
return pong === 'PONG';
|
|
63
|
+
} catch (err) {
|
|
64
|
+
// Redis not available
|
|
65
|
+
if (this._redisAvailable) {
|
|
66
|
+
console.warn(`[HealthCheckCache] Redis became unavailable: ${err.message}`);
|
|
67
|
+
this._redisAvailable = false;
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Gets cached health check result from Redis (if available) or in-memory cache.
|
|
75
|
+
* Throws error if Redis is configured but read fails (so caller can return proper error format).
|
|
76
|
+
* @returns {Promise<Object | null>} Cached result or null
|
|
77
|
+
* @throws {Error} If Redis is configured but read fails
|
|
78
|
+
*/
|
|
79
|
+
async get() {
|
|
80
|
+
// If Redis is configured, we MUST read from it (don't fall back to memory)
|
|
81
|
+
if (this.redisClient) {
|
|
82
|
+
try {
|
|
83
|
+
let cachedStr;
|
|
84
|
+
if (this._redisClientType === REDIS_V3) {
|
|
85
|
+
cachedStr = await new Promise((resolve, reject) => {
|
|
86
|
+
this.redisClient.get(this.cacheKey, (err, result) => {
|
|
87
|
+
if (err) reject(err);else resolve(result);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
} else if (this._redisClientType === REDIS_V4 || this._redisClientType === IOREDIS) {
|
|
91
|
+
cachedStr = await this.redisClient.get(this.cacheKey);
|
|
92
|
+
}
|
|
93
|
+
if (cachedStr) {
|
|
94
|
+
try {
|
|
95
|
+
const cached = JSON.parse(cachedStr);
|
|
96
|
+
if (cached.result && cached.timestamp) {
|
|
97
|
+
const age = Date.now() - cached.timestamp;
|
|
98
|
+
if (age < this.cacheTtlMs) {
|
|
99
|
+
// Also update in-memory cache as backup
|
|
100
|
+
this._memoryCache = cached.result;
|
|
101
|
+
this._memoryCacheTimestamp = cached.timestamp;
|
|
102
|
+
return cached.result;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
} catch (parseErr) {
|
|
106
|
+
console.warn(`[HealthCheckCache] Failed to parse Redis cache:`, parseErr.message);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// No cache in Redis - return null (worker may not have run yet)
|
|
110
|
+
return null;
|
|
111
|
+
} catch (redisErr) {
|
|
112
|
+
// Redis read failed - throw error so caller can return proper error format
|
|
113
|
+
this._redisAvailable = false;
|
|
114
|
+
throw new Error(`Redis cache read failed: ${redisErr.message}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// No Redis configured - fall back to in-memory cache
|
|
119
|
+
if (this._memoryCache && this._memoryCacheTimestamp) {
|
|
120
|
+
const age = Date.now() - this._memoryCacheTimestamp;
|
|
121
|
+
if (age < this.cacheTtlMs) {
|
|
122
|
+
return this._memoryCache;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Sets cached health check result in Redis (if available) and in-memory.
|
|
130
|
+
* @param {Object} result - Health check result to cache
|
|
131
|
+
* @returns {Promise<void>}
|
|
132
|
+
*/
|
|
133
|
+
async set(result) {
|
|
134
|
+
const cacheData = {
|
|
135
|
+
result,
|
|
136
|
+
timestamp: Date.now()
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// Update in-memory cache
|
|
140
|
+
this._memoryCache = result;
|
|
141
|
+
this._memoryCacheTimestamp = cacheData.timestamp;
|
|
142
|
+
|
|
143
|
+
// Try to update Redis if available
|
|
144
|
+
if (await this._checkRedisAvailable()) {
|
|
145
|
+
try {
|
|
146
|
+
const cacheStr = JSON.stringify(cacheData);
|
|
147
|
+
const ttlSeconds = Math.ceil(this.cacheTtlMs / 1000) + 10;
|
|
148
|
+
if (this._redisClientType === REDIS_V3) {
|
|
149
|
+
await new Promise((resolve, reject) => {
|
|
150
|
+
this.redisClient.setex(this.cacheKey, ttlSeconds, cacheStr, err => {
|
|
151
|
+
if (err) reject(err);else resolve();
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
} else if (this._redisClientType === REDIS_V4 || this._redisClientType === IOREDIS) {
|
|
155
|
+
await this.redisClient.setex(this.cacheKey, ttlSeconds, cacheStr);
|
|
156
|
+
}
|
|
157
|
+
} catch (redisErr) {
|
|
158
|
+
// Redis write failed, but in-memory cache is updated
|
|
159
|
+
console.warn(`[HealthCheckCache] Redis write failed (in-memory cache updated):`, redisErr.message);
|
|
160
|
+
this._redisAvailable = false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Clears the cache (both Redis and in-memory).
|
|
167
|
+
* @returns {Promise<void>}
|
|
168
|
+
*/
|
|
169
|
+
async clear() {
|
|
170
|
+
this._memoryCache = null;
|
|
171
|
+
this._memoryCacheTimestamp = null;
|
|
172
|
+
if (await this._checkRedisAvailable()) {
|
|
173
|
+
try {
|
|
174
|
+
if (this._redisClientType === REDIS_V3) {
|
|
175
|
+
await new Promise((resolve, reject) => {
|
|
176
|
+
this.redisClient.del(this.cacheKey, err => {
|
|
177
|
+
if (err) reject(err);else resolve();
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
} else if (this._redisClientType === REDIS_V4 || this._redisClientType === IOREDIS) {
|
|
181
|
+
await this.redisClient.del(this.cacheKey);
|
|
182
|
+
}
|
|
183
|
+
} catch (redisErr) {
|
|
184
|
+
console.warn(`[HealthCheckCache] Redis clear failed:`, redisErr.message);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Checks if Redis is configured and available.
|
|
191
|
+
* @returns {boolean}
|
|
192
|
+
*/
|
|
193
|
+
isRedisAvailable() {
|
|
194
|
+
return this._redisAvailable && this.redisClient !== null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
module.exports = {
|
|
198
|
+
HealthCheckCache
|
|
199
|
+
};
|
|
200
|
+
//# sourceMappingURL=healthCheckCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"healthCheckCache.js","names":["getRedisClientType","REDIS_V4","IOREDIS","REDIS_V3","require","HealthCheckCache","constructor","options","redisClient","appName","process","env","BUILD_APP_NAME","cacheTtlMs","cacheKey","_memoryCache","_memoryCacheTimestamp","_redisClientType","_redisAvailable","console","warn","_checkRedisAvailable","pong","Promise","resolve","reject","ping","err","result","message","get","cachedStr","cached","JSON","parse","timestamp","age","Date","now","parseErr","redisErr","Error","set","cacheData","cacheStr","stringify","ttlSeconds","Math","ceil","setex","clear","del","isRedisAvailable","module","exports"],"sources":["../../src/health/healthCheckCache.js"],"sourcesContent":["const {\n getRedisClientType,\n REDIS_V4,\n IOREDIS,\n REDIS_V3,\n} = require('../redisUtils')\n\n/**\n * HealthCheckCache provides a shared cache layer for health check results.\n * It uses Redis if available for cross-process sharing, with graceful fallback\n * to in-memory cache if Redis is not configured or unavailable.\n */\nclass HealthCheckCache {\n /**\n * @param {Object} options\n * @param {any} [options.redisClient] - Redis client instance (optional)\n * @param {string} [options.appName] - Application name for cache key\n * @param {number} [options.cacheTtlMs=60000] - Cache TTL in milliseconds\n */\n constructor(options = {}) {\n this.redisClient = options.redisClient || null\n this.appName = options.appName || process.env.BUILD_APP_NAME || 'unknown-app'\n this.cacheTtlMs = options.cacheTtlMs ?? 60 * 1000\n this.cacheKey = `healthcheck:${this.appName}`\n \n /** In-memory fallback cache */\n this._memoryCache = null\n this._memoryCacheTimestamp = null\n\n if (this.redisClient) {\n this._redisClientType = getRedisClientType(this.redisClient)\n this._redisAvailable = true\n } else {\n this._redisAvailable = false\n console.warn(\n `[HealthCheckCache] Redis not configured for ${this.appName}, using in-memory cache only (not shared across processes)`\n )\n }\n }\n\n /**\n * Checks if Redis is available and working.\n * @returns {Promise<boolean>}\n * @private\n */\n async _checkRedisAvailable() {\n if (!this.redisClient || !this._redisAvailable) {\n return false\n }\n\n try {\n let pong\n if (this._redisClientType === REDIS_V3) {\n pong = await new Promise((resolve, reject) => {\n this.redisClient.ping((err, result) => {\n if (err) reject(err)\n else resolve(result)\n })\n })\n } else if (\n this._redisClientType === REDIS_V4 ||\n this._redisClientType === IOREDIS\n ) {\n pong = await this.redisClient.ping()\n } else {\n return false\n }\n return pong === 'PONG'\n } catch (err) {\n // Redis not available\n if (this._redisAvailable) {\n console.warn(\n `[HealthCheckCache] Redis became unavailable: ${err.message}`\n )\n this._redisAvailable = false\n }\n return false\n }\n }\n\n /**\n * Gets cached health check result from Redis (if available) or in-memory cache.\n * Throws error if Redis is configured but read fails (so caller can return proper error format).\n * @returns {Promise<Object | null>} Cached result or null\n * @throws {Error} If Redis is configured but read fails\n */\n async get() {\n // If Redis is configured, we MUST read from it (don't fall back to memory)\n if (this.redisClient) {\n try {\n let cachedStr\n if (this._redisClientType === REDIS_V3) {\n cachedStr = await new Promise((resolve, reject) => {\n this.redisClient.get(this.cacheKey, (err, result) => {\n if (err) reject(err)\n else resolve(result)\n })\n })\n } else if (\n this._redisClientType === REDIS_V4 ||\n this._redisClientType === IOREDIS\n ) {\n cachedStr = await this.redisClient.get(this.cacheKey)\n }\n\n if (cachedStr) {\n try {\n const cached = JSON.parse(cachedStr)\n if (cached.result && cached.timestamp) {\n const age = Date.now() - cached.timestamp\n if (age < this.cacheTtlMs) {\n // Also update in-memory cache as backup\n this._memoryCache = cached.result\n this._memoryCacheTimestamp = cached.timestamp\n return cached.result\n }\n }\n } catch (parseErr) {\n console.warn(\n `[HealthCheckCache] Failed to parse Redis cache:`,\n parseErr.message\n )\n }\n }\n // No cache in Redis - return null (worker may not have run yet)\n return null\n } catch (redisErr) {\n // Redis read failed - throw error so caller can return proper error format\n this._redisAvailable = false\n throw new Error(`Redis cache read failed: ${redisErr.message}`)\n }\n }\n\n // No Redis configured - fall back to in-memory cache\n if (this._memoryCache && this._memoryCacheTimestamp) {\n const age = Date.now() - this._memoryCacheTimestamp\n if (age < this.cacheTtlMs) {\n return this._memoryCache\n }\n }\n\n return null\n }\n\n /**\n * Sets cached health check result in Redis (if available) and in-memory.\n * @param {Object} result - Health check result to cache\n * @returns {Promise<void>}\n */\n async set(result) {\n const cacheData = {\n result,\n timestamp: Date.now(),\n }\n\n // Update in-memory cache\n this._memoryCache = result\n this._memoryCacheTimestamp = cacheData.timestamp\n\n // Try to update Redis if available\n if (await this._checkRedisAvailable()) {\n try {\n const cacheStr = JSON.stringify(cacheData)\n const ttlSeconds = Math.ceil(this.cacheTtlMs / 1000) + 10\n\n if (this._redisClientType === REDIS_V3) {\n await new Promise((resolve, reject) => {\n this.redisClient.setex(\n this.cacheKey,\n ttlSeconds,\n cacheStr,\n (err) => {\n if (err) reject(err)\n else resolve()\n }\n )\n })\n } else if (\n this._redisClientType === REDIS_V4 ||\n this._redisClientType === IOREDIS\n ) {\n await this.redisClient.setex(this.cacheKey, ttlSeconds, cacheStr)\n }\n } catch (redisErr) {\n // Redis write failed, but in-memory cache is updated\n console.warn(\n `[HealthCheckCache] Redis write failed (in-memory cache updated):`,\n redisErr.message\n )\n this._redisAvailable = false\n }\n }\n }\n\n /**\n * Clears the cache (both Redis and in-memory).\n * @returns {Promise<void>}\n */\n async clear() {\n this._memoryCache = null\n this._memoryCacheTimestamp = null\n\n if (await this._checkRedisAvailable()) {\n try {\n if (this._redisClientType === REDIS_V3) {\n await new Promise((resolve, reject) => {\n this.redisClient.del(this.cacheKey, (err) => {\n if (err) reject(err)\n else resolve()\n })\n })\n } else if (\n this._redisClientType === REDIS_V4 ||\n this._redisClientType === IOREDIS\n ) {\n await this.redisClient.del(this.cacheKey)\n }\n } catch (redisErr) {\n console.warn(\n `[HealthCheckCache] Redis clear failed:`,\n redisErr.message\n )\n }\n }\n }\n\n /**\n * Checks if Redis is configured and available.\n * @returns {boolean}\n */\n isRedisAvailable() {\n return this._redisAvailable && this.redisClient !== null\n }\n}\n\nmodule.exports = { HealthCheckCache }\n\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -5,7 +5,11 @@ export type ComponentHealth = {
|
|
|
5
5
|
*/
|
|
6
6
|
status: HealthStatus;
|
|
7
7
|
/**
|
|
8
|
-
* -
|
|
8
|
+
* - Error message if status is unhealthy
|
|
9
|
+
*/
|
|
10
|
+
error?: string | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* - Optional status message (deprecated, use error)
|
|
9
13
|
*/
|
|
10
14
|
message?: string | undefined;
|
|
11
15
|
/**
|
|
@@ -35,15 +39,15 @@ export type HealthCheckResult = {
|
|
|
35
39
|
*/
|
|
36
40
|
timestamp: string;
|
|
37
41
|
/**
|
|
38
|
-
* -
|
|
42
|
+
* - Individual service health checks
|
|
39
43
|
*/
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* - Individual component health
|
|
43
|
-
*/
|
|
44
|
-
components: {
|
|
44
|
+
checks: {
|
|
45
45
|
[x: string]: ComponentHealth | DatabaseClusterHealth;
|
|
46
46
|
};
|
|
47
|
+
/**
|
|
48
|
+
* - Top-level error messages (not related to specific services)
|
|
49
|
+
*/
|
|
50
|
+
errors?: string[] | undefined;
|
|
47
51
|
};
|
|
48
52
|
export type CachedHealthResult = {
|
|
49
53
|
/**
|
|
@@ -71,7 +75,8 @@ export type DatabaseConfig = {
|
|
|
71
75
|
/**
|
|
72
76
|
* @typedef {Object} ComponentHealth
|
|
73
77
|
* @property {HealthStatus} status - Component health status
|
|
74
|
-
* @property {string} [
|
|
78
|
+
* @property {string} [error] - Error message if status is unhealthy
|
|
79
|
+
* @property {string} [message] - Optional status message (deprecated, use error)
|
|
75
80
|
* @property {number} [latencyMs] - Connection latency in milliseconds
|
|
76
81
|
*/
|
|
77
82
|
/**
|
|
@@ -83,8 +88,8 @@ export type DatabaseConfig = {
|
|
|
83
88
|
* @typedef {Object} HealthCheckResult
|
|
84
89
|
* @property {HealthStatus} status - Overall health status
|
|
85
90
|
* @property {string} timestamp - ISO timestamp of the check
|
|
86
|
-
* @property {
|
|
87
|
-
* @property {
|
|
91
|
+
* @property {Object<string, ComponentHealth | DatabaseClusterHealth>} checks - Individual service health checks
|
|
92
|
+
* @property {string[]} [errors] - Top-level error messages (not related to specific services)
|
|
88
93
|
*/
|
|
89
94
|
/**
|
|
90
95
|
* @typedef {Object} CachedHealthResult
|
|
@@ -114,7 +119,8 @@ export class HealthCheckClient {
|
|
|
114
119
|
* @param {string} [options.databaseUrl] - Main PostgreSQL connection URL
|
|
115
120
|
* @param {string} [options.databaseName='main'] - Name for the main database
|
|
116
121
|
* @param {Object<string, string>} [options.additionalDatabaseUrls] - Additional DB clusters (name -> URL)
|
|
117
|
-
* @param {any} [options.redisClient] - Redis client instance (ioredis or node-redis)
|
|
122
|
+
* @param {any} [options.redisClient] - Redis client instance (ioredis or node-redis) - used for cache
|
|
123
|
+
* @param {boolean} [options.includeRedisCheck=false] - Include Redis health check in results (for backend)
|
|
118
124
|
* @param {number} [options.cacheTtlMs=60000] - Cache TTL in milliseconds (default: 60s)
|
|
119
125
|
* @param {string} [options.appName] - Application name for logging
|
|
120
126
|
*/
|
|
@@ -125,15 +131,19 @@ export class HealthCheckClient {
|
|
|
125
131
|
[x: string]: string;
|
|
126
132
|
} | undefined;
|
|
127
133
|
redisClient?: any;
|
|
134
|
+
includeRedisCheck?: boolean | undefined;
|
|
128
135
|
cacheTtlMs?: number | undefined;
|
|
129
136
|
appName?: string | undefined;
|
|
130
137
|
});
|
|
131
138
|
redisClient: any;
|
|
139
|
+
includeRedisCheck: boolean;
|
|
132
140
|
cacheTtlMs: number;
|
|
133
141
|
appName: string;
|
|
134
142
|
prefixLogs: string;
|
|
135
143
|
/** @type {CachedHealthResult | null} */
|
|
136
144
|
_cachedResult: CachedHealthResult | null;
|
|
145
|
+
/** @type {Promise<HealthCheckResult> | null} */
|
|
146
|
+
_refreshPromise: Promise<HealthCheckResult> | null;
|
|
137
147
|
/** @type {Map<string, Pool>} */
|
|
138
148
|
_databasePools: Map<string, Pool>;
|
|
139
149
|
/** @type {DatabaseConfig | null} */
|
|
@@ -141,6 +151,7 @@ export class HealthCheckClient {
|
|
|
141
151
|
/** @type {DatabaseConfig[]} */
|
|
142
152
|
_clusterConfigs: DatabaseConfig[];
|
|
143
153
|
_redisClientType: string | undefined;
|
|
154
|
+
_cache: HealthCheckCache;
|
|
144
155
|
/**
|
|
145
156
|
* Initialize database configurations from options.
|
|
146
157
|
* @param {Object} options - Constructor options
|
|
@@ -182,10 +193,37 @@ export class HealthCheckClient {
|
|
|
182
193
|
/**
|
|
183
194
|
* Performs a full health check on all configured components.
|
|
184
195
|
* Results are cached for the configured TTL to prevent excessive load.
|
|
196
|
+
* Uses a mutex pattern to prevent concurrent health checks when cache expires.
|
|
197
|
+
* If cache is expired but a refresh is in progress, returns stale cache (if available).
|
|
185
198
|
*
|
|
186
199
|
* @returns {Promise<HealthCheckResult>}
|
|
187
200
|
*/
|
|
188
201
|
performHealthCheck(): Promise<HealthCheckResult>;
|
|
202
|
+
/**
|
|
203
|
+
* Internal method that actually performs the health check.
|
|
204
|
+
* This is separated to allow the mutex pattern in performHealthCheck.
|
|
205
|
+
* Only checks database - Redis is used only for cache, not health check.
|
|
206
|
+
*
|
|
207
|
+
* @returns {Promise<HealthCheckResult>}
|
|
208
|
+
* @private
|
|
209
|
+
*/
|
|
210
|
+
private _performHealthCheckInternal;
|
|
211
|
+
/**
|
|
212
|
+
* Gets cached result from shared cache (Redis if available, otherwise in-memory).
|
|
213
|
+
* Returns null if no cache is available. This is used by endpoints to read-only access.
|
|
214
|
+
* If Redis fails, returns error result with proper format.
|
|
215
|
+
*
|
|
216
|
+
* @returns {Promise<HealthCheckResult | null>} Cached result, error result if Redis fails, or null if not available
|
|
217
|
+
*/
|
|
218
|
+
getCachedResult(): Promise<HealthCheckResult | null>;
|
|
219
|
+
/**
|
|
220
|
+
* Forces a refresh of the health check cache.
|
|
221
|
+
* This is used by background workers to periodically update the cache.
|
|
222
|
+
* Updates shared cache (Redis if available, otherwise in-memory).
|
|
223
|
+
*
|
|
224
|
+
* @returns {Promise<HealthCheckResult>}
|
|
225
|
+
*/
|
|
226
|
+
refreshCache(): Promise<HealthCheckResult>;
|
|
189
227
|
/**
|
|
190
228
|
* Clears the cached health check result, forcing the next check to be fresh.
|
|
191
229
|
*/
|
|
@@ -204,6 +242,9 @@ export class HealthCheckClient {
|
|
|
204
242
|
* Response includes errors array when status is not healthy.
|
|
205
243
|
* All sensitive data (passwords, connection strings, etc.) is masked.
|
|
206
244
|
*
|
|
245
|
+
* This handler only reads from cache and never triggers database queries.
|
|
246
|
+
* Use a background worker to periodically refresh the cache.
|
|
247
|
+
*
|
|
207
248
|
* @returns {(req: any, res: any) => Promise<void>} Express request handler
|
|
208
249
|
*/
|
|
209
250
|
healthHandler(): (req: any, res: any) => Promise<void>;
|
|
@@ -211,7 +252,7 @@ export class HealthCheckClient {
|
|
|
211
252
|
* Register health check endpoint on an Express app.
|
|
212
253
|
*
|
|
213
254
|
* @param {import('express').Application} app - Express application
|
|
214
|
-
* @param {string} [path='/health
|
|
255
|
+
* @param {string} [path='/health'] - Path for the health endpoint
|
|
215
256
|
*/
|
|
216
257
|
registerHealthEndpoint(app: any, path?: string | undefined): void;
|
|
217
258
|
/**
|
|
@@ -221,4 +262,5 @@ export class HealthCheckClient {
|
|
|
221
262
|
cleanup(): Promise<void>;
|
|
222
263
|
}
|
|
223
264
|
import { Pool } from "pg";
|
|
265
|
+
import { HealthCheckCache } from "./healthCheckCache";
|
|
224
266
|
//# sourceMappingURL=healthCheckClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"healthCheckClient.d.ts","sourceRoot":"","sources":["../../src/health/healthCheckClient.js"],"names":[],"mappings":"2BAiEa,SAAS,GAAG,WAAW,GAAG,UAAU;;;;;YAKnC,YAAY;;;;;;;;;;;;;;;;;;YAQZ,YAAY;;;;;YACL,MAAM,GAAE,eAAe;;;;;;;YAK9B,YAAY;;;;eACZ,MAAM;;;;;YACC,MAAM,GAAE,eAAe,GAAG,qBAAqB;;;;;;;;;;;YAMtD,iBAAiB;;;;eACjB,MAAM;;;;;;UAKN,MAAM;;;;SACN,MAAM;;AAnCpB;;GAEG;AAEH;;;;;;GAMG;AAEH;;;;GAIG;AAEH;;;;;;GAMG;AAEH;;;;GAIG;AAEH;;;;GAIG;AAEH;;;;;;;;;;;GAWG;AACH;IACE;;;;;;;;;OASG;IACH;QAR4B,WAAW;QACX,YAAY;QACI,sBAAsB;;;QACzC,WAAW,GAAzB,GAAG;QACe,iBAAiB;QAClB,UAAU;QACV,OAAO;OAqClC;IAlCC,iBAA8C;IAC9C,2BAA2D;IAC3D,mBAAiE;IACjE,gBACgE;IAEhE,mBAAmD;IAEnD,wCAAwC;IACxC,eADW,kBAAkB,GAAG,IAAI,CACX;IAEzB,gDAAgD;IAChD,iBADW,QAAQ,iBAAiB,CAAC,GAAG,IAAI,CACjB;IAE3B,gCAAgC;IAChC,gBADW,IAAI,MAAM,EAAE,IAAI,CAAC,CACG;IAE/B,oCAAoC;IACpC,qBADW,cAAc,GAAG,IAAI,CACD;IAE/B,+BAA+B;IAC/B,iBADW,cAAc,EAAE,CACF;IAKvB,qCAA4D;IAG9D,yBAIE;IAGJ;;;;OAIG;IACH,uBAcC;IAED;;;;;OAKG;IACH,iBAaC;IAED;;;;OAIG;IACH,sBAGC;IAED;;;;;OAKG;IACH,6BAiBC;IAED;;;;OAIG;IACH,2BAsDC;IAED;;;;OAIG;IACH,oBA6CC;IAED;;;;;;;OAOG;IACH,sBAFa,QAAQ,iBAAiB,CAAC,CAqCtC;IAED;;;;;;;OAOG;IACH,oCAoCC;IAED;;;;;;OAMG;IACH,mBAFa,QAAQ,iBAAiB,GAAG,IAAI,CAAC,CA0B7C;IAED;;;;;;OAMG;IACH,gBAFa,QAAQ,iBAAiB,CAAC,CAOtC;IAED;;OAEG;IACH,mBAGC;IAED;;;;;;OAMG;IACH,0BA8BC;IAED;;;;;;;;;;OAUG;IACH,uBAFmB,GAAG,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,CAuDjD;IAED;;;;;OAKG;IACH,kEAGC;IAED;;;OAGG;IACH,WAFa,QAAQ,IAAI,CAAC,CAWzB;CACF"}
|