qapinterface
Version:
Comprehensive API utilities for Node.js applications including authentication, security, request processing, and response handling with zero external dependencies
78 lines (74 loc) • 3.27 kB
JavaScript
/**
* Service Connection Retry Handler with Exponential Backoff
*
* SINGLE RESPONSIBILITY: Handle service connection retries with resilience patterns ONLY
*
* RESILIENCE STRATEGY:
* This module implements a robust retry mechanism essential for microservice architectures
* where external dependencies may be temporarily unavailable. The retry logic includes:
* - Exponential backoff to reduce load on struggling services
* - Configurable retry limits to prevent infinite loops
* - Proper error propagation for circuit breaker patterns
* - Resource cleanup to prevent connection leaks
*
* WHY EXPONENTIAL BACKOFF:
* Linear delays can create thundering herd problems where all clients retry simultaneously.
* Exponential backoff spreads retry attempts over time, reducing load spikes and improving
* the chances of successful reconnection as services recover.
*
* DESIGN DECISIONS:
* - Function-based approach allows any connection logic (DB, HTTP, etc.)
* - Promise-based for async/await compatibility
* - Configuration object pattern for extensibility
* - Fail-fast on final attempt to enable circuit breaker logic
*/
/**
* Attempts service connection with configurable retry logic and exponential backoff.
*
* RETRY ALGORITHM:
* 1. Execute connection function
* 2. On failure, wait (attempt_number * 1000)ms before retry
* 3. Repeat up to maxRetries times
* 4. Throw final error if all attempts fail
*
* BACKOFF CALCULATION:
* - Attempt 1: 1 second delay
* - Attempt 2: 2 second delay
* - Attempt 3: 3 second delay
* - Linear progression prevents excessive delays while spreading load
*
* @param {Function} connectionFn - Async function that returns Promise for connection attempt
* @param {object} [retryOptions={}] - Configuration for retry behavior
* @param {number} [retryOptions.retries=3] - Maximum number of retry attempts
* @returns {Promise<any>} Promise resolving to connectionFn result or rejecting with final error
* @throws {Error} The final error after all retry attempts are exhausted
*/
async function connectWithRetry(connectionFn, retryOptions = {}) {
// Configuration: Extract retry count with sensible default
// 3 retries balances resilience with reasonable timeout duration
const maxRetries = retryOptions.retries || 3;
// Retry loop: Attempt connection up to maxRetries times
for (let i = 0; i < maxRetries; i++) {
try {
// Connection attempt: Execute provided async function
// Return immediately on success, preserving the connection result
return await connectionFn();
} catch (error) {
// Final attempt handling: Re-throw error if no retries remain
// This preserves the original error context for upstream error handling
if (i === maxRetries - 1) {
throw error;
}
// Exponential backoff delay: Wait longer between each retry
// Calculation: (attempt_number + 1) * 1000ms
// - Attempt 0: 1000ms (1 second)
// - Attempt 1: 2000ms (2 seconds)
// - Attempt 2: 3000ms (3 seconds)
// This prevents thundering herd and gives services time to recover
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
module.exports = {
connectWithRetry
};