il2cpp-dump-analyzer-mcp
Version:
Agentic RAG system for analyzing IL2CPP dump.cs files from Unity games
259 lines • 9.33 kB
JavaScript
;
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