es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Fuga de memoria en while con promesas.

Tienes un clúster de nodejs con un principal que maneja los ciclos de los trabajadores (en el bucle while) y que escucha los mensajes de los trabajadores para avanzar en el ciclo. (En mi código index.js no envía mensajes en setInterval sino en otro tipo de evento, aquí he simplificado el código para obtener la esencia del problema).

Server.js

var cluster = require('cluster');
const ClusterMessages = require('cluster-messages');
const messages = new ClusterMessages();

if (cluster.isMaster){
    let worker = cluster.fork()
    console.log(cluster);

(async () => {
    let cycle = 0
    while(true){
        console.log(cycle);
        cycle ++
        await Promise.all([
            enough(),
        ])
    }
    function enough () {
        return new Promise(resolve => {
            messages.on('enough', () => {
                console.log('enough');
                resolve()
            });
        });
    }
})()

} else {
    require('./index.js')
}

Index.js

const ClusterMessages = require('cluster-messages');
const messages = new ClusterMessages();

setInterval(() => {
    messages.send('enough');
}, 1000);

El código funciona bien (como tal, en este ejemplo y en mi código), pero parece que hay una fuga de memoria como se puede entender a partir de la salida de este código:

0
enough
1
enough
enough
2
enough
enough
enough
3
enough
enough
enough
enough...

He intentado varias cosas como intercambiar new Promise y messages.on(), agregar un return en el callback de la promesa, pero no tengo idea de lo que está sucediendo aquí. ¿Alguna idea?

La solución es hacer otro evento que se pueda activar una vez contrario al “escucha eventos” del paquete cluster-messages.

Server.js

if (cluster.isMaster){
    let worker = cluster.fork()
    console.log(cluster);

// Importación de eventos
const EventEmitter = require('events');
const eventEmitter = new EventEmitter();

messages.on('enough', () => {
    eventEmitter.emit('evento');
});
(async () => {
    let cycle = 0
    while(true){
        console.log(cycle);
        cycle ++
        await Promise.all([
            enough(),
        ])
    }
    function enough () {
        return new Promise(resolve => {
            eventEmitter.once('evento', () => {
                console.log('evento');
                resolve()
            });
        });
    }
})()

} else {
    require('./index.js')
}

Index.js

const ClusterMessages = require('cluster-messages');
const messages = new ClusterMessages();

setInterval(() => {
    messages.send('enough');
}, 1000);
Tags:  , , ,

Answer

  1. Avatar for davy.ai

    El problema en el código es que el paquete cluster-messages está usando un event listener que escucha el evento “enough” múltiples veces. Esto causa una fuga de memoria, ya que el event listener sigue acumulándose y el evento se activa más de una vez.

    Para solucionar esto, podemos crear otro evento que solo se pueda activar una vez. Podemos crear un nuevo emisor de eventos usando el paquete “events” y usar su método “once” para escuchar el evento solo una vez.

    Podemos modificar el código en Server.js para escuchar el evento “enough” usando el método “messages.on” del paquete cluster-messages. Luego podemos usar el método “eventEmitter.emit” del emisor de eventos que creamos para activar el evento “event”. Finalmente, podemos modificar la función “enough” para escuchar el evento “event” usando el método “eventEmitter.once”.

    El código modificado para Server.js:

    if (cluster.isMaster){
        let worker = cluster.fork()
        console.log(cluster);
    
        // Importación de eventos
        const EventEmitter = require('events');
        const eventEmitter = new EventEmitter();
    
        messages.on('enough', () => {
            eventEmitter.emit('event');
        });
        (async ()=>{
        let cycle = 0
        while(true){
            console.log(cycle);
            cycle ++
            await Promise.all([
                enough(),
            ])
        }
        function enough () {
            return new Promise(resolve => {
                eventEmitter.once('event', () => {
                    console.log('event');
                    resolve()
                });
            });
        }})()
    } else {
        require('./index.js')
    }
    

    El código modificado para Index.js sigue siendo el mismo:

    const ClusterMessages = require('cluster-messages');
    const messages = new ClusterMessages();
    
    setInterval(() => {
        messages.send('enough');
    }, 1000);
    

Comments are closed.