refactor: reorganize imports and update file paths for consistency
This commit is contained in:
50
src/core/memory/memoryMonitor.ts
Normal file
50
src/core/memory/memoryMonitor.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
// Monitor ligero de memoria y event loop.
|
||||
// Se activa si defines MEMORY_LOG_INTERVAL_SECONDS.
|
||||
import { monitorEventLoopDelay } from 'node:perf_hooks';
|
||||
|
||||
export interface MemoryMonitorOptions {
|
||||
intervalSeconds: number;
|
||||
warnHeapRatio?: number; // 0-1 fracción del máximo estimado antes de warning
|
||||
}
|
||||
|
||||
export function startMemoryMonitor(opts: MemoryMonitorOptions) {
|
||||
const { intervalSeconds, warnHeapRatio = 0.8 } = opts;
|
||||
if (intervalSeconds <= 0) return;
|
||||
|
||||
const h = monitorEventLoopDelay({ resolution: 20 });
|
||||
h.enable();
|
||||
|
||||
const formatMB = (n: number) => (n / 1024 / 1024).toFixed(1) + 'MB';
|
||||
|
||||
// Intento de obtener el límite de heap (puede variar según flags)
|
||||
let heapLimit = 0;
|
||||
try {
|
||||
// @ts-ignore
|
||||
const v8 = require('v8');
|
||||
const stats = v8.getHeapStatistics();
|
||||
heapLimit = stats.heap_size_limit || 0;
|
||||
} catch {}
|
||||
|
||||
setInterval(() => {
|
||||
const m = process.memoryUsage();
|
||||
const rss = formatMB(m.rss);
|
||||
const heapUsed = formatMB(m.heapUsed);
|
||||
const heapTotal = formatMB(m.heapTotal);
|
||||
const external = formatMB(m.external);
|
||||
const elDelay = h.mean / 1e6; // ms
|
||||
|
||||
let warn = '';
|
||||
if (heapLimit) {
|
||||
const ratio = m.heapUsed / heapLimit;
|
||||
if (ratio >= warnHeapRatio) {
|
||||
warn = ` ⚠ heap ${(ratio * 100).toFixed(1)}% del límite (~${formatMB(heapLimit)})`;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[MEM] rss=${rss} heapUsed=${heapUsed} heapTotal=${heapTotal} ext=${external} evLoopDelay=${elDelay.toFixed(2)}ms${warn}`);
|
||||
|
||||
// Resetear métricas de event loop delay para la siguiente ventana
|
||||
h.reset();
|
||||
}, intervalSeconds * 1000).unref();
|
||||
}
|
||||
|
||||
78
src/core/memory/memoryOptimizer.ts
Normal file
78
src/core/memory/memoryOptimizer.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
// Sistema adicional de optimización de memoria para complementar el monitor existente
|
||||
|
||||
export interface MemoryOptimizerOptions {
|
||||
forceGCInterval?: number; // minutos
|
||||
maxHeapUsageBeforeGC?: number; // MB
|
||||
logGCStats?: boolean;
|
||||
}
|
||||
|
||||
export class MemoryOptimizer {
|
||||
private gcTimer?: NodeJS.Timeout;
|
||||
private options: Required<MemoryOptimizerOptions>;
|
||||
|
||||
constructor(options: MemoryOptimizerOptions = {}) {
|
||||
this.options = {
|
||||
forceGCInterval: options.forceGCInterval ?? 15, // cada 15 min por defecto
|
||||
maxHeapUsageBeforeGC: options.maxHeapUsageBeforeGC ?? 200, // 200MB
|
||||
logGCStats: options.logGCStats ?? false
|
||||
};
|
||||
}
|
||||
|
||||
start() {
|
||||
// Solo habilitar si está disponible el GC manual
|
||||
if (typeof global.gc !== 'function') {
|
||||
console.warn('⚠️ Manual GC no disponible. Inicia con --expose-gc para habilitar optimizaciones adicionales.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Timer para GC forzado periódico
|
||||
if (this.options.forceGCInterval > 0) {
|
||||
this.gcTimer = setInterval(() => {
|
||||
this.performGC('scheduled');
|
||||
}, this.options.forceGCInterval * 60 * 1000);
|
||||
|
||||
this.gcTimer.unref(); // No bloquear el cierre del proceso
|
||||
}
|
||||
|
||||
console.log(`✅ Memory Optimizer iniciado - GC cada ${this.options.forceGCInterval}min, umbral: ${this.options.maxHeapUsageBeforeGC}MB`);
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (this.gcTimer) {
|
||||
clearInterval(this.gcTimer);
|
||||
this.gcTimer = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// Método público para forzar GC cuando sea necesario
|
||||
checkAndOptimize() {
|
||||
const memUsage = process.memoryUsage();
|
||||
const heapUsedMB = memUsage.heapUsed / 1024 / 1024;
|
||||
|
||||
if (heapUsedMB > this.options.maxHeapUsageBeforeGC) {
|
||||
this.performGC('threshold');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private performGC(reason: string) {
|
||||
if (typeof global.gc !== 'function') return;
|
||||
|
||||
const before = process.memoryUsage();
|
||||
const startTime = Date.now();
|
||||
|
||||
global.gc();
|
||||
|
||||
if (this.options.logGCStats) {
|
||||
const after = process.memoryUsage();
|
||||
const duration = Date.now() - startTime;
|
||||
const heapFreed = (before.heapUsed - after.heapUsed) / 1024 / 1024;
|
||||
|
||||
console.log(`🗑️ GC ${reason}: liberó ${heapFreed.toFixed(1)}MB en ${duration}ms`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Instancia singleton exportable
|
||||
export const memoryOptimizer = new MemoryOptimizer();
|
||||
Reference in New Issue
Block a user