UNPKG

behemoth-cli

Version:

🌍 BEHEMOTH CLIv3.760.4 - Level 50+ POST-SINGULARITY Intelligence Trading AI

308 lines (263 loc) 9.01 kB
/** * Memory Usage Monitor for Large File Operations * Provides monitoring and warnings for memory-intensive operations */ import * as fs from 'fs'; import * as util from 'util'; import { logWarn, logError, logDebug } from './error-handler.js'; const readFile = util.promisify(fs.readFile); const writeFile = util.promisify(fs.writeFile); const stat = util.promisify(fs.stat); export interface MemoryStats { used: number; total: number; external: number; heapUsed: number; heapTotal: number; percentage: number; } export interface FileOperationOptions { maxFileSize?: number; // Maximum file size in bytes (default: 50MB) memoryThreshold?: number; // Memory usage threshold percentage (default: 80%) chunkSize?: number; // Chunk size for streaming large files (default: 1MB) } export class MemoryMonitor { private static readonly DEFAULT_MAX_FILE_SIZE = 50 * 1024 * 1024; // 50MB private static readonly DEFAULT_MEMORY_THRESHOLD = 80; // 80% private static readonly DEFAULT_CHUNK_SIZE = 1024 * 1024; // 1MB /** * Get current memory usage statistics */ static getMemoryStats(): MemoryStats { const memUsage = process.memoryUsage(); const totalMemory = require('os').totalmem(); return { used: memUsage.rss, total: totalMemory, external: memUsage.external, heapUsed: memUsage.heapUsed, heapTotal: memUsage.heapTotal, percentage: (memUsage.rss / totalMemory) * 100 }; } /** * Check if memory usage is above threshold */ static isMemoryUsageHigh(threshold = MemoryMonitor.DEFAULT_MEMORY_THRESHOLD): boolean { const stats = this.getMemoryStats(); return stats.percentage > threshold; } /** * Log memory warning if usage is high */ private static checkMemoryUsage(operation: string): void { if (this.isMemoryUsageHigh()) { const stats = this.getMemoryStats(); logWarn(`High memory usage during ${operation}`, undefined, { component: 'MemoryMonitor', operation, metadata: { memoryUsagePercentage: stats.percentage.toFixed(2), heapUsedMB: (stats.heapUsed / 1024 / 1024).toFixed(2), totalMemoryMB: (stats.total / 1024 / 1024).toFixed(2) } }); } } /** * Safe file read with memory monitoring */ static async readFileWithMonitoring( filePath: string, options: FileOperationOptions & { encoding?: BufferEncoding } = {} ): Promise<string | Buffer> { const { maxFileSize = this.DEFAULT_MAX_FILE_SIZE, memoryThreshold = this.DEFAULT_MEMORY_THRESHOLD, encoding = 'utf8' } = options; try { // Check file size before reading const fileStats = await stat(filePath); if (fileStats.size > maxFileSize) { throw new Error(`File size (${fileStats.size} bytes) exceeds maximum allowed size (${maxFileSize} bytes)`); } // Check memory before operation if (this.isMemoryUsageHigh(memoryThreshold)) { logWarn('Reading file with high memory usage', undefined, { component: 'MemoryMonitor', operation: 'readFile', metadata: { filePath, fileSize: fileStats.size } }); } const data = await readFile(filePath, encoding); // Check memory after operation this.checkMemoryUsage('file read'); logDebug(`File read completed`, undefined, { component: 'MemoryMonitor', operation: 'readFile', metadata: { filePath, fileSize: fileStats.size } }); return data; } catch (error) { logError('File read operation failed', error, { component: 'MemoryMonitor', operation: 'readFile', metadata: { filePath } }); throw error; } } /** * Safe file write with memory monitoring */ static async writeFileWithMonitoring( filePath: string, data: string | Buffer, options: FileOperationOptions & fs.WriteFileOptions = {} ): Promise<void> { const { memoryThreshold = this.DEFAULT_MEMORY_THRESHOLD } = options; const encoding = (options as any).encoding || 'utf8'; try { const dataSize = Buffer.isBuffer(data) ? data.length : Buffer.byteLength(data, encoding as BufferEncoding); // Check memory before operation if (this.isMemoryUsageHigh(memoryThreshold)) { logWarn('Writing file with high memory usage', undefined, { component: 'MemoryMonitor', operation: 'writeFile', metadata: { filePath, dataSize } }); } await writeFile(filePath, data, options); // Check memory after operation this.checkMemoryUsage('file write'); logDebug(`File write completed`, undefined, { component: 'MemoryMonitor', operation: 'writeFile', metadata: { filePath, dataSize } }); } catch (error) { logError('File write operation failed', error, { component: 'MemoryMonitor', operation: 'writeFile', metadata: { filePath } }); throw error; } } /** * Synchronous file read with memory monitoring (use sparingly) */ static readFileSyncWithMonitoring( filePath: string, options: FileOperationOptions & { encoding?: BufferEncoding } = {} ): string | Buffer { const { maxFileSize = this.DEFAULT_MAX_FILE_SIZE, memoryThreshold = this.DEFAULT_MEMORY_THRESHOLD, encoding = 'utf8' } = options; try { // Check file size before reading const fileStats = fs.statSync(filePath); if (fileStats.size > maxFileSize) { throw new Error(`File size (${fileStats.size} bytes) exceeds maximum allowed size (${maxFileSize} bytes)`); } // Check memory before operation if (this.isMemoryUsageHigh(memoryThreshold)) { logWarn('Reading file synchronously with high memory usage', undefined, { component: 'MemoryMonitor', operation: 'readFileSync', metadata: { filePath, fileSize: fileStats.size } }); } const data = fs.readFileSync(filePath, encoding); // Check memory after operation this.checkMemoryUsage('sync file read'); return data; } catch (error) { logError('Synchronous file read operation failed', error, { component: 'MemoryMonitor', operation: 'readFileSync', metadata: { filePath } }); throw error; } } /** * Force garbage collection if available and memory usage is high */ static forceGarbageCollection(): boolean { if (global.gc && this.isMemoryUsageHigh(70)) { const beforeStats = this.getMemoryStats(); global.gc(); const afterStats = this.getMemoryStats(); logDebug('Forced garbage collection', undefined, { component: 'MemoryMonitor', operation: 'forceGC', metadata: { beforeMemoryMB: (beforeStats.heapUsed / 1024 / 1024).toFixed(2), afterMemoryMB: (afterStats.heapUsed / 1024 / 1024).toFixed(2), freedMB: ((beforeStats.heapUsed - afterStats.heapUsed) / 1024 / 1024).toFixed(2) } }); return true; } return false; } /** * Stream large file processing for files that exceed memory limits */ static async processLargeFile( filePath: string, processor: (chunk: string | Buffer) => void | Promise<void>, options: FileOperationOptions = {} ): Promise<void> { const { chunkSize = this.DEFAULT_CHUNK_SIZE, memoryThreshold = this.DEFAULT_MEMORY_THRESHOLD } = options; return new Promise((resolve, reject) => { const stream = fs.createReadStream(filePath, { encoding: 'utf8', highWaterMark: chunkSize }); stream.on('data', async (chunk: string | Buffer) => { try { // Check memory before processing chunk if (this.isMemoryUsageHigh(memoryThreshold)) { logWarn('Processing file chunk with high memory usage', undefined, { component: 'MemoryMonitor', operation: 'processLargeFile', metadata: { filePath, chunkSize: chunk.length } }); // Attempt garbage collection this.forceGarbageCollection(); } await processor(chunk); } catch (error) { stream.destroy(); reject(error); } }); stream.on('end', () => { logDebug('Large file processing completed', undefined, { component: 'MemoryMonitor', operation: 'processLargeFile', metadata: { filePath } }); resolve(); }); stream.on('error', (error) => { logError('Large file processing failed', error, { component: 'MemoryMonitor', operation: 'processLargeFile', metadata: { filePath } }); reject(error); }); }); } }