@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
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 __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