UNPKG

@ahmedhegazee/nestjs-telescope

Version:

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

249 lines 8.41 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CircuitBreakerRegistry = exports.CircuitBreakerFactory = exports.CircuitBreakerOpenError = exports.CircuitBreaker = void 0; const common_1 = require("@nestjs/common"); class CircuitBreaker { constructor(options) { this.options = options; this.logger = new common_1.Logger(`CircuitBreaker:${this.options.name || 'default'}`); this.state = 'closed'; this.failures = 0; this.successes = 0; this.lastFailureTime = 0; this.lastSuccessTime = 0; this.nextAttempt = 0; this.logger.log(`Circuit breaker initialized: ${JSON.stringify(options)}`); } async execute(operation) { const currentTime = Date.now(); if (this.state === 'open') { if (currentTime < this.nextAttempt) { throw new CircuitBreakerOpenError(`Circuit breaker is open. Next attempt in ${this.nextAttempt - currentTime}ms`); } else { this.state = 'half-open'; this.options.onHalfOpen?.(this); this.logger.warn(`Circuit breaker transitioning to half-open state`); } } try { const result = await Promise.race([ operation(), new Promise((_, reject) => setTimeout(() => reject(new Error('Operation timeout')), this.options.timeout)) ]); this.onSuccess(); return result; } catch (error) { this.onFailure(error); throw error; } } onSuccess() { this.successes++; this.lastSuccessTime = Date.now(); if (this.state === 'half-open') { this.state = 'closed'; this.failures = 0; this.options.onClose?.(this); this.logger.log(`Circuit breaker closed after successful operation`); } else if (this.state === 'closed') { this.failures = Math.max(0, this.failures - 1); } } onFailure(error) { this.failures++; this.lastFailureTime = Date.now(); if (this.state === 'half-open') { this.state = 'open'; this.nextAttempt = Date.now() + this.options.resetTimeout; this.options.onOpen?.(this); this.logger.warn(`Circuit breaker opened from half-open state due to failure: ${error.message}`); } else if (this.failures >= this.options.failureThreshold) { this.state = 'open'; this.nextAttempt = Date.now() + this.options.resetTimeout; this.options.onOpen?.(this); this.logger.warn(`Circuit breaker opened due to ${this.failures} failures. Error: ${error.message}`); } } getState() { return { state: this.state, failures: this.failures, successes: this.successes, lastFailureTime: this.lastFailureTime, lastSuccessTime: this.lastSuccessTime, nextAttempt: this.nextAttempt }; } getFailureCount() { return this.failures; } getSuccessCount() { return this.successes; } getLastFailureTime() { return this.lastFailureTime; } getLastSuccessTime() { return this.lastSuccessTime; } getCurrentState() { return this.state; } isOpen() { return this.state === 'open'; } isClosed() { return this.state === 'closed'; } isHalfOpen() { return this.state === 'half-open'; } forceOpen() { this.state = 'open'; this.nextAttempt = Date.now() + this.options.resetTimeout; this.options.onOpen?.(this); this.logger.warn('Circuit breaker force opened'); } forceClose() { this.state = 'closed'; this.failures = 0; this.options.onClose?.(this); this.logger.log('Circuit breaker force closed'); } reset() { this.state = 'closed'; this.failures = 0; this.successes = 0; this.lastFailureTime = 0; this.lastSuccessTime = 0; this.nextAttempt = 0; this.logger.log('Circuit breaker reset'); } getHealthInfo() { const total = this.failures + this.successes; const failureRate = total > 0 ? (this.failures / total) * 100 : 0; const successRate = total > 0 ? (this.successes / total) * 100 : 0; const uptime = this.lastSuccessTime > 0 ? Date.now() - this.lastSuccessTime : 0; return { isHealthy: this.state === 'closed', state: this.state, failureRate, successRate, uptime }; } } exports.CircuitBreaker = CircuitBreaker; class CircuitBreakerOpenError extends Error { constructor(message) { super(message); this.name = 'CircuitBreakerOpenError'; } } exports.CircuitBreakerOpenError = CircuitBreakerOpenError; class CircuitBreakerFactory { static createForStorage(name = 'storage') { return new CircuitBreaker({ name, failureThreshold: 5, timeout: 30000, resetTimeout: 60000, onOpen: (breaker) => console.warn(`Storage circuit breaker opened: ${name}`), onHalfOpen: (breaker) => console.info(`Storage circuit breaker half-open: ${name}`), onClose: (breaker) => console.info(`Storage circuit breaker closed: ${name}`) }); } static createForDevTools(name = 'devtools') { return new CircuitBreaker({ name, failureThreshold: 3, timeout: 15000, resetTimeout: 30000, onOpen: (breaker) => console.warn(`DevTools circuit breaker opened: ${name}`), onHalfOpen: (breaker) => console.info(`DevTools circuit breaker half-open: ${name}`), onClose: (breaker) => console.info(`DevTools circuit breaker closed: ${name}`) }); } static createForNetwork(name = 'network') { return new CircuitBreaker({ name, failureThreshold: 10, timeout: 5000, resetTimeout: 15000, onOpen: (breaker) => console.warn(`Network circuit breaker opened: ${name}`), onHalfOpen: (breaker) => console.info(`Network circuit breaker half-open: ${name}`), onClose: (breaker) => console.info(`Network circuit breaker closed: ${name}`) }); } static createCustom(options) { return new CircuitBreaker(options); } } exports.CircuitBreakerFactory = CircuitBreakerFactory; class CircuitBreakerRegistry { constructor() { this.logger = new common_1.Logger('CircuitBreakerRegistry'); this.breakers = new Map(); } register(name, breaker) { this.breakers.set(name, breaker); this.logger.log(`Circuit breaker registered: ${name}`); } get(name) { return this.breakers.get(name); } getAll() { return new Map(this.breakers); } getAllStates() { const states = {}; for (const [name, breaker] of this.breakers) { states[name] = breaker.getState(); } return states; } getHealthStatus() { let healthy = 0; let unhealthy = 0; const details = {}; for (const [name, breaker] of this.breakers) { const health = breaker.getHealthInfo(); details[name] = health; if (health.isHealthy) { healthy++; } else { unhealthy++; } } return { healthy, unhealthy, total: this.breakers.size, details }; } resetAll() { for (const [name, breaker] of this.breakers) { breaker.reset(); this.logger.log(`Reset circuit breaker: ${name}`); } } remove(name) { const removed = this.breakers.delete(name); if (removed) { this.logger.log(`Circuit breaker removed: ${name}`); } return removed; } clear() { this.breakers.clear(); this.logger.log('All circuit breakers cleared'); } } exports.CircuitBreakerRegistry = CircuitBreakerRegistry; //# sourceMappingURL=circuit-breaker.js.map