UNPKG

@takentrade/takentrade-libs

Version:
121 lines (120 loc) 4.88 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 CircuitBreakerService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.CircuitBreakerService = void 0; const common_1 = require("@nestjs/common"); const circuit_breaker_types_1 = require("./circuit-breaker.types"); let CircuitBreakerService = CircuitBreakerService_1 = class CircuitBreakerService { logger = new common_1.Logger(CircuitBreakerService_1.name); breakers = new Map(); createBreaker(name, options = {}) { const defaultOptions = { failureThreshold: 5, resetTimeout: 60000, monitorTimeout: 30000, healthCheckInterval: 5000, }; const finalOptions = { ...defaultOptions, ...options }; const breaker = { name, state: circuit_breaker_types_1.CircuitState.CLOSED, failures: 0, lastFailureTime: null, lastSuccessTime: null, options: finalOptions, }; this.breakers.set(name, breaker); this.startHealthCheck(name); return breaker; } startHealthCheck(name) { const interval = setInterval(() => { const breaker = this.breakers.get(name); if (!breaker) { clearInterval(interval); return; } if (breaker.state === circuit_breaker_types_1.CircuitState.OPEN) { const now = Date.now(); const resetTime = breaker.lastFailureTime ? breaker.lastFailureTime + breaker.options.resetTimeout : 0; if (now >= resetTime) { this.setState(name, circuit_breaker_types_1.CircuitState.HALF_OPEN); this.logger.log(`Circuit ${name} moved to HALF_OPEN state for testing`); } } }, this.breakers.get(name)?.options.healthCheckInterval); } async execute(name, operation) { const breaker = this.breakers.get(name); if (!breaker) { throw new Error(`Circuit breaker ${name} not found`); } if (breaker.state === circuit_breaker_types_1.CircuitState.OPEN) { throw new Error(`Circuit ${name} is OPEN`); } try { const result = await operation(); this.onSuccess(name); return result; } catch (error) { this.onFailure(name); throw error; } } onSuccess(name) { const breaker = this.breakers.get(name); if (!breaker) return; breaker.failures = 0; breaker.lastSuccessTime = Date.now(); if (breaker.state === circuit_breaker_types_1.CircuitState.HALF_OPEN) { this.setState(name, circuit_breaker_types_1.CircuitState.CLOSED); this.logger.log(`Circuit ${name} closed after successful test`); } } onFailure(name) { const breaker = this.breakers.get(name); if (!breaker) return; breaker.failures++; breaker.lastFailureTime = Date.now(); if (breaker.state === circuit_breaker_types_1.CircuitState.CLOSED && breaker.failures >= breaker.options.failureThreshold) { this.setState(name, circuit_breaker_types_1.CircuitState.OPEN); this.logger.warn(`Circuit ${name} opened after ${breaker.failures} failures`); } } setState(name, state) { const breaker = this.breakers.get(name); if (breaker) { breaker.state = state; this.breakers.set(name, breaker); } } getState(name) { return this.breakers.get(name)?.state || circuit_breaker_types_1.CircuitState.CLOSED; } reset(name) { const breaker = this.breakers.get(name); if (breaker) { breaker.failures = 0; breaker.state = circuit_breaker_types_1.CircuitState.CLOSED; breaker.lastFailureTime = null; breaker.lastSuccessTime = null; this.breakers.set(name, breaker); } } }; exports.CircuitBreakerService = CircuitBreakerService; exports.CircuitBreakerService = CircuitBreakerService = CircuitBreakerService_1 = __decorate([ (0, common_1.Injectable)() ], CircuitBreakerService);