UNPKG

il2cpp-dump-analyzer-mcp

Version:

Agentic RAG system for analyzing IL2CPP dump.cs files from Unity games

259 lines 9.33 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CircuitBreaker = exports.RetryManager = void 0; exports.withRetry = withRetry; /** * Default retry configuration */ const DEFAULT_RETRY_CONFIG = { maxAttempts: 3, initialDelayMs: 1000, maxDelayMs: 10000, backoffMultiplier: 2, jitterFactor: 0.1, shouldRetry: (error, attempt) => { // Retry on network errors, timeouts, and specific database errors if (error?.code) { const retryableCodes = [ 'PGRST301', // Connection timeout 'PGRST302', // Connection error '08000', // Connection exception '08003', // Connection does not exist '08006', // Connection failure '53300', // Too many connections '57P01', // Admin shutdown '57P02', // Crash shutdown '57P03', // Cannot connect now ]; return retryableCodes.includes(error.code); } // Retry on network-related errors if (error?.message) { const retryableMessages = [ 'network error', 'timeout', 'connection refused', 'connection reset', 'temporary failure', 'service unavailable' ]; const message = error.message.toLowerCase(); return retryableMessages.some(msg => message.includes(msg)); } return false; }, onRetry: (error, attempt, delay) => { console.warn(`Retry attempt ${attempt} after ${delay}ms due to error:`, error?.message || error); } }; /** * Enhanced retry manager with exponential backoff and jitter */ class RetryManager { constructor(config = {}) { this.config = { ...DEFAULT_RETRY_CONFIG, ...config }; } /** * Execute a function with retry logic */ async execute(operation, operationName = 'database operation') { let lastError; for (let attempt = 1; attempt <= this.config.maxAttempts; attempt++) { try { const result = await operation(); // Log successful retry if this wasn't the first attempt if (attempt > 1) { console.log(`${operationName} succeeded on attempt ${attempt}`); } return result; } catch (error) { lastError = error; // Check if we should retry this error if (!this.config.shouldRetry(error, attempt)) { console.error(`${operationName} failed with non-retryable error:`, error); throw error; } // Don't retry if this was the last attempt if (attempt === this.config.maxAttempts) { console.error(`${operationName} failed after ${attempt} attempts:`, error); break; } // Calculate delay with exponential backoff and jitter const delay = this.calculateDelay(attempt); // Call retry callback this.config.onRetry(error, attempt, delay); // Wait before retrying await this.sleep(delay); } } throw lastError; } /** * Calculate delay with exponential backoff and jitter */ calculateDelay(attempt) { // Exponential backoff: initialDelay * (backoffMultiplier ^ (attempt - 1)) const exponentialDelay = this.config.initialDelayMs * Math.pow(this.config.backoffMultiplier, attempt - 1); // Apply maximum delay limit const cappedDelay = Math.min(exponentialDelay, this.config.maxDelayMs); // Add jitter to prevent thundering herd const jitter = cappedDelay * this.config.jitterFactor * Math.random(); return Math.floor(cappedDelay + jitter); } /** * Sleep for specified milliseconds */ sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } /** * Create a retry manager with database-specific configuration */ static forDatabase(customConfig = {}) { const databaseConfig = { maxAttempts: 3, initialDelayMs: 500, maxDelayMs: 5000, backoffMultiplier: 2, jitterFactor: 0.2, shouldRetry: (error, attempt) => { // Database-specific retry logic if (error?.code) { const retryableCodes = [ 'PGRST301', // Connection timeout 'PGRST302', // Connection error '23505', // Unique violation (for upsert operations) '40001', // Serialization failure '40P01', // Deadlock detected '53300', // Too many connections '57P03', // Cannot connect now ]; return retryableCodes.includes(error.code); } // Retry on specific error messages if (error?.message) { const message = error.message.toLowerCase(); const retryablePatterns = [ 'connection', 'timeout', 'network', 'temporary', 'unavailable', 'overloaded' ]; return retryablePatterns.some(pattern => message.includes(pattern)); } return false; }, onRetry: (error, attempt, delay) => { console.warn(`Database operation retry ${attempt}/${customConfig.maxAttempts || 3} ` + `after ${delay}ms. Error: ${error?.message || error}`); }, ...customConfig }; return new RetryManager(databaseConfig); } /** * Create a retry manager for vector operations */ static forVectorOperations(customConfig = {}) { const vectorConfig = { maxAttempts: 5, initialDelayMs: 1000, maxDelayMs: 8000, backoffMultiplier: 1.5, jitterFactor: 0.3, shouldRetry: (error, attempt) => { // Vector-specific retry logic if (error?.message) { const message = error.message.toLowerCase(); const retryablePatterns = [ 'embedding', 'vector', 'dimension', 'similarity', 'index', 'timeout', 'connection' ]; return retryablePatterns.some(pattern => message.includes(pattern)); } return false; }, onRetry: (error, attempt, delay) => { console.warn(`Vector operation retry ${attempt}/${customConfig.maxAttempts || 5} ` + `after ${delay}ms. Error: ${error?.message || error}`); }, ...customConfig }; return new RetryManager(vectorConfig); } } exports.RetryManager = RetryManager; /** * Utility function to wrap any async function with retry logic */ function withRetry(fn, config) { const retryManager = new RetryManager(config); return async (...args) => { return retryManager.execute(() => fn(...args), fn.name || 'anonymous function'); }; } /** * Circuit breaker pattern for database operations */ class CircuitBreaker { constructor(failureThreshold = 5, timeoutMs = 60000, monitoringPeriodMs = 10000) { this.failureThreshold = failureThreshold; this.timeoutMs = timeoutMs; this.monitoringPeriodMs = monitoringPeriodMs; this.failures = 0; this.lastFailureTime = 0; this.state = 'CLOSED'; } async execute(operation) { if (this.state === 'OPEN') { if (Date.now() - this.lastFailureTime > this.timeoutMs) { this.state = 'HALF_OPEN'; } else { throw new Error('Circuit breaker is OPEN'); } } try { const result = await operation(); this.onSuccess(); return result; } catch (error) { this.onFailure(); throw error; } } onSuccess() { this.failures = 0; this.state = 'CLOSED'; } onFailure() { this.failures++; this.lastFailureTime = Date.now(); if (this.failures >= this.failureThreshold) { this.state = 'OPEN'; console.warn(`Circuit breaker opened after ${this.failures} failures`); } } getState() { return this.state; } getStats() { return { failures: this.failures, state: this.state, lastFailureTime: this.lastFailureTime }; } } exports.CircuitBreaker = CircuitBreaker; //# sourceMappingURL=retry-manager.js.map