@ahmedhegazee/nestjs-telescope
Version:
Advanced observability and monitoring solution for NestJS applications with ML-powered analytics, enterprise features, and production-ready scaling
272 lines • 11.9 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var PerformanceOptimizerService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PerformanceOptimizerService = void 0;
const common_1 = require("@nestjs/common");
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
let PerformanceOptimizerService = PerformanceOptimizerService_1 = class PerformanceOptimizerService {
constructor() {
this.logger = new common_1.Logger(PerformanceOptimizerService_1.name);
this.metricsSubject = new rxjs_1.Subject();
this.settingsSubject = new rxjs_1.BehaviorSubject({
batchSize: 100,
flushInterval: 1000,
maxConcurrency: 3,
memoryThreshold: 100 * 1024 * 1024,
adaptiveOptimization: true
});
this.performanceHistory = [];
this.maxHistorySize = 100;
this.lastOptimizationTime = 0;
this.optimizationInterval = 30000;
this.startPerformanceMonitoring();
}
startPerformanceMonitoring() {
setInterval(() => {
this.collectSystemMetrics();
}, 5000);
setInterval(() => {
this.analyzeAndOptimize();
}, this.optimizationInterval);
}
collectSystemMetrics() {
const metrics = {
averageProcessingTime: this.calculateAverageProcessingTime(),
throughput: this.calculateThroughput(),
memoryUsage: this.getMemoryUsage(),
cpuUsage: this.getCpuUsage(),
errorRate: this.calculateErrorRate(),
queueSize: this.getCurrentQueueSize()
};
this.metricsSubject.next(metrics);
this.addToHistory(metrics);
}
calculateAverageProcessingTime() {
return this.performanceHistory.length > 0
? this.performanceHistory.slice(-10).reduce((sum, m) => sum + m.averageProcessingTime, 0) / Math.min(10, this.performanceHistory.length)
: 0;
}
calculateThroughput() {
const recentMetrics = this.performanceHistory.slice(-12);
if (recentMetrics.length < 2)
return 0;
const timeSpan = 60;
const totalProcessed = recentMetrics.reduce((sum, m) => sum + (m.throughput || 0), 0);
return totalProcessed / timeSpan;
}
getMemoryUsage() {
const memUsage = process.memoryUsage();
return memUsage.heapUsed;
}
getCpuUsage() {
const usage = process.cpuUsage();
return (usage.user + usage.system) / 1000000;
}
calculateErrorRate() {
const recentMetrics = this.performanceHistory.slice(-12);
if (recentMetrics.length === 0)
return 0;
const totalErrors = recentMetrics.reduce((sum, m) => sum + (m.errorRate || 0), 0);
return totalErrors / recentMetrics.length;
}
getCurrentQueueSize() {
return 0;
}
addToHistory(metrics) {
this.performanceHistory.push(metrics);
if (this.performanceHistory.length > this.maxHistorySize) {
this.performanceHistory.shift();
}
}
analyzeAndOptimize() {
const now = Date.now();
if (now - this.lastOptimizationTime < this.optimizationInterval) {
return;
}
const currentSettings = this.settingsSubject.value;
if (!currentSettings.adaptiveOptimization) {
return;
}
const recommendations = this.generateOptimizationRecommendations();
if (recommendations.length > 0) {
this.logger.log(`Generated ${recommendations.length} optimization recommendations`);
this.applyOptimizations(recommendations);
}
this.lastOptimizationTime = now;
}
generateOptimizationRecommendations() {
const recommendations = [];
const currentSettings = this.settingsSubject.value;
const recentMetrics = this.performanceHistory.slice(-12);
if (recentMetrics.length < 5) {
return recommendations;
}
const avgMetrics = this.calculateAverageMetrics(recentMetrics);
if (avgMetrics.averageProcessingTime > 5000) {
recommendations.push({
type: 'batch_size',
current: currentSettings.batchSize,
recommended: Math.max(10, currentSettings.batchSize * 0.8),
reason: 'High processing time detected',
impact: 'medium',
confidence: 0.7
});
}
else if (avgMetrics.averageProcessingTime < 1000 && avgMetrics.throughput > 50) {
recommendations.push({
type: 'batch_size',
current: currentSettings.batchSize,
recommended: Math.min(500, currentSettings.batchSize * 1.2),
reason: 'Low processing time with high throughput',
impact: 'low',
confidence: 0.6
});
}
if (avgMetrics.queueSize > currentSettings.batchSize * 0.8) {
recommendations.push({
type: 'flush_interval',
current: currentSettings.flushInterval,
recommended: Math.max(500, currentSettings.flushInterval * 0.8),
reason: 'High queue size detected',
impact: 'medium',
confidence: 0.8
});
}
if (avgMetrics.cpuUsage > 80) {
recommendations.push({
type: 'concurrency',
current: currentSettings.maxConcurrency,
recommended: Math.max(1, currentSettings.maxConcurrency - 1),
reason: 'High CPU usage detected',
impact: 'high',
confidence: 0.9
});
}
else if (avgMetrics.cpuUsage < 30 && avgMetrics.averageProcessingTime > 3000) {
recommendations.push({
type: 'concurrency',
current: currentSettings.maxConcurrency,
recommended: Math.min(10, currentSettings.maxConcurrency + 1),
reason: 'Low CPU usage with high processing time',
impact: 'medium',
confidence: 0.7
});
}
if (avgMetrics.memoryUsage > currentSettings.memoryThreshold) {
recommendations.push({
type: 'memory',
current: currentSettings.batchSize,
recommended: Math.max(10, currentSettings.batchSize * 0.7),
reason: 'Memory usage above threshold',
impact: 'high',
confidence: 0.8
});
}
return recommendations;
}
calculateAverageMetrics(metrics) {
const count = metrics.length;
return {
averageProcessingTime: metrics.reduce((sum, m) => sum + m.averageProcessingTime, 0) / count,
throughput: metrics.reduce((sum, m) => sum + m.throughput, 0) / count,
memoryUsage: metrics.reduce((sum, m) => sum + m.memoryUsage, 0) / count,
cpuUsage: metrics.reduce((sum, m) => sum + m.cpuUsage, 0) / count,
errorRate: metrics.reduce((sum, m) => sum + m.errorRate, 0) / count,
queueSize: metrics.reduce((sum, m) => sum + m.queueSize, 0) / count
};
}
applyOptimizations(recommendations) {
const currentSettings = this.settingsSubject.value;
const newSettings = { ...currentSettings };
const sortedRecommendations = recommendations.sort((a, b) => {
const impactOrder = { high: 3, medium: 2, low: 1 };
return (impactOrder[b.impact] * b.confidence) - (impactOrder[a.impact] * a.confidence);
});
for (const recommendation of sortedRecommendations) {
if (recommendation.confidence < 0.6) {
continue;
}
switch (recommendation.type) {
case 'batch_size':
newSettings.batchSize = Math.round(recommendation.recommended);
this.logger.log(`Optimized batch size: ${recommendation.current} → ${newSettings.batchSize}`);
break;
case 'flush_interval':
newSettings.flushInterval = Math.round(recommendation.recommended);
this.logger.log(`Optimized flush interval: ${recommendation.current} → ${newSettings.flushInterval}`);
break;
case 'concurrency':
newSettings.maxConcurrency = Math.round(recommendation.recommended);
this.logger.log(`Optimized concurrency: ${recommendation.current} → ${newSettings.maxConcurrency}`);
break;
case 'memory':
newSettings.batchSize = Math.round(recommendation.recommended);
this.logger.log(`Optimized for memory: batch size ${recommendation.current} → ${newSettings.batchSize}`);
break;
}
}
this.settingsSubject.next(newSettings);
}
getMetricsStream() {
return this.metricsSubject.asObservable().pipe((0, operators_1.shareReplay)(1));
}
getSettingsStream() {
return this.settingsSubject.asObservable().pipe((0, operators_1.shareReplay)(1));
}
getCurrentMetrics() {
return this.performanceHistory.length > 0
? this.performanceHistory[this.performanceHistory.length - 1]
: null;
}
getCurrentSettings() {
return this.settingsSubject.value;
}
updateSettings(settings) {
const currentSettings = this.settingsSubject.value;
const newSettings = { ...currentSettings, ...settings };
this.settingsSubject.next(newSettings);
this.logger.log('Performance settings updated:', settings);
}
generatePerformanceReport() {
const recommendations = this.generateOptimizationRecommendations();
const recentHistory = this.performanceHistory.slice(-20);
return {
currentMetrics: this.getCurrentMetrics(),
settings: this.getCurrentSettings(),
recommendations,
trends: {
processingTime: recentHistory.map(m => m.averageProcessingTime),
throughput: recentHistory.map(m => m.throughput),
memoryUsage: recentHistory.map(m => m.memoryUsage),
errorRate: recentHistory.map(m => m.errorRate)
}
};
}
resetOptimizations() {
const defaultSettings = {
batchSize: 100,
flushInterval: 1000,
maxConcurrency: 3,
memoryThreshold: 100 * 1024 * 1024,
adaptiveOptimization: true
};
this.settingsSubject.next(defaultSettings);
this.logger.log('Performance settings reset to defaults');
}
};
exports.PerformanceOptimizerService = PerformanceOptimizerService;
exports.PerformanceOptimizerService = PerformanceOptimizerService = PerformanceOptimizerService_1 = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [])
], PerformanceOptimizerService);
//# sourceMappingURL=performance-optimizer.service.js.map