socket.io icon indicating copy to clipboard operation
socket.io copied to clipboard

Why are volatile messages rapidly sent by the server not being received by the client?

Open GStipick opened this issue 5 years ago • 5 comments

You want to:

  • [x] report a bug
  • [ ] request a feature

Current behaviour

When firing off multiple emits from the server, I am getting a behavior I was not expecting for volatile messages.

When I do a regular emit in rapid succession via a loop, all messages are received by the client and can be inspected via chrome network inspector. When I change to doing volatile.emit, I am only receiving the first message (of 100).

The question is, why is this the case?

Additionally, I noticed that if I had a 50ms delay to sending each volatile.emit message via setTimeout() then all messages are sent correctly (but of course with a delay). What factors go into this method working as expected, but not immediate volatile.emits working like regular emits?

Steps to reproduce (if the current behaviour is a bug)

Below is a link to a repo that is a setup of my test between volatile and non volatile messages. https://github.com/GStipick/socketioVolatileTest

Expected behaviour

From my understanding of: The Documentation for volatile messages , I would expect all of the messages to be sent, even if they are not received by the client. If they are indeed being sent, then why is emit able to be received for all messages, while volatile.emit is not?

Setup

  • OS: Windows 10 (Version 1803) [OS Build 17134.285]
  • browser: Firefox/Chrome
  • socket.io version: 2.1.1
  • node.js version: 10.9.0

GStipick avatar Oct 11 '18 21:10 GStipick

For future readers:

Please see the documentation here: https://socket.io/docs/v3/emitting-events/#Volatile-events

darrachequesne avatar Mar 04 '21 06:03 darrachequesne

Please explain how does the documentation fixes (or clarify) this issue ? 🤔 Maybe this part : Volatile events are events that will not be sent if the underlying connection is not ready ?

My understanding of the volatile events was that every event names were treated independently, for example if I do this :

socket.volatile.emit('ping', 123);
socket.volatile.emit('ping', 456);
socket.volatile.emit('bonjour');

I would expect to receive 1 of the 2 ping events, and 1 bonjour event. But what actually happen is : I only receive 1 ping event, nothing more ! Similar problems occurs if I reverse the order :

socket.emit('bonjour');
socket.volatile.emit('ping', 123);
socket.volatile.emit('ping', 456);

In this case I would never receive any ping events 😱 ! (since the underlying connection is used by the bonjour event)

I don't understand how the current implementation is useful, I guess it works when your server/client sends only ONE unique event, but when does that happen ?? 🤔 Also the use of a setTimeout (or setImmediate) seems required when emitting multiple volatile events synchronously, yet there is no mentions of it in the documentation.

wmcmurray avatar Mar 31 '22 15:03 wmcmurray

@wmcmurray sorry for dismissing the issue :angel:

No, every event names are not treated independently. The "volatile" flag actually refers to the state of the underlying connection.

For example, in HTTP long-polling, if the client is already sending something with a POST request, any volatile packets sent during this delay will be discarded.

socket.volatile.emit('ping', 123);
socket.volatile.emit('ping', 456);
socket.volatile.emit('bonjour');

In the example above, the POST request will only contain the first packet.

Same behavior when pulling content from the server: if the client has an active GET request waiting for data, then the first volatile packet goes through, and next ones are discarded.

Is my explanation clear enough?

darrachequesne avatar Apr 01 '22 13:04 darrachequesne

Yes very clear 🙂 but when is it useful to discard events based on "is the server/client already sending something" ? 🤔 I can't find any use case for this... And from what I've read online, WebSockets is based on TCP anyway, so it's not like using this would have UDP benefits.. it's just more unreliable and unpredictable, when does a developer needs those ? lol

Taking your example from the doc [...] sending the position of the characters in an online game, a proper implementation of this would need to identify each unique events, then instead of sending them right away (or piling them in a queue), it should check if that same unique event is already in the queue (but with older values), if it's the case it should update the values with the latest ones. Then send the event when it's time to. This way only the latest values would be sent (reliably and predictably).

This has to be implemented on app level though (I believe), because if someone send an event like this :

socket.emit('update-position', [entity1.id, entity1.position]);
socket.emit('update-position', [entity2.id, entity2.position]);

Socket.io has no way of identifying those 2 events as unique, because even if the event name is the same, it affects 2 different entities therefore it should be treated separately.

wmcmurray avatar Apr 02 '22 14:04 wmcmurray

@wmcmurray that's a good point, actually! I was not able to find the initial motivation for this feature, which was implemented a while ago.

darrachequesne avatar Apr 05 '22 23:04 darrachequesne

Same here, I thought the underlying connection is not ready means not connected, but it clearly is not according to above info exposed in this issue. Can the doc be more worse than this? as long as the connection is not disconnected, it should not be considered "not ready", I'm using websocket, it's clearly not the http POST case here described!

ulion avatar Jun 14 '23 00:06 ulion