socket.io duplicate emit events on browser refresh

Viewed 351

I'm running into an issue with my socket.io implementation and don't know how to solve it. I'm using pg_notify with LISTEN so when a certain value is modified in the db, it emits 'is_logged_in' to a certain client.

That in itself is working fine - my issue is when I refresh the page, socket.io disconnects the current socket_id, creates a new socket_id as usual, but when this happens, it's creating a second pgsql client instance and duplicating requests - fires the "logged_in" event 2x.

If I refresh the page again, and then manually fire the pg "logged_in" trigger, it will now emit 3 times etc. I have a leak.

const io = require('socket.io')();
const pg = require('pg');

io.on('connection', (socket) => {

  const pgsql = new pg.Client({
      (host, port, user, pass, db)
  })

  pgsql.connect()
  pgsql.query("LISTEN logged_in");

  pgsql.on('notification', function (data) {
        socket.to(json.socket_id).emit('is_logged_in', { status:'Y' });
  });

  socket.on('disconnect', () => {
    //pgsql.end();
  });

});

I've tried killing the pgsql instance (in the socket.on disconnect) but for some reason the LISTEN stops working when I do that.

I've also tried moving the new pg.Client outside the io.on connection but when I refresh the page, the old socket_id disconnects, the new one connects, and it never executes the code to recreate the pg client.

Any ideas?

1 Answers

These are creating problems probably:

  • The pgsql instance is created on each socket connection request and is not being destroyed on disconnection
  • notification handler is not being removed on disconnection

I'm not much familiar with postgres, but I have worked extensively with socket. so, something like this should fix your issue:

const io = require('socket.io')();
const pg = require('pg');

const pgsql = new pg.Client({
   (host, port, user, pass, db)
})

pgsql.connect();

io.on('connection', (socket) => {
  pgsql.query("LISTEN logged_in");

  const handler = function (data) {
     socket.to(json.socket_id).emit('is_logged_in', { status:'Y' });
     // You could also do pgsql.off('notification', handler) here probably
     // or check if pgsql.once method is available as we need to call this handler only once?
  }

  pgsql.on('notification', handler);

  socket.on('disconnect', () => {
    pgsql.off('notification', handler);
    //pgsql.end(); // Call it in server termination logic
  });

});
Related