chronik-cache
Version:
A cache helper for chronik-client
87 lines (76 loc) • 3.25 kB
text/typescript
// Copyright (c) 2024 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
import Logger from './Logger';
interface FailoverOptions {
maxRetries?: number;
retryDelay?: number;
exponentialBackoff?: boolean;
enableLogging?: boolean;
}
export default class FailoverHandler {
private maxRetries: number;
private retryDelay: number;
private exponentialBackoff: boolean;
private enableLogging: boolean;
private logger: Logger;
constructor(options: FailoverOptions = {}) {
this.maxRetries = options.maxRetries || 3;
this.retryDelay = options.retryDelay || 1500; // milliseconds
this.exponentialBackoff = options.exponentialBackoff ?? true;
this.enableLogging = options.enableLogging ?? false;
this.logger = new Logger(this.enableLogging);
}
async executeWithRetry<T>(operation: () => Promise<T>, context: string = ''): Promise<T> {
let lastError: Error;
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error as Error;
const delay = this.exponentialBackoff
? this.retryDelay * Math.pow(2, attempt - 1)
: this.retryDelay;
this.logger.error(`[Failover] ${context} - Attempt ${attempt}/${this.maxRetries} failed:`, lastError.message);
if (attempt < this.maxRetries) {
this.logger.log(`[Failover] Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
this.logger.error(`[Failover] ${context} - All retry attempts failed`);
throw lastError!;
}
/**
* Special handling for WebSocket related operations
*/
async handleWebSocketOperation<T>(operation: () => Promise<T>, address: string, context: string = ''): Promise<T> {
try {
return await this.executeWithRetry(operation, context);
} catch (error) {
const err = error as any;
this.logger.error(`[Failover] WebSocket operation failed for address ${address}:`, err.message);
// WebSocket specific error handling logic
if (err.code === 'ECONNREFUSED' || err.code === 'ECONNRESET') {
this.logger.log(`[Failover] WebSocket connection issue detected for ${address}`);
}
throw error;
}
}
/**
* Special handling for database operations
*/
async handleDbOperation<T>(operation: () => Promise<T>, context: string = ''): Promise<T | null> {
try {
return await this.executeWithRetry(operation, context);
} catch (error) {
const err = error as any;
this.logger.error(`[Failover] Database operation failed:`, err.message);
// Database specific error handling logic
if (err.type === 'NotFoundError') {
return null;
}
throw error;
}
}
}