From 5e826dee5d489dd8fd3e640e602730da39c018ef Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Thu, 13 Nov 2025 16:05:42 +0100 Subject: [PATCH] Add DNS retry logic to Drift initialization - Handles transient network failures (EAI_AGAIN, ENOTFOUND, ETIMEDOUT) - Automatically retries up to 3 times with 2s delay between attempts - Logs retry attempts for monitoring - Prevents 500 errors from temporary DNS hiccups - Fixes: n8n workflow failures during brief network issues Impact: - Improves reliability during DNS/network instability - Reduces false negatives (missed trades due to transient errors) - User-friendly retry logs for diagnostics --- lib/drift/client.ts | 85 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 19 deletions(-) diff --git a/lib/drift/client.ts b/lib/drift/client.ts index aa5061d..9424654 100644 --- a/lib/drift/client.ts +++ b/lib/drift/client.ts @@ -75,8 +75,52 @@ export class DriftService { console.log('✅ Drift service created for wallet:', this.wallet.publicKey.toString()) } + /** + * Retry helper for handling transient network failures (DNS, timeouts) + */ + private async retryOperation( + operation: () => Promise, + maxRetries: number = 3, + delayMs: number = 2000, + operationName: string = 'operation' + ): Promise { + let lastError: Error | null = null + + for (let attempt = 1; attempt <= maxRetries; attempt++) { + try { + return await operation() + } catch (error: any) { + lastError = error + + // Check if it's a transient network error + const isTransient = + error?.message?.includes('fetch failed') || + error?.message?.includes('EAI_AGAIN') || + error?.message?.includes('ENOTFOUND') || + error?.message?.includes('ETIMEDOUT') || + error?.message?.includes('ECONNREFUSED') || + error?.code === 'EAI_AGAIN' || + error?.cause?.code === 'EAI_AGAIN' + + if (!isTransient || attempt === maxRetries) { + // Non-transient error or max retries reached - fail immediately + throw error + } + + console.log(`⚠️ ${operationName} failed (attempt ${attempt}/${maxRetries}): ${error?.message || error}`) + console.log(`⏳ Retrying in ${delayMs}ms...`) + + // Wait before retry + await new Promise(resolve => setTimeout(resolve, delayMs)) + } + } + + throw lastError || new Error(`${operationName} failed after ${maxRetries} retries`) + } + /** * Initialize Drift client and subscribe to account updates + * Includes automatic retry for transient network failures (DNS, timeouts) */ async initialize(): Promise { if (this.isInitialized) { @@ -87,33 +131,36 @@ export class DriftService { try { console.log('🚀 Initializing Drift Protocol client...') - // Initialize Drift SDK (gets program IDs and config) - const sdkConfig = initialize({ - env: this.config.env === 'devnet' ? 'devnet' : 'mainnet-beta' - }) + // Wrap initialization in retry logic to handle DNS failures + await this.retryOperation(async () => { + // Initialize Drift SDK (gets program IDs and config) + const sdkConfig = initialize({ + env: this.config.env === 'devnet' ? 'devnet' : 'mainnet-beta' + }) - // Create Drift client with manual wallet and SDK config - this.driftClient = new DriftClient({ - connection: this.connection, - wallet: this.wallet as any, // Type assertion for compatibility - programID: new PublicKey(sdkConfig.DRIFT_PROGRAM_ID), - opts: { - commitment: 'confirmed', - }, - }) + // Create Drift client with manual wallet and SDK config + this.driftClient = new DriftClient({ + connection: this.connection, + wallet: this.wallet as any, // Type assertion for compatibility + programID: new PublicKey(sdkConfig.DRIFT_PROGRAM_ID), + opts: { + commitment: 'confirmed', + }, + }) - // Subscribe to Drift account updates - await this.driftClient.subscribe() - console.log('✅ Drift client subscribed to account updates') + // Subscribe to Drift account updates (this makes RPC calls) + await this.driftClient.subscribe() + console.log('✅ Drift client subscribed to account updates') - // Get user account - this.user = this.driftClient.getUser() + // Get user account + this.user = this.driftClient.getUser() + }, 3, 2000, 'Drift initialization') this.isInitialized = true console.log('✅ Drift service initialized successfully') } catch (error) { - console.error('❌ Failed to initialize Drift service:', error) + console.error('❌ Failed to initialize Drift service after retries:', error) throw error } }