From b531b469990d9251dae49f4fdc73ebdc349cb40c Mon Sep 17 00:00:00 2001 From: Renato Alcara Date: Sun, 26 Apr 2026 22:46:30 -0300 Subject: [PATCH] refactor(utils): remove circuit-breaker integration from helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Utils/index.ts: re-export bounded-retry instead of circuit-breaker - Utils/unified-session.ts: drop the optional circuit-breaker wrapping. Telemetry is non-critical; a single send attempt with the underlying sendNode timeout is sufficient. Removed enableCircuitBreaker option, circuitBreaker field, and CircuitOpenError handling. - Utils/retry-utils.ts: drop the optional circuitBreaker option from withRetry. Retry mechanics are now self-contained; callers that need bounded retries should prefer withBoundedRetry directly. - Utils/health-status.ts: stop importing globalCircuitRegistry. Keep the CircuitBreakerHealth shape for k8s probe schema stability — always reports an empty list now. Note: src/Utils/structured-logger.ts has its own *private* CircuitBreaker class for log-throttling — independent of the socket-level circuit breaker being removed. Untouched. baileys-logger.ts logCircuitBreaker() is a generic logging helper that takes state strings as input — no import dependency. Untouched (no-op for callers that no longer have circuit breakers). --- src/Utils/health-status.ts | 45 +++++------------------------------- src/Utils/index.ts | 2 +- src/Utils/retry-utils.ts | 9 -------- src/Utils/unified-session.ts | 42 ++++----------------------------- 4 files changed, 11 insertions(+), 87 deletions(-) diff --git a/src/Utils/health-status.ts b/src/Utils/health-status.ts index 7486226d..ee6307b7 100644 --- a/src/Utils/health-status.ts +++ b/src/Utils/health-status.ts @@ -6,11 +6,12 @@ * @module Utils/health-status */ -import { globalCircuitRegistry } from './circuit-breaker.js' import { getVersionCacheStatus } from './version-cache.js' /** - * Circuit breaker health information + * Circuit breaker health information (kept for k8s probe schema stability — + * always reports an empty list now that circuit breakers were removed in + * favour of bounded-retry). */ export interface CircuitBreakerHealth { name: string @@ -105,44 +106,10 @@ export function getHealthStatus(): HealthStatus { }) } - // 2. Check circuit breakers + // 2. Circuit breakers were removed in favour of bounded-retry. + // Keep the field for backward-compat with k8s probe schema, always empty. const circuitBreakers: CircuitBreakerHealth[] = [] - let openCircuits = 0 - - for (const [name, breaker] of globalCircuitRegistry.getAll()) { - const stats = breaker.getStats() - const state = breaker.getState() - - circuitBreakers.push({ - name, - state, - failures: stats.totalFailures, - successes: stats.totalSuccesses, - totalCalls: stats.totalCalls - }) - - if (state === 'open') { - openCircuits++ - } - } - - if (openCircuits > 0) { - checks.push({ - name: 'circuit_breakers', - status: openCircuits > 2 ? 'fail' : 'warn', - message: `${openCircuits} circuit breaker(s) are open` - }) - if (openCircuits > 2) { - overallStatus = 'unhealthy' - } else if (overallStatus === 'healthy') { - overallStatus = 'degraded' - } - } else { - checks.push({ - name: 'circuit_breakers', - status: 'pass' - }) - } + checks.push({ name: 'circuit_breakers', status: 'pass' }) // 3. Check memory usage (warn if > 90%) const memUsage = process.memoryUsage() diff --git a/src/Utils/index.ts b/src/Utils/index.ts index 2c8e793b..74b4bc67 100644 --- a/src/Utils/index.ts +++ b/src/Utils/index.ts @@ -33,8 +33,8 @@ export * from './trace-context' export * from './prometheus-metrics' // Resilience and performance +export * from './bounded-retry' export * from './cache-utils' -export * from './circuit-breaker' export * from './retry-utils' // Telemetry and detection mitigation diff --git a/src/Utils/retry-utils.ts b/src/Utils/retry-utils.ts index b656bfac..9449f751 100644 --- a/src/Utils/retry-utils.ts +++ b/src/Utils/retry-utils.ts @@ -14,7 +14,6 @@ */ import { EventEmitter } from 'events' -import type { CircuitBreaker } from './circuit-breaker.js' import { metrics } from './prometheus-metrics.js' /** @@ -59,8 +58,6 @@ export interface RetryOptions { operationName?: string /** Collect metrics */ collectMetrics?: boolean - /** Circuit breaker for integration */ - circuitBreaker?: CircuitBreaker /** Callback before each retry */ onRetry?: (error: Error, attempt: number, delay: number) => void | Promise /** Callback on success */ @@ -283,7 +280,6 @@ export async function retry( timeout: options.timeout, operationName: options.operationName ?? 'operation', collectMetrics: options.collectMetrics ?? true, - circuitBreaker: options.circuitBreaker, onRetry: options.onRetry ?? (() => {}), onSuccess: options.onSuccess ?? (() => {}), onFailure: options.onFailure ?? (() => {}), @@ -313,11 +309,6 @@ export async function retry( throw new RetryAbortedError(attempt) } - // Check circuit breaker - if (config.circuitBreaker?.isOpen()) { - throw new Error(`Circuit breaker "${config.circuitBreaker.getName()}" is open`) - } - try { // Execute operation let result: T diff --git a/src/Utils/unified-session.ts b/src/Utils/unified-session.ts index cff005f7..4244cd69 100644 --- a/src/Utils/unified-session.ts +++ b/src/Utils/unified-session.ts @@ -15,7 +15,6 @@ */ import type { BinaryNode } from '../WABinary/types.js' -import { CircuitBreaker, CircuitOpenError, createConnectionCircuitBreaker } from './circuit-breaker.js' import type { ILogger } from './logger.js' import { metrics } from './prometheus-metrics.js' @@ -58,8 +57,6 @@ export interface UnifiedSessionOptions { enabled?: boolean /** Logger instance for debugging */ logger?: ILogger - /** Enable circuit breaker protection for send operations */ - enableCircuitBreaker?: boolean /** Function to send binary nodes to WhatsApp */ sendNode?: (node: BinaryNode) => Promise } @@ -121,7 +118,6 @@ export class UnifiedSessionManager { } private readonly options: Required - private circuitBreaker: CircuitBreaker | null = null /** Minimum interval between unified_session sends (1 minute) */ private static readonly MIN_SEND_INTERVAL_MS = TimeMs.Minute @@ -130,7 +126,6 @@ export class UnifiedSessionManager { this.options = { enabled: options.enabled ?? true, logger: options.logger ?? (console as unknown as ILogger), - enableCircuitBreaker: options.enableCircuitBreaker ?? true, sendNode: options.sendNode ?? (async () => { @@ -138,25 +133,6 @@ export class UnifiedSessionManager { }) } - // Initialize circuit breaker if enabled - if (this.options.enableCircuitBreaker) { - this.circuitBreaker = createConnectionCircuitBreaker({ - name: 'unified-session', - failureThreshold: 3, - failureWindow: 60000, - resetTimeout: 30000, - successThreshold: 1, - timeout: 10000, - onStateChange: (from, to) => { - this.options.logger.debug?.({ from, to }, 'Unified session circuit breaker state changed') - }, - onOpen: () => { - this.options.logger.warn?.('Unified session circuit breaker OPENED') - metrics.circuitBreakerTrips?.inc({ name: 'unified-session' }) - } - }) - } - this.state.isInitialized = true this.options.logger.debug?.('UnifiedSessionManager initialized') } @@ -292,20 +268,12 @@ export class UnifiedSessionManager { } try { - // Execute with circuit breaker if available - if (this.circuitBreaker) { - await this.circuitBreaker.execute(sendOperation) - } else { - await sendOperation() - } + // Telemetry is non-critical: a single send attempt with the + // underlying sendNode timeout is enough. No retry/circuit needed. + await sendOperation() } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error) - - if (error instanceof CircuitOpenError) { - this.options.logger.warn?.({ trigger, circuitState: error.state }, 'Unified session blocked by circuit breaker') - } else { - this.options.logger.warn?.({ trigger, error: errorMessage }, 'Failed to send unified session telemetry') - } + this.options.logger.warn?.({ trigger, error: errorMessage }, 'Failed to send unified session telemetry') // Record failure metric metrics.errors?.inc({ category: 'unified_session', code: 'send_failed' }) @@ -331,7 +299,6 @@ export class UnifiedSessionManager { sendCount: 0, isInitialized: true } - this.circuitBreaker?.reset() this.options.logger.debug?.('UnifiedSessionManager reset') } @@ -339,7 +306,6 @@ export class UnifiedSessionManager { * Destroy the manager and clean up resources. */ destroy(): void { - this.circuitBreaker?.destroy() this.state.isInitialized = false this.options.logger.debug?.('UnifiedSessionManager destroyed') }