UNPKG

@ahmedhegazee/nestjs-telescope

Version:

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

250 lines 10.6 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 __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; var ResilientBridgeService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.ResilientBridgeService = void 0; const common_1 = require("@nestjs/common"); const stream_processing_bridge_service_1 = require("./stream-processing-bridge.service"); const circuit_breaker_1 = require("./circuit-breaker"); let ResilientBridgeService = ResilientBridgeService_1 = class ResilientBridgeService { constructor(streamBridge, telescopeConfig) { this.streamBridge = streamBridge; this.telescopeConfig = telescopeConfig; this.logger = new common_1.Logger(ResilientBridgeService_1.name); this.circuitBreakerRegistry = new circuit_breaker_1.CircuitBreakerRegistry(); this.config = { circuitBreakerEnabled: true, fallbackEnabled: true, maxRetries: 3, retryDelayMs: 1000, healthCheckIntervalMs: 30000 }; this.updateConfig(telescopeConfig); } async onModuleInit() { if (this.config.circuitBreakerEnabled) { this.setupCircuitBreakers(); } this.startHealthChecks(); this.logger.log('Resilient bridge service initialized'); } async onModuleDestroy() { if (this.healthCheckInterval) { clearInterval(this.healthCheckInterval); } this.circuitBreakerRegistry.clear(); this.logger.log('Resilient bridge service destroyed'); } updateConfig(config) { if (config.devtools?.bridge?.resilience) { const resilienceConfig = config.devtools.bridge.resilience; this.config.circuitBreakerEnabled = resilienceConfig.circuitBreakerEnabled ?? true; this.config.fallbackEnabled = resilienceConfig.fallbackEnabled ?? true; this.config.maxRetries = resilienceConfig.maxRetries ?? 3; this.config.retryDelayMs = resilienceConfig.retryDelayMs ?? 1000; this.config.healthCheckIntervalMs = resilienceConfig.healthCheckIntervalMs ?? 30000; } } setupCircuitBreakers() { const storageBreaker = circuit_breaker_1.CircuitBreakerFactory.createForStorage('storage-primary'); this.circuitBreakerRegistry.register('storage', storageBreaker); const devToolsBreaker = circuit_breaker_1.CircuitBreakerFactory.createForDevTools('devtools-processing'); this.circuitBreakerRegistry.register('devtools', devToolsBreaker); const networkBreaker = circuit_breaker_1.CircuitBreakerFactory.createForNetwork('network-external'); this.circuitBreakerRegistry.register('network', networkBreaker); const streamBreaker = circuit_breaker_1.CircuitBreakerFactory.createCustom({ name: 'stream-processing', failureThreshold: 5, timeout: 10000, resetTimeout: 30000, onOpen: (breaker) => { this.logger.warn('Stream processing circuit breaker opened'); this.handleStreamProcessingFailure(); }, onHalfOpen: (breaker) => this.logger.log('Stream processing circuit breaker half-open'), onClose: (breaker) => this.logger.log('Stream processing circuit breaker closed') }); this.circuitBreakerRegistry.register('stream', streamBreaker); this.logger.log('Circuit breakers initialized'); } handleStreamProcessingFailure() { if (this.config.fallbackEnabled) { this.logger.warn('Stream processing failed, activating fallback mode'); } } startHealthChecks() { if (this.healthCheckInterval) { clearInterval(this.healthCheckInterval); } this.healthCheckInterval = setInterval(() => { this.performHealthCheck(); }, this.config.healthCheckIntervalMs); this.performHealthCheck(); } performHealthCheck() { try { const streamMetrics = this.streamBridge.getStreamMetrics(); const circuitBreakerStatus = this.circuitBreakerRegistry.getHealthStatus(); const streamHealth = this.streamBridge.getHealthStatus(); const issues = []; if (circuitBreakerStatus.unhealthy > 0) { issues.push(`${circuitBreakerStatus.unhealthy} circuit breakers are unhealthy`); } if (!streamHealth.isHealthy) { issues.push(...streamHealth.issues); } if (streamMetrics.errorCount > 50) { issues.push(`High error count: ${streamMetrics.errorCount}`); } if (streamMetrics.averageProcessingTime > 10000) { issues.push(`High average processing time: ${streamMetrics.averageProcessingTime}ms`); } this.lastHealthStatus = { isHealthy: issues.length === 0, issues, circuitBreakers: circuitBreakerStatus.details, streamMetrics, lastHealthCheckAt: new Date() }; if (!this.lastHealthStatus.isHealthy) { this.logger.warn('Health check failed:', issues); } else { this.logger.debug('Health check passed'); } } catch (error) { this.logger.error('Health check failed:', error); } } async processDevToolsEntryWithResilience(entry, type) { const streamBreaker = this.circuitBreakerRegistry.get('stream'); if (!streamBreaker) { return this.processWithRetry(entry, type); } try { return await streamBreaker.execute(async () => { return this.processWithRetry(entry, type); }); } catch (error) { if (error instanceof circuit_breaker_1.CircuitBreakerOpenError) { this.logger.warn('Circuit breaker open, attempting fallback processing'); return this.fallbackProcessing(entry, type); } throw error; } } async processWithRetry(entry, type) { let lastError = null; for (let attempt = 1; attempt <= this.config.maxRetries; attempt++) { try { await this.streamBridge.processDevToolsEntry(entry, type); return; } catch (error) { lastError = error; this.logger.warn(`Processing attempt ${attempt} failed: ${error.message}`); if (attempt < this.config.maxRetries) { const delay = this.config.retryDelayMs * Math.pow(2, attempt - 1); await this.delay(delay); } } } throw lastError; } async fallbackProcessing(entry, type) { if (!this.config.fallbackEnabled) { throw new Error('Fallback processing disabled'); } try { this.logger.log(`Fallback processing for ${type} entry: ${entry.id || 'unknown'}`); this.logger.debug('Fallback entry:', { type, id: entry.id, timestamp: new Date() }); } catch (error) { this.logger.error('Fallback processing failed:', error); throw error; } } delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } getHealthStatus() { return this.lastHealthStatus || { isHealthy: false, issues: ['Health check not yet performed'], circuitBreakers: {}, streamMetrics: {}, lastHealthCheckAt: new Date() }; } getCircuitBreakerStatus() { return this.circuitBreakerRegistry.getAllStates(); } getStreamMetrics() { return this.streamBridge.getStreamMetrics(); } getStreamConfiguration() { return this.streamBridge.getStreamConfiguration(); } openCircuitBreaker(name) { const breaker = this.circuitBreakerRegistry.get(name); if (breaker) { breaker.forceOpen(); this.logger.warn(`Circuit breaker ${name} manually opened`); } } closeCircuitBreaker(name) { const breaker = this.circuitBreakerRegistry.get(name); if (breaker) { breaker.forceClose(); this.logger.log(`Circuit breaker ${name} manually closed`); } } resetCircuitBreaker(name) { const breaker = this.circuitBreakerRegistry.get(name); if (breaker) { breaker.reset(); this.logger.log(`Circuit breaker ${name} reset`); } } resetAllCircuitBreakers() { this.circuitBreakerRegistry.resetAll(); this.logger.log('All circuit breakers reset'); } async flushStreamBuffer() { await this.streamBridge.flushBuffer(); } updateStreamConfiguration(config) { this.streamBridge.updateStreamConfiguration(config); } getComprehensiveStatus() { return { bridge: this.getHealthStatus(), circuitBreakers: this.getCircuitBreakerStatus(), streamMetrics: this.getStreamMetrics(), configuration: { resilience: this.config, stream: this.getStreamConfiguration() } }; } }; exports.ResilientBridgeService = ResilientBridgeService; exports.ResilientBridgeService = ResilientBridgeService = ResilientBridgeService_1 = __decorate([ (0, common_1.Injectable)(), __param(1, (0, common_1.Inject)('TELESCOPE_CONFIG')), __metadata("design:paramtypes", [stream_processing_bridge_service_1.StreamProcessingBridgeService, Object]) ], ResilientBridgeService); //# sourceMappingURL=resilient-bridge.service.js.map