UNPKG

@ahmedhegazee/nestjs-telescope

Version:

Advanced observability and monitoring solution for NestJS applications with ML-powered analytics, enterprise features, and production-ready scaling

219 lines 9.96 kB
"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 EnhancedCircuitBreakerService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.EnhancedCircuitBreakerService = exports.CircuitBreakerState = void 0; const common_1 = require("@nestjs/common"); var CircuitBreakerState; (function (CircuitBreakerState) { CircuitBreakerState["CLOSED"] = "CLOSED"; CircuitBreakerState["OPEN"] = "OPEN"; CircuitBreakerState["HALF_OPEN"] = "HALF_OPEN"; })(CircuitBreakerState || (exports.CircuitBreakerState = CircuitBreakerState = {})); let EnhancedCircuitBreakerService = EnhancedCircuitBreakerService_1 = class EnhancedCircuitBreakerService { constructor() { this.logger = new common_1.Logger(EnhancedCircuitBreakerService_1.name); this.circuitBreakers = new Map(); this.requestHistory = new Map(); this.defaultConfig = { failureThreshold: 5, recoveryTimeout: 30000, monitoringWindow: 60000, minimumRequests: 3, successThreshold: 3 }; } async execute(operationName, operation, config = {}) { const finalConfig = { ...this.defaultConfig, ...config }; const stats = this.getOrCreateStats(operationName); if (!this.canExecute(operationName, finalConfig)) { throw new Error(`Circuit breaker is OPEN for operation: ${operationName}`); } const startTime = Date.now(); try { const result = await operation(); this.recordSuccess(operationName, finalConfig); this.logger.debug(`Circuit breaker operation succeeded: ${operationName} (${Date.now() - startTime}ms)`); return result; } catch (error) { this.recordFailure(operationName, finalConfig); this.logger.warn(`Circuit breaker operation failed: ${operationName} (${Date.now() - startTime}ms)`, error.message); throw error; } } async executeWithFallback(operationName, operation, fallback, config = {}) { try { return await this.execute(operationName, operation, config); } catch (error) { this.logger.warn(`Primary operation failed, executing fallback: ${operationName}`, error.message); try { return await fallback(); } catch (fallbackError) { this.logger.error(`Both primary and fallback operations failed: ${operationName}`, fallbackError.message); throw fallbackError; } } } canExecute(operationName, config) { const stats = this.getOrCreateStats(operationName); const now = Date.now(); switch (stats.state) { case CircuitBreakerState.CLOSED: return true; case CircuitBreakerState.OPEN: if (stats.nextAttemptTime && now >= stats.nextAttemptTime) { stats.state = CircuitBreakerState.HALF_OPEN; stats.successCount = 0; stats.failureCount = 0; this.logger.debug(`Circuit breaker moved to HALF_OPEN: ${operationName}`); return true; } return false; case CircuitBreakerState.HALF_OPEN: return true; default: return false; } } getStats(operationName) { return this.circuitBreakers.get(operationName) || null; } getAllStats() { const stats = {}; for (const [name, stat] of this.circuitBreakers.entries()) { stats[name] = { ...stat }; } return stats; } reset(operationName) { const stats = this.circuitBreakers.get(operationName); if (stats) { stats.state = CircuitBreakerState.CLOSED; stats.failureCount = 0; stats.successCount = 0; stats.totalRequests = 0; stats.lastFailureTime = null; stats.lastSuccessTime = null; stats.nextAttemptTime = null; this.requestHistory.delete(operationName); this.logger.debug(`Circuit breaker reset: ${operationName}`); } } forceOpen(operationName, recoveryTimeout) { const stats = this.getOrCreateStats(operationName); stats.state = CircuitBreakerState.OPEN; stats.nextAttemptTime = Date.now() + (recoveryTimeout || this.defaultConfig.recoveryTimeout); this.logger.warn(`Circuit breaker forced OPEN: ${operationName}`); } forceClose(operationName) { const stats = this.getOrCreateStats(operationName); stats.state = CircuitBreakerState.CLOSED; stats.failureCount = 0; stats.nextAttemptTime = null; this.logger.debug(`Circuit breaker forced CLOSED: ${operationName}`); } getOrCreateStats(operationName) { let stats = this.circuitBreakers.get(operationName); if (!stats) { stats = { state: CircuitBreakerState.CLOSED, failureCount: 0, successCount: 0, totalRequests: 0, lastFailureTime: null, lastSuccessTime: null, nextAttemptTime: null }; this.circuitBreakers.set(operationName, stats); this.requestHistory.set(operationName, []); } return stats; } recordSuccess(operationName, config) { const stats = this.getOrCreateStats(operationName); const history = this.requestHistory.get(operationName); const now = Date.now(); stats.successCount++; stats.totalRequests++; stats.lastSuccessTime = now; history.push({ timestamp: now, success: true }); this.cleanupHistory(operationName, config.monitoringWindow); switch (stats.state) { case CircuitBreakerState.HALF_OPEN: if (stats.successCount >= config.successThreshold) { stats.state = CircuitBreakerState.CLOSED; stats.failureCount = 0; stats.successCount = 0; this.logger.debug(`Circuit breaker moved to CLOSED: ${operationName}`); } break; case CircuitBreakerState.OPEN: stats.state = CircuitBreakerState.CLOSED; stats.failureCount = 0; this.logger.debug(`Circuit breaker unexpectedly recovered to CLOSED: ${operationName}`); break; } } recordFailure(operationName, config) { const stats = this.getOrCreateStats(operationName); const history = this.requestHistory.get(operationName); const now = Date.now(); stats.failureCount++; stats.totalRequests++; stats.lastFailureTime = now; history.push({ timestamp: now, success: false }); this.cleanupHistory(operationName, config.monitoringWindow); const recentRequests = this.getRecentRequests(operationName, config.monitoringWindow); const recentFailures = recentRequests.filter(r => !r.success).length; if (recentRequests.length >= config.minimumRequests && recentFailures >= config.failureThreshold) { stats.state = CircuitBreakerState.OPEN; stats.nextAttemptTime = now + config.recoveryTimeout; this.logger.warn(`Circuit breaker opened for ${operationName}: ${recentFailures} failures in ${recentRequests.length} requests`); } if (stats.state === CircuitBreakerState.HALF_OPEN) { stats.state = CircuitBreakerState.OPEN; stats.nextAttemptTime = now + config.recoveryTimeout; this.logger.warn(`Circuit breaker re-opened from HALF_OPEN: ${operationName}`); } } getRecentRequests(operationName, windowMs) { const history = this.requestHistory.get(operationName) || []; const cutoff = Date.now() - windowMs; return history.filter(request => request.timestamp >= cutoff); } cleanupHistory(operationName, windowMs) { const history = this.requestHistory.get(operationName); if (!history) return; const cutoff = Date.now() - windowMs; const recentHistory = history.filter(request => request.timestamp >= cutoff); if (recentHistory.length > 1000) { recentHistory.splice(0, recentHistory.length - 1000); } this.requestHistory.set(operationName, recentHistory); } getFailureRate(operationName, windowMs = this.defaultConfig.monitoringWindow) { const recentRequests = this.getRecentRequests(operationName, windowMs); if (recentRequests.length === 0) return 0; const failures = recentRequests.filter(r => !r.success).length; return failures / recentRequests.length; } getSuccessRate(operationName, windowMs = this.defaultConfig.monitoringWindow) { return 1 - this.getFailureRate(operationName, windowMs); } }; exports.EnhancedCircuitBreakerService = EnhancedCircuitBreakerService; exports.EnhancedCircuitBreakerService = EnhancedCircuitBreakerService = EnhancedCircuitBreakerService_1 = __decorate([ (0, common_1.Injectable)() ], EnhancedCircuitBreakerService); //# sourceMappingURL=enhanced-circuit-breaker.service.js.map