#!/usr/bin/env node /** * Retry Strategies Example * * This example demonstrates various retry strategies available in the SDK, * including exponential backoff, linear retry, and Fibonacci sequences. * * Use cases: * - Handling transient network failures * - Managing rate limits gracefully * - Building resilient API integrations */ import { claude, createRetryExecutor, createExponentialRetryExecutor, createLinearRetryExecutor, createFibonacciRetryExecutor, withRetry } from '@instantlyeasy/claude-code-sdk-ts'; async function retryStrategiesExample() { console.log('šŸ”„ Retry Strategies Example\n'); // Example 1: Basic retry with exponential backoff console.log('1. Exponential Backoff Retry'); console.log('----------------------------\n'); const exponentialRetry = createExponentialRetryExecutor({ maxAttempts: 4, initialDelay: 1000, // Start with 1 second multiplier: 2, // Double the delay each time maxDelay: 10000, // Cap at 10 seconds jitter: true // Add randomization to prevent thundering herd }); try { let attemptCount = 0; const result = await exponentialRetry.execute(async () => { attemptCount++; console.log(`šŸ“ Attempt ${attemptCount}`); // Simulate failures for first 2 attempts if (attemptCount < 3) { throw new Error('Simulated transient error'); } return await claude() .withModel('sonnet') .query('Say "Success after retry!"') .asText(); }, { onRetry: (attempt, error, nextDelay) => { console.log(`ā³ Retry ${attempt} in ${nextDelay}ms after: ${error.message}`); } }); console.log('āœ… Result:', result); console.log('šŸ“Š Stats:', exponentialRetry.getStats()); } catch (error) { console.log('āŒ Failed after all retries:', error.message); } // Example 2: Linear retry for predictable delays console.log('\n\n2. Linear Retry Strategy'); console.log('------------------------\n'); const linearRetry = createLinearRetryExecutor({ maxAttempts: 3, delay: 2000, // Fixed 2 second delay between attempts jitter: false // No randomization }); try { const startTime = Date.now(); const result = await linearRetry.execute(async () => { const elapsed = Math.floor((Date.now() - startTime) / 1000); console.log(`šŸ“ Attempting at T+${elapsed}s`); // Succeed on second attempt if (linearRetry.getStats().totalAttempts > 1) { return 'Linear retry success!'; } throw new Error('Need one more try'); }); console.log('āœ… Result:', result); } catch (error) { console.log('āŒ Linear retry failed:', error.message); } // Example 3: Fibonacci retry for gradual backoff console.log('\n\n3. Fibonacci Retry Strategy'); console.log('---------------------------\n'); const fibonacciRetry = createFibonacciRetryExecutor({ maxAttempts: 5, initialDelay: 1000, // 1 second maxDelay: 8000 // Cap at 8 seconds }); console.log('Fibonacci sequence delays: 1s, 1s, 2s, 3s, 5s...\n'); try { let previousDelay = 0; await fibonacciRetry.execute(async () => { throw new Error('Always fail to show sequence'); }, { onRetry: (attempt, error, nextDelay) => { const sequence = attempt === 1 ? 1 : previousDelay; console.log(`šŸ”¢ Fibonacci attempt ${attempt}: waiting ${nextDelay}ms`); previousDelay = nextDelay; } }); } catch (error) { console.log('āœ… Fibonacci sequence demonstrated'); } // Example 4: Using withRetry helper function console.log('\n\n4. WithRetry Helper Function'); console.log('----------------------------\n'); try { // Simple retry wrapper with custom configuration const result = await withRetry( async () => { return await claude() .withModel('sonnet') .query('Write a haiku about persistence') .asText(); }, { maxAttempts: 3, strategy: 'exponential', initialDelay: 500, shouldRetry: (error, attempt) => { // Custom retry logic - only retry on specific errors const retryableErrors = ['network_error', 'timeout_error', 'rate_limit_error']; const errorType = error.type || 'unknown'; console.log(`šŸ¤” Checking if should retry: ${errorType}`); return retryableErrors.includes(errorType) && attempt < 3; } } ); console.log('āœ… Result with retry helper:'); console.log(result); } catch (error) { console.log('āŒ WithRetry failed:', error.message); } // Example 5: Advanced retry with circuit breaker pattern console.log('\n\n5. Circuit Breaker Pattern'); console.log('--------------------------\n'); class CircuitBreaker { constructor(retryExecutor, options = {}) { this.retryExecutor = retryExecutor; this.failureThreshold = options.failureThreshold || 3; this.resetTimeout = options.resetTimeout || 30000; // 30 seconds this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN this.failures = 0; this.lastFailureTime = null; } async execute(fn) { // Check if circuit should be reset if (this.state === 'OPEN' && Date.now() - this.lastFailureTime > this.resetTimeout) { console.log('šŸ”„ Circuit breaker: Moving to HALF_OPEN'); this.state = 'HALF_OPEN'; } // If circuit is open, fail fast if (this.state === 'OPEN') { throw new Error('Circuit breaker is OPEN - failing fast'); } try { const result = await this.retryExecutor.execute(fn); // Success - reset the circuit if (this.state === 'HALF_OPEN') { console.log('āœ… Circuit breaker: Success in HALF_OPEN, closing circuit'); this.state = 'CLOSED'; this.failures = 0; } return result; } catch (error) { this.failures++; this.lastFailureTime = Date.now(); if (this.failures >= this.failureThreshold) { console.log('🚫 Circuit breaker: Opening circuit after', this.failures, 'failures'); this.state = 'OPEN'; } throw error; } } getState() { return { state: this.state, failures: this.failures, lastFailureTime: this.lastFailureTime }; } } // Create circuit breaker with exponential retry const breaker = new CircuitBreaker( createExponentialRetryExecutor({ maxAttempts: 2, initialDelay: 1000 }), { failureThreshold: 2, resetTimeout: 5000 } ); // Simulate multiple failures to open the circuit for (let i = 0; i < 4; i++) { try { await breaker.execute(async () => { throw new Error('Service unavailable'); }); } catch (error) { console.log(`āŒ Request ${i + 1} failed:`, error.message); console.log(' Circuit state:', breaker.getState().state); } // Small delay between requests await new Promise(resolve => setTimeout(resolve, 500)); } console.log('\nā³ Waiting for circuit reset timeout...'); await new Promise(resolve => setTimeout(resolve, 5500)); // Try again after reset try { await breaker.execute(async () => { console.log('āœ… Service recovered!'); return 'Success after circuit reset'; }); } catch (error) { console.log('āŒ Still failing:', error.message); } // Example 6: Retry with telemetry console.log('\n\n6. Retry with Telemetry'); console.log('-----------------------\n'); const telemetryRetry = createRetryExecutor({ maxAttempts: 3, initialDelay: 1000 }); // Track retry metrics const metrics = { attempts: [], totalDuration: 0, finalStatus: null }; const startTime = Date.now(); try { await telemetryRetry.execute( async () => { const attemptStart = Date.now(); const attemptNumber = metrics.attempts.length + 1; try { // Simulate work await new Promise(resolve => setTimeout(resolve, 200)); if (attemptNumber < 2) { throw new Error('Simulated failure'); } return 'Success with telemetry'; } finally { metrics.attempts.push({ number: attemptNumber, duration: Date.now() - attemptStart, timestamp: new Date().toISOString() }); } }, { onRetry: (attempt, error, delay) => { console.log(`šŸ“ˆ Telemetry: Retry ${attempt}, delay ${delay}ms`); } } ); metrics.finalStatus = 'success'; } catch (error) { metrics.finalStatus = 'failure'; } metrics.totalDuration = Date.now() - startTime; console.log('\nšŸ“Š Retry Telemetry Report:'); console.log('- Total attempts:', metrics.attempts.length); console.log('- Total duration:', metrics.totalDuration, 'ms'); console.log('- Final status:', metrics.finalStatus); console.log('- Attempt details:'); metrics.attempts.forEach(attempt => { console.log(` - Attempt ${attempt.number}: ${attempt.duration}ms`); }); console.log('\n✨ Retry strategies examples completed!'); } // Error handling wrapper retryStrategiesExample().catch(error => { console.error('Fatal error:', error); process.exit(1); });