Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

241
Vistas
¿Por qué 'requerir' sigue cargando archivos como cadenas en el montón a pesar de que se eliminó de require.cache?

Tengo una aplicación NodeJS que es como un administrador de tareas. Cada segundo (usando un temporizador rxjs), obtiene las tareas de la base de datos que están programadas. Cada tarea tiene un nombre de archivo y ese archivo se carga con el archivo required Cada archivo debe tener un método run() que ejecute la tarea. Una vez completada la tarea, el archivo requerido se elimina de require.cache . La aplicación falla después de un par de días porque el montón está lleno. Verifiqué con node --inspect y vi que los archivos requeridos se mantienen en las cadenas. ¿Alguien puede explicarme por qué los archivos requeridos se mantienen como cadenas en el montón, mientras que el archivo se elimina de require.cache ? Y mejor aún, si alguien me puede explicar cómo se puede resolver, ¡sería aún mejor!

A continuación se muestra una versión abreviada de mi código

 async test() { timer(0, 1000).pipe( exhaustMap(async () => { return await this.executeTasks(); }) ).subscribe(); } async executeTasks(): Promise<void> { // get all queue items const tasks = await this.getTasks(); // for each item for (const task of tasks) { // execute item await this.executeTask(task); } } async executeTask(item: QueueItem) { try { const Callback: typeof AutomationCallback = require(item.callbackFile); const callback = new Callback(); await callback.run(); } catch (error) { if (error instanceof Error && (error as any).code === 'MODULE_NOT_FOUND') { log_message('Failed executing', item.callback, item.id, `Cannot find file ${item.callbackFile}`); } else if (error instanceof RescheduleError) { log_message('Failed executing', item.callback, item.id, error.message); } else { log_message('Failed executing', item.callback, item.id, (error as any).toString()); } } finally { // remove require from cache; try { delete require.cache[require.resolve(item.callbackFile)]; } catch (error) { } } }

A continuación se muestra una captura de pantalla de una instantánea, el archivo resaltado se agrega al montón cada vez que el archivo se carga con require . Como puede ver en el lado izquierdo, el montón se llenará lentamente, el tiempo entre las instantáneas 1 y 2 es probablemente de 5 minutos. Captura de pantalla del montón

about 4 years ago · Santiago Trujillo
2 Respuestas
Responde la pregunta

0

Si todo está previamente definido, ¿por qué no tenerlos requeridos en la parte superior y llamarlos condicionalmente?

Ejemplo:

 class TaskRunnerBase { async run() { this.doRun(); } async doRun() {} // implemented by specific runners } class TaskRunnerA extends TaskRunnerBase { async doRun() {...} // custom implementation for TaskRunnerA } class TaskRunnerB extends TaskRunnerBase { async doRun() {...} // custom implementation for TaskRunnerB } class DefaultRunner extends TaskRunnerBase { async doRun() { console.log('Default Runner'); } }

Administrador de tareas

 const TaskRunnerA = require('./task-runners/task-runner-a'); const TaskRunnerB = require('./task-runners/task-runner-b'); // more runners const DefaultRunner = require('./task-runners/default-runner'); ... ... async executeTask(item: QueueItem) { try { const Callback: typeof AutomationCallback = this.getRunnerByType(item.callbackFile); const callback = new Callback(); await callback.run(); } catch (error) { ... } finally { ... } } getRunnerByType(type) { if(type === 'RunnerA') { // "RunnerA" for example return TaskRunnerA; } else if(type === 'RunnerB') { // "RunnerB" for example return TaskRunnerB; } .... else { return DefaultRunner; // If unknown type is passed, let's keep going, with DefaultRunner. } }

Este tipo de enfoque es simple, manejable y obvio a largo plazo.

about 4 years ago · Santiago Trujillo Denunciar

0

require.cache no es el único lugar donde se almacenan en caché los módulos cargados. Hay algunos más según el tipo de módulo y cómo se carga, y Node.js ni siquiera expone algunos de estos objetos, lo que significa que no podría eliminarlos si quisiera.

Un ejemplo bien documentado es module.children , una matriz de módulos cargados. Si observa la longitud de esta matriz, verá que se hace cada vez más grande después de cada require , porque efectivamente está cargando los mismos módulos una y otra vez, en lugar de reutilizar los objetos almacenados en caché:

 const Callback: typeof AutomationCallback = require(item.callbackFile); console.log('Loaded modules:', module.children.length); // <- const callback = new Callback(); await callback.run();

Por supuesto, podría borrar la lista de niños (que de todos modos no se usa como caché) junto con el caché require :

 // No guarantee that this will help. delete require.cache[require.resolve(item.callbackFile)]; module.children.pop();

Incluso escribí mi propia herramienta para hacer algo así de una manera que no afecte al resto del programa.

Hacerlo puede ayudar o no y, de hecho, podría ser incluso contraproducente si el único objetivo es evitar fugas de memoria, asignando recursos para cargar un módulo cada vez que se requiere el módulo.

Su mejor opción probablemente sea deshacerse del bloque " finally " y mantener el require.cache sin cambios. De esta forma, cada módulo se cargará como máximo una vez.

about 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda