socket.io
socket.io copied to clipboard
Why are volatile messages rapidly sent by the server not being received by the client?
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.emit
s working like regular emit
s?
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
For future readers:
Please see the documentation here: https://socket.io/docs/v3/emitting-events/#Volatile-events
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 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?
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 that's a good point, actually! I was not able to find the initial motivation for this feature, which was implemented a while ago.
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!