Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

235
Views
¿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 answers
Answer question

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 Report

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 Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!