1

Environment:

  1. Backend
    • node:latest
    • socket.io | 4.5.2
  2. Frontend
    • React Native | 0.70.4
    • socket.io-client | 4.6.0
    • both Android and iOS

Here is my NodeJs entry file:

const numCPUs = cpus().length
if (cluster.isPrimary) {
  const app = express()
  const httpServer = http.createServer(app)

  setupMaster(httpServer, { loadBalancingMethod: 'least-connection' })

  setupPrimary()

  for (let i = 0; i < numCPUs; i++) {
    cluster.fork()
  }

  cluster.on('exit', (worker) => {
    cluster.fork()
  })
} else {
  const app = express()
  const httpServer = http.createServer(app)
  const io = new Server(httpServer, { maxHttpBufferSize: 1e8 })

  io.adapter(createAdapter())
  setupWorker(io)

  API.Socket.init(io, process.pid)

  middlewares.forEach((middleware: any) => app.use(middleware))
  routes.forEach((route) => app.use(route.path, route.handler))

  httpServer.listen(CONFIG.PORT, () => {})
}

I have a simple chat application. When user A sends message to user B, new chat message and notification is recorded in database. Now that chat message and notification* should be sent to the B user. There are 2 socket emit-functions for that:

  sendNewNotification(
    notification: BE.Entities.TNotification,
    toUser: string,
  ) {
    this.io
      ?.to(toUser)
      .volatile.emit(ECustomEvents.NewNotification, notification)
  }

  sendPrivateMessage(
    toUser: string | Array<string>,
    chatMessage: BE.Entities.TChatMessage,
    sourceUser: BE.Entities.TUser,
  ) {
    this.io
      ?.to(toUser)
      .volatile.emit(ECustomEvents.PrivateMessage, chatMessage, sourceUser)
  }

If I do it like this, the targetUser is not going to receive the event with the newChatMessage however he will receive the savedNotification

  API.Socket.sendPrivateMessage(targetUserId, newChatMessage, userToPass)
  API.Socket.sendNewNotification(savedNotification, targetUserId)

Now, if I switch these lines:

  API.Socket.sendNewNotification(savedNotification, targetUserId)
  API.Socket.sendPrivateMessage(targetUserId, newChatMessage, userToPass)

the behavior would be as expected: the target user B will receive both saved notification and new chat message

How is that possible? What could be wrong?

Thank you mates in advance!

spatak
  • 1,039
  • 1
  • 14
  • 25

1 Answers1

0

With the current information, I'm not so sure the order matters but perhaps that it's a side-effect / coincidence. Are you checking anywhere to make sure the server-side socket is ready before the client emits?

Consider this super simple WebSocket chat sandbox:

One of the issues I noticed when writing this is when the server WebSocket is not ready, I could not emit from the client to the server. To make sure the server is ready, I sent a ping from the server to the client notifying the client that the server is ready:

wss.on("connection", async function connection(client, request) {
  console.log("user connected", Date.now());
  client.send(JSON.stringify({ ready: true }));

  ...
});

I also notice you are usingg the volatile.emit which according to the documentation:

Volatile events

Volatile events are events that will not be sent if the underlying connection is not ready (a bit like UDP, in terms of reliability).

This can be interesting for example if you need to send the position of the characters in an online game (as only the latest values are useful).

socket.volatile.emit("hello", "might or might not be received");

The Socket.IO docs have a similar listener which lets you know when the server is ready.

If you prevent the client from emitting until the server is ready, you can avoid this issue. You also should not need to use the volatile.emit for something that must be delivered.

Wesley LeMahieu
  • 2,296
  • 1
  • 12
  • 8