Open source vector database with HNSW indexing, graph relationships, and metadata facets. Features CLI with professional augmentation registry integration for discovering extensions and capabilities.
244 lines
No EOL
7.6 KiB
JavaScript
244 lines
No EOL
7.6 KiB
JavaScript
/**
|
|
* Health Monitor
|
|
* Monitors and reports instance health in distributed deployments
|
|
*/
|
|
export class HealthMonitor {
|
|
constructor(configManager) {
|
|
this.requestCount = 0;
|
|
this.errorCount = 0;
|
|
this.totalLatency = 0;
|
|
this.cacheHits = 0;
|
|
this.cacheMisses = 0;
|
|
this.vectorCount = 0;
|
|
this.checkInterval = 30000; // 30 seconds
|
|
this.metricsWindow = []; // Sliding window for RPS calculation
|
|
this.latencyWindow = []; // Sliding window for latency
|
|
this.windowSize = 60000; // 1 minute window
|
|
this.configManager = configManager;
|
|
this.startTime = Date.now();
|
|
}
|
|
/**
|
|
* Start health monitoring
|
|
*/
|
|
start() {
|
|
// Initial health update
|
|
this.updateHealth();
|
|
// Schedule periodic health checks
|
|
this.healthCheckTimer = setInterval(() => {
|
|
this.updateHealth();
|
|
}, this.checkInterval);
|
|
}
|
|
/**
|
|
* Stop health monitoring
|
|
*/
|
|
stop() {
|
|
if (this.healthCheckTimer) {
|
|
clearInterval(this.healthCheckTimer);
|
|
this.healthCheckTimer = undefined;
|
|
}
|
|
}
|
|
/**
|
|
* Update health status and metrics
|
|
*/
|
|
async updateHealth() {
|
|
const metrics = this.collectMetrics();
|
|
// Update config with latest metrics
|
|
await this.configManager.updateMetrics({
|
|
vectorCount: metrics.vectorCount,
|
|
cacheHitRate: metrics.cacheHitRate,
|
|
memoryUsage: metrics.memoryUsage,
|
|
cpuUsage: metrics.cpuUsage
|
|
});
|
|
// Clean sliding windows
|
|
this.cleanWindows();
|
|
}
|
|
/**
|
|
* Collect current metrics
|
|
*/
|
|
collectMetrics() {
|
|
const memUsage = process.memoryUsage();
|
|
return {
|
|
vectorCount: this.vectorCount,
|
|
cacheHitRate: this.calculateCacheHitRate(),
|
|
memoryUsage: memUsage.heapUsed,
|
|
cpuUsage: this.getCPUUsage(),
|
|
requestsPerSecond: this.calculateRPS(),
|
|
averageLatency: this.calculateAverageLatency(),
|
|
errorRate: this.calculateErrorRate()
|
|
};
|
|
}
|
|
/**
|
|
* Calculate cache hit rate
|
|
*/
|
|
calculateCacheHitRate() {
|
|
const total = this.cacheHits + this.cacheMisses;
|
|
if (total === 0)
|
|
return 0;
|
|
return this.cacheHits / total;
|
|
}
|
|
/**
|
|
* Calculate requests per second
|
|
*/
|
|
calculateRPS() {
|
|
const now = Date.now();
|
|
const recentRequests = this.metricsWindow.filter(timestamp => now - timestamp < this.windowSize);
|
|
return recentRequests.length / (this.windowSize / 1000);
|
|
}
|
|
/**
|
|
* Calculate average latency
|
|
*/
|
|
calculateAverageLatency() {
|
|
if (this.latencyWindow.length === 0)
|
|
return 0;
|
|
const sum = this.latencyWindow.reduce((a, b) => a + b, 0);
|
|
return sum / this.latencyWindow.length;
|
|
}
|
|
/**
|
|
* Calculate error rate
|
|
*/
|
|
calculateErrorRate() {
|
|
if (this.requestCount === 0)
|
|
return 0;
|
|
return this.errorCount / this.requestCount;
|
|
}
|
|
/**
|
|
* Get CPU usage (simplified)
|
|
*/
|
|
getCPUUsage() {
|
|
// Simplified CPU usage based on process time
|
|
const usage = process.cpuUsage();
|
|
const total = usage.user + usage.system;
|
|
const seconds = (Date.now() - this.startTime) / 1000;
|
|
return Math.min(100, (total / 1000000 / seconds) * 100);
|
|
}
|
|
/**
|
|
* Clean old entries from sliding windows
|
|
*/
|
|
cleanWindows() {
|
|
const now = Date.now();
|
|
const cutoff = now - this.windowSize;
|
|
this.metricsWindow = this.metricsWindow.filter(t => t > cutoff);
|
|
// Keep only recent latency measurements
|
|
if (this.latencyWindow.length > 100) {
|
|
this.latencyWindow = this.latencyWindow.slice(-100);
|
|
}
|
|
}
|
|
/**
|
|
* Record a request
|
|
* @param latency - Request latency in milliseconds
|
|
* @param error - Whether the request resulted in an error
|
|
*/
|
|
recordRequest(latency, error = false) {
|
|
this.requestCount++;
|
|
this.metricsWindow.push(Date.now());
|
|
this.latencyWindow.push(latency);
|
|
if (error) {
|
|
this.errorCount++;
|
|
}
|
|
}
|
|
/**
|
|
* Record cache access
|
|
* @param hit - Whether it was a cache hit
|
|
*/
|
|
recordCacheAccess(hit) {
|
|
if (hit) {
|
|
this.cacheHits++;
|
|
}
|
|
else {
|
|
this.cacheMisses++;
|
|
}
|
|
}
|
|
/**
|
|
* Update vector count
|
|
* @param count - New vector count
|
|
*/
|
|
updateVectorCount(count) {
|
|
this.vectorCount = count;
|
|
}
|
|
/**
|
|
* Get current health status
|
|
* @returns Health status object
|
|
*/
|
|
getHealthStatus() {
|
|
const metrics = this.collectMetrics();
|
|
const uptime = Date.now() - this.startTime;
|
|
const warnings = [];
|
|
const errors = [];
|
|
// Check for warnings
|
|
if (metrics.memoryUsage > 1024 * 1024 * 1024) { // > 1GB
|
|
warnings.push('High memory usage detected');
|
|
}
|
|
if (metrics.cacheHitRate < 0.5) {
|
|
warnings.push('Low cache hit rate');
|
|
}
|
|
if (metrics.errorRate && metrics.errorRate > 0.05) {
|
|
warnings.push('High error rate detected');
|
|
}
|
|
if (metrics.averageLatency && metrics.averageLatency > 1000) {
|
|
warnings.push('High latency detected');
|
|
}
|
|
// Check for errors
|
|
if (metrics.memoryUsage > 2 * 1024 * 1024 * 1024) { // > 2GB
|
|
errors.push('Critical memory usage');
|
|
}
|
|
if (metrics.errorRate && metrics.errorRate > 0.2) {
|
|
errors.push('Critical error rate');
|
|
}
|
|
// Determine overall status
|
|
let status = 'healthy';
|
|
if (errors.length > 0) {
|
|
status = 'unhealthy';
|
|
}
|
|
else if (warnings.length > 0) {
|
|
status = 'degraded';
|
|
}
|
|
return {
|
|
status,
|
|
instanceId: this.configManager.getInstanceId(),
|
|
role: this.configManager.getRole(),
|
|
uptime,
|
|
lastCheck: new Date().toISOString(),
|
|
metrics,
|
|
warnings: warnings.length > 0 ? warnings : undefined,
|
|
errors: errors.length > 0 ? errors : undefined
|
|
};
|
|
}
|
|
/**
|
|
* Get health check endpoint data
|
|
* @returns JSON-serializable health data
|
|
*/
|
|
getHealthEndpointData() {
|
|
const status = this.getHealthStatus();
|
|
return {
|
|
status: status.status,
|
|
instanceId: status.instanceId,
|
|
role: status.role,
|
|
uptime: Math.floor(status.uptime / 1000), // Convert to seconds
|
|
lastCheck: status.lastCheck,
|
|
metrics: {
|
|
vectorCount: status.metrics.vectorCount,
|
|
cacheHitRate: Math.round(status.metrics.cacheHitRate * 100) / 100,
|
|
memoryUsageMB: Math.round(status.metrics.memoryUsage / 1024 / 1024),
|
|
cpuUsagePercent: Math.round(status.metrics.cpuUsage || 0),
|
|
requestsPerSecond: Math.round(status.metrics.requestsPerSecond || 0),
|
|
averageLatencyMs: Math.round(status.metrics.averageLatency || 0),
|
|
errorRate: Math.round((status.metrics.errorRate || 0) * 100) / 100
|
|
},
|
|
warnings: status.warnings,
|
|
errors: status.errors
|
|
};
|
|
}
|
|
/**
|
|
* Reset metrics (useful for testing)
|
|
*/
|
|
resetMetrics() {
|
|
this.requestCount = 0;
|
|
this.errorCount = 0;
|
|
this.totalLatency = 0;
|
|
this.cacheHits = 0;
|
|
this.cacheMisses = 0;
|
|
this.metricsWindow = [];
|
|
this.latencyWindow = [];
|
|
}
|
|
}
|
|
//# sourceMappingURL=healthMonitor.js.map
|