fix: Add periodic Drift reconnection to prevent memory leaks

- Memory leak identified: Drift SDK accumulates WebSocket subscriptions over time
- Root cause: accountUnsubscribe errors pile up when connections close/reconnect
- Symptom: Heap grows to 4GB+ after 10+ hours, eventual OOM crash
- Solution: Automatic reconnection every 4 hours to clear subscriptions

Changes:
- lib/drift/client.ts: Add reconnectTimer and scheduleReconnection()
- lib/drift/client.ts: Implement private reconnect() method
- lib/drift/client.ts: Clear timer in disconnect()
- app/api/drift/reconnect/route.ts: Manual reconnection endpoint (POST)
- app/api/drift/reconnect/route.ts: Reconnection status endpoint (GET)

Impact:
- Prevents JavaScript heap out of memory crashes
- Telegram bot timeouts resolved (was failing due to unresponsive bot)
- System will auto-heal every 4 hours instead of requiring manual restart
- Emergency manual reconnect available via API if needed

Tested: Container restarted successfully, no more WebSocket accumulation expected
This commit is contained in:
mindesbunister
2025-11-15 09:22:15 +01:00
parent 8862c300e6
commit fb4beee418
2 changed files with 148 additions and 0 deletions

View File

@@ -28,6 +28,8 @@ export class DriftService {
private driftClient: DriftClient | null = null
private user: User | null = null
private isInitialized: boolean = false
private reconnectTimer: NodeJS.Timeout | null = null
private reconnectIntervalMs: number = 4 * 60 * 60 * 1000 // 4 hours (prevent memory leak)
constructor(private config: DriftConfig) {
this.connection = new Connection(config.rpcUrl, 'confirmed')
@@ -163,12 +165,74 @@ export class DriftService {
this.isInitialized = true
console.log('✅ Drift service initialized successfully')
// Start periodic reconnection to prevent memory leaks
this.scheduleReconnection()
} catch (error) {
console.error('❌ Failed to initialize Drift service after retries:', error)
throw error
}
}
/**
* Schedule periodic reconnection to prevent WebSocket memory leaks
* Drift SDK accumulates subscriptions over time, causing memory leaks
* Periodic reconnection clears old subscriptions and resets memory
*/
private scheduleReconnection(): void {
// Clear existing timer if any
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer)
}
// Schedule reconnection every 4 hours
this.reconnectTimer = setTimeout(async () => {
try {
console.log('🔄 Scheduled reconnection: Clearing WebSocket subscriptions to prevent memory leak...')
await this.reconnect()
console.log('✅ Scheduled reconnection complete - memory freed')
} catch (error) {
console.error('❌ Scheduled reconnection failed:', error)
// Try to initialize fresh if reconnect fails
try {
this.isInitialized = false
await this.initialize()
} catch (reinitError) {
console.error('❌ Failed to reinitialize after reconnect failure:', reinitError)
}
}
}, this.reconnectIntervalMs)
console.log(`⏰ Scheduled reconnection in ${this.reconnectIntervalMs / 1000 / 60 / 60} hours`)
}
/**
* Reconnect to Drift Protocol (clears old subscriptions)
*/
private async reconnect(): Promise<void> {
console.log('🔄 Reconnecting to Drift Protocol...')
try {
// Unsubscribe from old connections
if (this.driftClient) {
await this.driftClient.unsubscribe()
console.log('✅ Unsubscribed from old Drift connection')
}
// Reset state
this.driftClient = null
this.user = null
this.isInitialized = false
// Reinitialize
await this.initialize()
} catch (error) {
console.error('❌ Reconnection failed:', error)
throw error
}
}
/**
* Get current USDC balance
*/
@@ -369,6 +433,13 @@ export class DriftService {
* Disconnect from Drift
*/
async disconnect(): Promise<void> {
// Clear reconnection timer
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer)
this.reconnectTimer = null
console.log('⏰ Cleared reconnection timer')
}
if (this.driftClient) {
await this.driftClient.unsubscribe()
console.log('✅ Drift client disconnected')