question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Event listeners on dynamic namespaces break when server emits on matching custom namespace before client connects

See original GitHub issue

Describe the bug

Generic description When a server emits an event on a custom namespace before any client has connected to that namespace, event listeners on a dynamic namespace that matches the custom namespace will not work. When a client connects to the custom namespace after an event has been emitted on it, the event listeners of the matching dynamic namespace will not trigger.

Our concrete use case We have a setup where we have a node cluster with multiple workers. Each worker is both used to communicate with clients using socket.io websockets and to run long running jobs on the background and emit progress about these jobs. The setup uses the Redis adapter to let socket.io communicate between workers.

We setup the “connect” event listeners on the serverside with a dynamic namespace, lets say /^\/dynamic-\d+$/. Then, when a worker starts on a job it will start emitting on namespace dynamic-7 for instance.

We have experienced that the event listeners on the dynamic namespace do not work anymore for a particular worker when that worker starts emitting on a matching custom namespace before any client connects to the worker with the same custom namespace.

So, this works:

  1. Worker starts and initiates socket.io setup and adds listener to dynamic namespace /^\/dynamic-\d+$/
  2. Clients connects to custom namespace /dynamic-7
  3. Same worker starts working on long running job and emits on custom namespace /dynamic-7
  4. Client receives updates

But this doesn’t:

  1. Worker starts and initiates socket.io setup and adds listener to dynamic namespace /^\/dynamic-\d+$/
  2. Same worker starts working on long running job and emits on custom namespace /dynamic-7
  3. Clients connects to custom namespace /dynamic-7
  4. Client doesn’t receive updates

To Reproduce

Be sure to start the server script before starting the client script.

Socket.IO server version: 4.3.2

Server

const socketio = require("socket.io");

const io = new socketio.Server(3000, {});

io.of(/^\/dynamic-\d+$/).on("connection", (socket) => {
  // we only get here if the bottom line is commented out, otherwise this listener is never triggered
  console.log(`connected with ${socket.id} based on dynamic namespace`);

  io.of("/dynamic-1").emit("some_event", {"foo": "bar"});
});

// this initial emit causes the bug. comment out this line to see the expected behavior
io.of("/dynamic-1").emit("some_event", {"foo": "bar"});

Socket.IO client version: 4.3.2

Client

const socketio = require("socket.io-client");

const socket = socketio.io("ws://localhost:3000/dynamic-1", {});

socket.on("connect", () => {
  console.log(`connected with id ${socket.id}`);
});

// we will only receive this event if the server doesn't emit before we connect
socket.on("some_event", (data) => {
  console.log("received event:", data);
});

Expected behavior I would expect the server to be able to start emitting events on a custom namespace that matches a dynamic namespace, even if no client has yet connected to that namespace.

Platform:

  • Device: Chrome
  • OS: Ubuntu

Additional context Note that for us the cluster setup caused the bug to arise, but it is not necessary as the reproduction example demonstrates.

Our current workaround is to execute io._checkNamespace("/dynamic-1", {}, () => {}) before we execute the first emit. This ensures that the custom namespace is added to the dynamic namespace and the event listeners of the dynamic namespace are copied to the custom namespace.

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:4
  • Comments:8 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
thombohlkcommented, Dec 2, 2021

@dknapp47 Yes, we are running multiple workers in our cluster. The workaround ensures that a child namespace on which a worker wants to broadcast (e.g. /dynamic-1) is first assigned to the dynamic namespace that was created for the listener (e.g. /^\/dynamic-\d+$/. So currently we are doing this check before any emit on a child namespace of a dynamic namespace.

0reactions
Beretta1979commented, Oct 4, 2022

I’ve added a note here; https://socket.io/docs/v4/namespaces/#dynamic-namespaces

In your example, the connection handler is only called if there is no existing namespace that matches. But since the “dynamic-1” was already registered (by calling io.of("/dynamic-1")), the connection handler is not called.

That being said, I’m open to suggestions on how we could improve this.

Well I would certainly make sense to register all middlewares / event handlers that were defined on the dynamic namespace level when .of() is called with a string and it matches a dynamic namespace, not only when the first connection is done. The current flow is confusing and unexpected. Especially in the multi instance (cluster) usecase where you want to emit event to a namespace even if there were no connections in your instance yet.

We will use the workaround from @thombohlk to match that behavior for now.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Working with socket.io dynamic namespaces - alxolr
Problem. In order to have a dynamic namespace there is needed a first peer to subscribe to the namespace. In other words the...
Read more >
Namespaces | Socket.IO
A Namespace is a communication channel that allows you to split the logic of your application over a single shared connection (also called ......
Read more >
Dynamic Namespaces Socket.IO - Stack Overflow
event when socket connected in 'user' namespace ev.on('socket.connection route.user', function () { console.log('route[user] connecting.
Read more >
EventTarget.removeEventListener() - Web APIs | MDN
The removeEventListener() method of the EventTarget interface removes an event listener previously registered with EventTarget.
Read more >
Fix list for IBM WebSphere Application Server V8.5
Java client hang when queue manager is quiescing as new connection attempts are made. IT24521, Activation Specifications that consume request messages without ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found