src — networking
src — networking
The src/networking module provides robust utilities for building resilient and fault-tolerant network interactions within the Code Buddy application. It encapsulates common patterns like Circuit Breakers and Health Checks, along with re-exporting related utilities for retries and rate limiting.
This module is crucial for ensuring the stability and reliability of services that interact with external APIs or internal microservices, preventing cascading failures and providing insights into endpoint health.
1. Overview
The src/networking module aggregates several key functionalities:
- Circuit Breaker: Implements the Circuit Breaker pattern to prevent repeated requests to failing services, allowing them time to recover.
- Health Check: Monitors the health and latency of API endpoints, providing real-time status and supporting failover strategies.
- Related Utilities: Re-exports
retrymechanisms andRateLimiterfrom thesrc/utilsmodule, centralizing networking-related tools.
This documentation focuses on the CircuitBreaker and HealthCheckManager components, as they are defined within this module.
2. Circuit Breaker (src/networking/circuit-breaker.ts)
The CircuitBreaker class implements the Circuit Breaker pattern, a critical fault-tolerance mechanism. It prevents an application from repeatedly trying to execute an operation that is likely to fail, thereby saving resources and preventing cascading failures.
2.1 Core Concepts
The circuit breaker operates through three primary states:
- CLOSED: The normal state. Requests are allowed to pass through to the target service. If failures occur, they are counted. If the failure count exceeds a
failureThresholdwithin afailureWindow, the circuit transitions toOPEN. - OPEN: The circuit is tripped. Requests are immediately blocked and an error is thrown without attempting to call the service. After a
resetTimeoutperiod, the circuit transitions toHALF_OPEN. - HALF_OPEN: A probationary state. A limited number of requests are allowed to pass through to test if the service has recovered.
- If these test requests succeed (reaching
successThreshold), the circuit transitions back toCLOSED. - If any request fails, the circuit immediately transitions back to
OPEN.
2.2 CircuitBreaker Class
The CircuitBreaker class manages the state and logic for a single circuit.
2.2.1 Initialization and Options
A CircuitBreaker instance is created with CircuitBreakerOptions:
interface CircuitBreakerOptions {
failureThreshold?: number; class="hl-cmt">// Default: 5
resetTimeout?: number; class="hl-cmt">// Default: 30000ms (30 seconds)
successThreshold?: number; class="hl-cmt">// Default: 2
failureWindow?: number; class="hl-cmt">// Default: 60000ms (60 seconds)
isFailure?: (error: unknown) => boolean; class="hl-cmt">// Custom error check
onStateChange?: (from: CircuitState, to: CircuitState) => void; class="hl-cmt">// Callback
}
const breaker = new CircuitBreaker({
failureThreshold: 3,
resetTimeout: 15000,
isFailure: (err) => err instanceof NetworkError, class="hl-cmt">// Only trip on network errors
});
2.2.2 Executing Operations
The primary way to interact with the circuit breaker is through the execute method:
async execute<T>(fn: () => Promise<T>): Promise<T>
This method wraps an asynchronous function (fn) and applies the circuit breaker logic:
- It first calls
this.canExecute()to determine if the current state allows the request.
- If
OPENandresetTimeouthas passed, it transitions toHALF_OPENand allows the request. - If
OPENandresetTimeouthas not passed, it immediately throws anError('Circuit breaker is OPEN - request blocked').
- If allowed, it executes
fn(). - On successful completion of
fn(),this.onSuccess()is called, potentially transitioning the circuit fromHALF_OPENtoCLOSED. - On failure of
fn(),this.onFailure()is called (ifoptions.isFailurereturns true for the error), potentially transitioning the circuit fromCLOSEDorHALF_OPENtoOPEN.
2.2.3 State Management
The internal methods onSuccess(), onFailure(), canExecute(), and transitionTo() manage the circuit's state transitions and failure/success counts.
onSuccess(): IncrementsconsecutiveSuccesses. If inHALF_OPENandconsecutiveSuccessesmeetssuccessThreshold, it callstransitionTo('CLOSED').onFailure(): IncrementsfailedRequests, resetsconsecutiveSuccesses, and records the failure timestamp. It filters out failures older thanfailureWindow. If inHALF_OPENor iffailures.lengthmeetsfailureThresholdinCLOSEDstate, it callstransitionTo('OPEN').canExecute(): Determines if a request should proceed based on the currentstateandnextAttempttimestamp (forOPENstate).transitionTo(newState: CircuitState): Handles the actual state change, resetting relevant counters (failures,consecutiveSuccesses) and settingnextAttemptif transitioning toOPEN. It also emitsstateChangeevents and calls theonStateChangecallback if provided.
2.2.4 Utility Methods
getState(): CircuitState: Returns the current state of the circuit.getStats(): CircuitBreakerStats: Provides detailed statistics about the circuit's performance and state.reset(): void: Manually forces the circuit toCLOSEDstate, clearing failure counts.trip(): void: Manually forces the circuit toOPENstate.isHealthy(): boolean: Returnstrueif the circuit isCLOSED.getTimeUntilRetry(): number: Returns the remaining time in milliseconds until the circuit attempts recovery (only relevant inOPENstate).formatStats(): string: Provides a human-readable string representation of the circuit's statistics.dispose(): void: Cleans up event listeners.
2.2.5 Events
The CircuitBreaker extends EventEmitter and emits the following events:
success: On successful execution offn.failure: On failed execution offn(andisFailurereturns true).stateChange: When the circuit transitions between states, with{ from: CircuitState, to: CircuitState }.reset: Whenreset()is called.trip: Whentrip()is called.
2.2.6 Global Circuit Breaker Registry
The module also provides global functions for managing named circuit breakers:
getCircuitBreaker(name: string, options?: CircuitBreakerOptions): CircuitBreaker: Retrieves an existing circuit breaker by name or creates a new one if it doesn't exist. This is useful for managing multiple breakers across different services or endpoints.getAllCircuitBreakerStats(): Record: Returns statistics for all registered named circuit breakers.resetAllCircuitBreakers(): void: Resets all registered named circuit breakers toCLOSED.
2.2.7 Circuit Breaker State Diagram
stateDiagram
direction LR
[*] --> CLOSED
CLOSED --> OPEN : Failures > Threshold
OPEN --> HALF_OPEN : Reset Timeout
HALF_OPEN --> CLOSED : Successes > Threshold
HALF_OPEN --> OPEN : Any Failure
CLOSED --> CLOSED : Success
OPEN --> OPEN : Blocked
2.3 Usage Example
import { getCircuitBreaker } from 39;./networking/circuit-breaker.js39;;
const myServiceBreaker = getCircuitBreaker(39;my-external-service39;, {
failureThreshold: 5,
resetTimeout: 60000, class="hl-cmt">// 1 minute
successThreshold: 3,
});
myServiceBreaker.on(39;stateChange39;, ({ from, to }) => {
console.log(`Circuit for 39;my-external-service39; changed from ${from} to ${to}`);
});
async function callExternalService(): Promise<string> {
return myServiceBreaker.execute(async () => {
console.log(39;Attempting to call external service...39;);
class="hl-cmt">// Simulate an API call
const response = await fetch(39;https:class="hl-cmt">//api.example.com/data39;);
if (!response.ok) {
throw new Error(`Service responded with status: ${response.status}`);
}
const data = await response.text();
console.log(39;Service call successful.39;);
return data;
});
}
class="hl-cmt">// Example usage loop
setInterval(async () => {
try {
const result = await callExternalService();
class="hl-cmt">// console.log(39;Received:39;, result.substring(0, 20));
} catch (error: any) {
console.error(39;Service call failed or blocked:39;, error.message);
console.log(39;Current breaker state:39;, myServiceBreaker.getState());
if (myServiceBreaker.getState() === 39;OPEN39;) {
console.log(`Retry in: ${Math.ceil(myServiceBreaker.getTimeUntilRetry() / 1000)}s`);
}
}
console.log(myServiceBreaker.formatStats());
console.log(39;---39;);
}, 5000);
3. Health Check (src/networking/health-check.ts)
The HealthCheckManager provides a centralized way to monitor the health and performance of various API endpoints. It tracks status, latency, and failure counts, enabling applications to make informed decisions about routing requests or alerting operators.
3.1 Core Concepts
- Endpoint Monitoring: Continuously checks specified URLs.
- Health Status: Categorizes endpoint health as
healthy,degraded,unhealthy, orunknown. - Latency Tracking: Records the response time for each check.
- Failure Thresholds: Determines when an endpoint transitions from
degradedtounhealthy. - Custom Checks: Allows for custom logic beyond simple HTTP HEAD requests.
3.2 HealthCheckManager Class
The HealthCheckManager class is responsible for managing and performing health checks.
3.2.1 Initialization and Options
A HealthCheckManager instance is created with HealthCheckOptions:
interface HealthCheckOptions {
interval?: number; class="hl-cmt">// Default: 30000ms (30 seconds)
timeout?: number; class="hl-cmt">// Default: 5000ms (5 seconds)
failureThreshold?: number; class="hl-cmt">// Default: 3
degradedThreshold?: number; class="hl-cmt">// Default: 2000ms (2 seconds)
maxHistory?: number; class="hl-cmt">// Default: 100
checkFn?: (url: string) => Promise<boolean>; class="hl-cmt">// Custom check function
}
const manager = new HealthCheckManager({
interval: 10000, class="hl-cmt">// Check every 10 seconds
failureThreshold: 2,
checkFn: async (url) => {
class="hl-cmt">// Custom logic, e.g., check a specific API endpoint
const response = await fetch(`${url}/health`);
return response.status === 200;
},
});
3.2.2 Endpoint Management
addEndpoint(url: string): void: Registers an endpoint for monitoring. An initialcheckEndpointis performed immediately.removeEndpoint(url: string): void: Deregisters an endpoint and stops its continuous monitoring.startMonitoring(url: string): void: Begins continuous, interval-based health checks for a registered endpoint. If the endpoint isn't added, it callsaddEndpointfirst.stopMonitoring(url: string): void: Halts continuous monitoring for an endpoint.
3.2.3 Performing Checks
checkEndpoint(url: string): Promise: Executes a single health check for a given URL. This method updates the internalEndpointHealthrecord, calculates latency, and determines the newHealthStatus. It also emitscheck,unhealthy, andrecoveredevents.private performCheck(url: string): Promise: This private method performs the actual check. Ifoptions.checkFnis provided, it uses that. Otherwise, it defaults to an HTTPHEADrequest with a configurabletimeout.
3.2.4 Reporting and Querying
getHealth(url: string): EndpointHealth | undefined: Retrieves the detailed health status for a specific endpoint.getAllHealth(): EndpointHealth[]: Returns an array ofEndpointHealthobjects for all monitored endpoints.getHealthiestEndpoint(urls: string[]): string | null: Given a list of URLs, it returns the URL of the healthiest endpoint based on status and latency. This is useful for failover or load balancing.getSummary(): Provides a count of endpoints by their health status (total,healthy,degraded,unhealthy,unknown).formatHealth(): string: Generates a human-readable string summary of all endpoint health statuses.dispose(): void: Cleans up all active monitoring intervals and event listeners.
3.2.5 Events
The HealthCheckManager extends EventEmitter and emits the following events:
check: After every health check, with{ url: string, result: HealthCheckResult }.unhealthy: When an endpoint transitions tounhealthy, with{ url: string, result: HealthCheckResult }.recovered: When an endpoint recovers from a failure state tohealthy, with{ url: string, result: HealthCheckResult }.
3.2.6 Global Health Check Manager
The module provides global functions for managing a singleton HealthCheckManager instance:
getHealthCheckManager(options?: HealthCheckOptions): HealthCheckManager: Retrieves the singleton instance ofHealthCheckManager, creating it if it doesn't exist.resetHealthCheckManager(): void: Disposes of the current singleton instance and sets it tonull, allowing a new one to be created.
3.2.7 Health Status State Diagram
stateDiagram
direction LR
[*] --> unknown
unknown --> healthy : Check Success
unknown --> degraded : Check Failure (below threshold)
unknown --> unhealthy : Check Failure (above threshold)
healthy --> degraded : High Latency
healthy --> unhealthy : Failures > Threshold
degraded --> healthy : Low Latency, Success
degraded --> unhealthy : Failures > Threshold
unhealthy --> healthy : Recovered
3.3 Usage Example
import { getHealthCheckManager } from 39;./networking/health-check.js39;;
const healthManager = getHealthCheckManager({
interval: 5000, class="hl-cmt">// Check every 5 seconds
degradedThreshold: 1000, class="hl-cmt">// Degraded if latency > 1s
failureThreshold: 2, class="hl-cmt">// Unhealthy after 2 consecutive failures
});
healthManager.addEndpoint(39;https:class="hl-cmt">//jsonplaceholder.typicode.com/posts/139;);
healthManager.addEndpoint(39;https:class="hl-cmt">//non-existent-service.com/health39;); // Will fail
healthManager.addEndpoint(39;https:class="hl-cmt">//httpbin.org/delay/139;); // Will be degraded due to latency
healthManager.on(39;check39;, ({ url, result }) => {
class="hl-cmt">// console.log(`Checked ${url}: ${result.status} (${result.latency}ms)`);
});
healthManager.on(39;unhealthy39;, ({ url, result }) => {
console.warn(`🚨 Endpoint UNHEALTHY: ${url} - ${result.error}`);
});
healthManager.on(39;recovered39;, ({ url }) => {
console.info(`✅ Endpoint RECOVERED: ${url}`);
});
class="hl-cmt">// Start continuous monitoring
healthManager.startMonitoring(39;https:class="hl-cmt">//jsonplaceholder.typicode.com/posts/139;);
healthManager.startMonitoring(39;https:class="hl-cmt">//non-existent-service.com/health39;);
healthManager.startMonitoring(39;https:class="hl-cmt">//httpbin.org/delay/139;);
class="hl-cmt">// Periodically log summary
setInterval(() => {
console.log(39;\n--- Health Check Report ---39;);
console.log(healthManager.formatHealth());
const healthiest = healthManager.getHealthiestEndpoint([
39;https:class="hl-cmt">//jsonplaceholder.typicode.com/posts/139;,
39;https:class="hl-cmt">//non-existent-service.com/health39;,
39;https:class="hl-cmt">//httpbin.org/delay/139;
]);
console.log(`Healthiest endpoint: ${healthiest || 39;None39;}`);
console.log(39;---------------------------\n39;);
}, 15000);
class="hl-cmt">// Clean up after a while
setTimeout(() => {
console.log(39;Disposing health check manager...39;);
healthManager.dispose();
}, 60000);
4. Related Utilities
The src/networking/index.ts file re-exports several related utilities from the src/utils module, centralizing access to common networking patterns:
- Retry Mechanisms:
retry,retryWithResult,withRetry: Functions for retrying failed operations.Retry: A class for more configurable retry logic.RetryPredicates,RetryStrategies: Enums/interfaces for defining retry conditions and backoff strategies.
These are useful for handling transient network errors or temporary service unavailability.
- Rate Limiting:
RateLimiter: A class for controlling the rate of function execution.getRateLimiter: A global function to get or create named rate limiters.rateLimited: A decorator/higher-order function to apply rate limiting to functions.
These prevent overwhelming services with too many requests in a short period.
Developers should refer to the documentation for src/utils/retry.ts and src/utils/rate-limiter.ts for detailed usage of these re-exported utilities.
5. Conclusion
The src/networking module provides a robust foundation for building resilient and observable network interactions. By leveraging CircuitBreaker for fault tolerance and HealthCheckManager for proactive monitoring, developers can create more stable and reliable applications that gracefully handle external service dependencies. The re-export of retry and rate-limiting utilities further enhances this capability, offering a comprehensive toolkit for network-aware development.