brainy/dist/distributed/healthMonitor.js
David Snelling f8c45f2d8d Initial commit: Brainy - Multi-Dimensional AI Database
Open source vector database with HNSW indexing, graph relationships,
and metadata facets. Features CLI with professional augmentation registry
integration for discovering extensions and capabilities.
2025-08-18 17:35:06 -07:00

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