My goal is to create a parent process that streams messages to the child processes indefinitely. To test this, i tried the below code for 150M messages to a child process that does nothing.
- (Possible Answer Edited Below) Why would this code run out of memory?
- I am using the ofe module but I do not know what to look for in the resulting heapdump, what method could I have used to search the heapdump for clues?
code:
server.js
// After 20M messages i get:
// FATAL ERROR: CALL_AND_RETRY_2 Allocation failed - process out of memory
var cp = require('child_process');
var xrange = require('xrange');
var maxChildren = require('os').cpus().length;
var util = require('util');
require('ofe').call();
var childError = function(err) {
console.log('Error: ' + err);
};
var childExit = function(code, signal) {
console.log('Exit Code: ' + code);
console.log('Exit Signal: ' + signal);
};
var childClose = function(code, signal) {
console.log('Close Code: ' + code);
console.log('Close Signal: ' + signal);
};
var childDisconnect = function() {
console.log('Disconnect');
};
var childMessage = function(msg, handle) {
console.log('Msg: ' + msg);
};
var createChild = function(){
var child = cp.fork("./child");
child.on('error', childError);
child.on('exit', childExit);
child.on('close', childClose);
child.on('disconnect', childDisconnect);
child.on('message', childMessage);
console.log("Child Created: " + child.pid);
return child;
}
var createChildren = function(){
var children = [];
xrange(maxChildren).each(function(i) {
children[i] = createChild();
});
return children;
}
var sendMessages = function(children) {
xrange(150000000).each(function(num) {
var idx = num % maxChildren;
if (num % 1000000 == 0) {
console.log(num);
}
children[idx].send(num);
});
};
child.js (contents)
process.on('message', function(msg) {});
Answer
Since this code is asynchronous , the parent process will send all the messages to the children without waiting for them to be processed, which seems to overload the clients. I believe the solution is to send a message back from the clients to 'pull' the next number after processing. Assuming this is the answer, I have a follow up question.
1) I'd like to write this but i'm not sure how to turn the xrange into a generator without the ability to yield (no-harmony nodejs), is there a callback solution? :
function getNextNumber(){
//pull the next available number from the 150M
}
child.on('message',function(msg) {
child.send(getNextNumber());
});
Followup
If I just wanted to iterate, this seems to be the way: https://stackoverflow.com/a/5784473/1578888 .
If I am reading this alternate answer correctly, it seems that implementing the xrange call as a true generator is not possible pre-harmony: https://stackoverflow.com/a/7442013/1578888
This is the code i ended up using (also renamed server.js to parent.js):
parent.js
var cp = require('child_process');
var xrange = require('xrange');
var maxChildren = require('os').cpus().length;
var util = require('util');
require('ofe').call();
var childError = function(err) {
console.log('Error: ' + err);
};
var childExit = function(code, signal) {
console.log('Exit Code: ' + code);
console.log('Exit Signal: ' + signal);
};
var childClose = function(code, signal) {
console.log('Close Code: ' + code);
console.log('Close Signal: ' + signal);
};
var childDisconnect = function() {
console.log('Disconnect');
};
var childMessage = function(msg, handle) {
//no output anymore!
//console.log('Msg: ' + msg);
};
var createChild = function(){
var child = cp.fork("./child.js");
child.on('error', childError);
child.on('exit', childExit);
child.on('close', childClose);
child.on('disconnect', childDisconnect);
child.on('message', childMessage);
console.log("Child Created: " + child.pid);
return child;
}
var getNextFn = (function () {
var i = 0
return function(cb) {
i = i + 1;
if (i < 150000000) {
return i;
} else {
return null;
}
}
})();
var createChildren = function(){
var children = [];
xrange(maxChildren).each(function(i) {
var child = createChild();
child.on('message', function(msg) {
var next = getNextFn();
if (next) {
if (next % 1000000 == 0) {
console.log(next + " " + new Date().toISOString());
}
child.send(next);
} else {
child.kill();
}
});
children[i] = child;
});
return children;
}
var c = createChildren();
child.js
process.on('message', function(msg) {
process.send({});
});
process.send({});