Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Persisting data with socket.io server on 'connect'

I'm using socket.io with MongoDB and promises. I'd like to save some data in Mongo when the session is established, and further socket events can't necessarily be handled properly until this data is saved.

// Simplified code
io.sockets.on('connection', function(socket) 
  var req = socket.handshake;

  mongoDbPromise.then(function (db) {
    return db.collection('mycollection').insert({some_user_data: req.query.some_user_data});
  }).then(function (insertResult) {
    // do some other work
  }).catch(function (error) {
    socket.disconnect();
    logger.info(error);
  });
}

The problem I'm experiencing is that the connection socket event is emitted immediately after this connection handler returns, but the work of setting up the connection continues when MongoDB's promise resolves and my then handler is called. Clients that make a request immediately after receiving connection see the app in an inconsistent state.

I can see two ways to resolve this:

1) Use a callback of some sort to control when connection is emitted. I haven't seen any sign in the socket.io docs that it's possible to do this, though.

2) Instead of using connection to signal that the socket is ready to go, use some other custom event (lets call it connection_established) and emit it in the then handler.

Is option 1 possible? Is there a better way of handling this case?

like image 788
Pathogen Avatar asked Apr 10 '26 21:04

Pathogen


1 Answers

Well, if you have async work to do in your connection handler (which you do), then you can't prevent the client from thinking it is connected before you are done with your work in the connect handler. Your have a couple choices for solving that:

  1. You can hook into the authentication process for the socket.io connection using middleware and use that feature to control things. If your DB operation fails, you will essentially act like the connection is not authorized which will disconnect it.

  2. You can send the client your own "connectionReady" message to the client and program the client end to not send you anything until it receives the "connectionReady" message which you will only send when the connection has finished doing it's thing (it sounds like you already have this idea with your thought of connection_established).

The auth process in socket.io v1 and higher can be done with middleware like this:

io.use(function(socket, next) {
  var req = socket.handshake;

  mongoDbPromise.then(function (db) {
    return db.collection('mycollection').insert({some_user_data: req.query.some_user_data});
  }).then(function (insertResult) {
    // do some other work

    // socket is now ready to go
    next();
  }).catch(function (error) {
    // error on authentication
    next(new Error('not authorized');
    logger.info(error);
  });
});

The connectionReady process could work like this:

// Simplified code
io.sockets.on('connection', function(socket) 
  var req = socket.handshake;

  mongoDbPromise.then(function (db) {
    return db.collection('mycollection').insert({some_user_data: req.query.some_user_data});
  }).then(function (insertResult) {
    // do some other work

    // send client a message indicating that the connection is now ready
    socket.emit("connectionReady", true);
  }).catch(function (error) {
    socket.disconnect();
    logger.info(error);
  });
}
like image 104
jfriend00 Avatar answered Apr 12 '26 12:04

jfriend00