bluebird icon indicating copy to clipboard operation
bluebird copied to clipboard

Memory leak with infinite loop built on long stacktraces and promisified function

Open Rush opened this issue 8 years ago • 11 comments

Running Node 4.4.2 and latest bluebird.3.3.5.

'use strict';
let Promise = require('bluebird');

Promise.longStackTraces();

function sendPing(cb) {
  cb();
}

let promisifiedSendPing = Promise.promisify(sendPing);

function pingLoop() {
  return promisifiedSendPing()
  .then(pingLoop)
  .catch(err => {
    console.log('Caught error', err);
    process.exit();
  });
}

pingLoop();

// this is to demonstrate that the memory is growing despite the GC working
if(global.gc) setInterval(global.gc, 1);

Run with:

node --expose_gc test.js

Then observe memory usage rising:

while [ 1 ];do ps aux|grep test.js|grep -v grep ; sleep 1;done

If promisifiedSendPing is changed to Promise.delay(1) there is no memory leak.

Rush avatar Apr 25 '16 15:04 Rush

Does it leak with stack traces disabled?

benjamingr avatar May 05 '16 09:05 benjamingr

No. 4 and 5.

Rush avatar May 05 '16 09:05 Rush

@benjamingr any ideas?

Rush avatar May 22 '16 09:05 Rush

I think this happens on the version without long stack traces as well - but it's much slower.

benjamingr avatar Sep 01 '16 09:09 benjamingr

That code is buggy and there's hardly a way to avoid growing memory - you are adding more and more catch handlers to the chain with every pingLoop call. You should either be using

function pingLoop() {
  return promisifiedSendPing()
  .then(pingLoop);
}
pingLoop()
.catch(err => {
  console.log('Caught error', err);
  process.exit();
});

or

function pingLoop() {
  return promisifiedSendPing()
  .catch(err => {
    console.log('Caught error', err);
    process.exit();
  })
  .then(pingLoop);
}
pingLoop();

bergus avatar Sep 01 '16 10:09 bergus

@bergus oh wait you're absolutely right! The catch handlers are added after the current promise and not before it - there is nothing bluebird can do about this.

Great catch!

benjamingr avatar Sep 01 '16 11:09 benjamingr

Both versions you posted don't leak so that's extra proof.

benjamingr avatar Sep 01 '16 11:09 benjamingr

@benjamingr Unfortunately this is leaking memory. Can you re-open the ticket?

let Promise = require('bluebird');
Promise.longStackTraces();

setInterval(global.gc, 100);

function promisifiedSendPing() {
  return Promise.resolve();
}

function pingLoop() {
  return promisifiedSendPing()
  .then(pingLoop);
}

pingLoop()
.catch(err => {
  console.log('Caught error', err);
  process.exit();
});

Run with node --expose_gc test.js

Rush avatar Sep 29 '16 10:09 Rush

Confirmed. Also confirming that it doesn't leak without LST with node --max-old-space-size=10 for at least 90s

With LST time BLUEBIRD_DEBUG=1 node --max-old-space-size=20 leak.js dies after 2 seconds.

@petkaantonov if this is impossible to be both performant (because stacks are not stringified) and non-leaky, maybe we should limit the stack trace size to N async events...

spion avatar Sep 29 '16 17:09 spion

I can also reproduce the memory leak with longStackTraces enabled.

// npm install bluebird faker memwatch-next

var Promise = require('bluebird');
Promise.config({
    longStackTraces: true
});
var faker = require('faker');
var memwatch = require('memwatch-next');
function memUsage() {
    var hd = new memwatch.HeapDiff();
    var diff = hd.end();
    console.log(diff.after.size);
}

// a simple database.query() mock
function query(criteria, callback) {
    var data = faker.lorem.paragraphs(5000, '');
    memUsage();
    setTimeout(function () {
        callback(null, data);
    }, 600);
}

function queryPromisified(criteria) {
    return new Promise(function (resolve, reject) {
        query(criteria, function (ex, response) {
            ex ? reject(ex) : resolve(response);
        });
    });
}
function getItems(criteria) {
    return queryPromisified(criteria).then(function (data) {
        return data ? getItems(data) : null;
    });
}
getItems({});

If I comment out the Promise to use native it doesn't have the problem.

zorji avatar Mar 10 '17 05:03 zorji

has it been fixed? or still an issue?

atdksp avatar Oct 19 '20 14:10 atdksp