Search code examples
meteornode-fibers

Why does an insert() break wrapAsync'd child_process.spawn() handlers in a Meteor method?


I'm trying to use child_process.spawn() in a Meteor method. I want to capture the PID, stdout, stderr, and exit code from an external process, and store all that in the database.

Everything worked until I added that first insert() call. With that insert(), only one 'dummy' document is inserted into the database. I get no error messages in the server console. If I comment out that first insert(), the other insert() calls succeed.

// server/app.js
var spawn = Npm.require('child_process').spawn;

Meteor.methods({
  start: function() {
    var child = spawn('ls', ['/tmp']);
    var pid = child.pid;

    var wrappedChildStdoutOn = Meteor.wrapAsync(child.stdout.on, child.stdout);
    var wrappedChildStderrOn = Meteor.wrapAsync(child.stderr.on, child.stderr);
    var wrappedChildOn = Meteor.wrapAsync(child.on, child);

    // this insert() breaks upcoming insert() calls!
    Stuff.insert({pid: pid, date: new Date(), type: 'dummy', data: 'dummy'});

    wrappedChildStdoutOn('data',  function (data) {
      Stuff.insert({pid: pid, date: new Date(), type: 'stdout', data: data.toString()});
    });

    wrappedChildStderrOn('data', function (data) {
      Stuff.insert({pid: pid, date: new Date(), type: 'stderr', data: data.toString()});
    });

    wrappedChildOn('exit', function (code) {
      Stuff.insert({pid: pid, date: new Date(), type: 'exit', code: code});
    });
  }
});

What's up with that first insert() call?

Here's a Meteor app demonstrating the issue.


Solution

  • The insert takes a bit of time, so ls finishes its output before the insert completes. By the time you've put the event handler in place, it's too late.

    You can fix the issue by moving the first insert to the end, moving the first insert before the spawn call, or adding a no-op function () {} callback to the insert so it's called asynchronously.