Alexander Mills
Alexander Mills

Reputation: 100496

Node.js socket.send( ) functions failing to complete before exit

In some Node.js scripts that I have written, I notice that even if the last line is a synchronous call, sometimes it doesn't complete before Node.js exits.

I have never seen a console.log statement fail to run/complete before exiting, but I have seen some other statements fail to complete before exiting, and I believe they are all synchronous. I could see why the callback of an async function would fail to fire of course in this case.

The code in question is a ZeroMQ .send() call like so:

   var zmq = require('zmq');
   var pub = zmq.socket('pub');

   pub.bindSync('tcp://127.0.0.1:5555');   

    setInterval(function(){
        pub.send('polyglot');
    },500);

The above code works as expected...but if I remove setInterval() and just call it like this:

   var zmq = require('zmq');
   var pub = zmq.socket('pub');

    pub.bindSync('tcp://127.0.0.1:5555');

    pub.send('polyglot');  //this message does not get delivered before exit
    process.exit(0);

...Then the message will not get delivered - the program will apparently exit before the pub.send() call completes.

What is the best way to ensure a statement completes before exiting in Node.js ? Shutdown hooks would work here, but I am afraid that would just be masking the problem since you can't put everything that you need to ensure runs in a shutdown hook.

This problem can also be demonstrated this way:

 if (typeof messageHandler[nameOfHandlerFunction] == 'function') {
          reply.send('Success');
          messageHandler[nameOfHandlerFunction](null, args);
         } else {
         reply.send('Failure'); //***this call might not complete before the error is thrown below.***
         throw new Error('SmartConnect error: no handler for ZMQ message sent from Redis CSV uploader.');
     }

I believe this is a legit/serious problem because a lot of programs just need to publish messages and then die, but how can we effectively ensure all messages get sent (though not necessarily received)?

EDIT: One (potential) way to fix this is to do:

socket.send('xyz');
socket.close(); // supposedly this will block until the above message is sent
process.exit(0);

Upvotes: 17

Views: 917

Answers (2)

Alexander Mills
Alexander Mills

Reputation: 100496

I think the simple answer is the the socket.send() method is in fact asynchronous and that is why we see the behavior I described in the OP.

The question then is - why does socket.send() have to be asynchronous - could there not be a blocking/synchronous version that we could use instead for the purpose intended in the OP? Could we please have socket.sendSync()?

Upvotes: 0

lxe
lxe

Reputation: 7629

Diving into zeromq.node, you can see what Socket.send just pushes your data to _outgoing:

this._outgoing.push([msg, flags]);

... and then calls _flush iff zmq.ZMQ_SNDMORE is unset:

this._flush();

Looks like _flush is actually doing the socket write. If _flush() fails, it emits an error.

Edit:

I'm guessing calling pub.unbind() before exiting, will force the _flush() to be called:

pub.unbind('tcp://127.0.0.1:5555', function(err) {
  if (err) console.log(err);
  process.exit(0); // Probably not even needed
});

Upvotes: 8

Related Questions