Support unref method with option to autoUnref#652
Support unref method with option to autoUnref#652KCErb wants to merge 4 commits intosocketio:masterfrom
unref method with option to autoUnref#652Conversation
|
Hi! Thanks for the pull request 👍 I'm not sure about merging this. I think I'd rather have the users explicitly call I see there are a lot of ❤️ on the pull request, what is the opinion of others? |
|
Hi, @darrachequesne -- without this patch, it can be extremely difficult for users of libraries which use SocketIO to ensure that their Node programs will exit "naturally" - i.e. without explicitly calling process.exit(). This means, in turn then, that developers of libraries using SocketIO have to add a specific close function, and that they have to expose their API consumers to the underlying implementation detail that there is a SocketIO socket in use which needs to be closed. This is particularly insidious, as the implementation details of an underlying library should not be of concern to API consumers -- but the lack of an unref() for use by the library developer forces the API consumer to handle socket closing. In turn this means that libraries that are implemented with Node sockets cannot be re-implemented with SocketIO without changing their externally-facing API library. I believe that it should be a design goal of SocketIO to allow library authors to implement the same libraries they can implement with native NodeJS sockets, without requiring a library API change. NodeJS explicitly added Timer and Socket FWIW -- node-redis and net-websocket-polyfill expose an unref() method and it is on the roadmap for simple-websockets. |
|
That makes sense, thanks a lot for the clear explanation 👍 I encountered an exception when restricting the transports: const socket = eio("http://localhost:3000", {
autoUnref: true,
transports: ["websocket"]
});throws: (I think we need to wait for the With HTTP long-polling only, it does not seem to work either (the process does not stop): const socket = eio("http://localhost:3000", {
autoUnref: true,
transports: ["polling"]
});I think we'll have to dig into the source of the Would you have time to check? |
|
Thanks @wesgarland that's a great way of putting it! @darrachequesne I haven't played with restricted transports yet. I'll take a look next chance and get back to you with my ideas. 👍 |
|
I was able to reproduce the issue when the transport layer is specified as @darrachequesne I usually use wtfnode to diagnose what exactly is hanging up the event loop. Perhaps you could post your test case or the output of |
|
@KCErb here's my code: const eio = require(".");
const socket = eio("http://localhost:3000", {
autoUnref: true,
transports: ["websocket"]
});
socket.on("open", () => {
console.log("open");
});
setTimeout(() => {
console.log("process should exit now");
}, 2000);With But with |
|
Interesting, I ran your code with You mentioned |
|
Merged as 6551683. Some tests were added here: https://github.com/socketio/engine.io-client/blob/master/test/node.js To make the test with HTTP long-polling only pass, it was necessary to edit the engine.io-client/contrib/xmlhttprequest-ssl/XMLHttpRequest.js Lines 497 to 501 in 6551683 @KCErb could you please check if everything is OK for you? |
|
Yup, works for me. Thanks for accepting and adding those tests too. I wasn't sure how best to test it all, nice to see your solution! Now I'm really excited to see this get paired with socketio/socket.io-client#1442 :) |
|
@wesgarland "...but the lack of an unref() for use by the library developer forces the API consumer to handle socket closing." to me this argument is not convincing. I will argue that if you (the consumer) use socket.io, you should be aware that you need to close it when you are done just like when you open a database connection you should close the connection when you are finished. |
|
@darrachequesne @wesgarland Although I was not convinced that argument I did find node-redis explained unref purpose. His explanation made sense to me, "This is commonly the case for some sort of script where you are doing a batch query, or a test that connects to Redis.", i.e. for a short-lived script it is convenient. But I also find that unref was also proposed to ioredis and author seemed to like the idea and agreed to add it but he finally rejected it. |
The kind of change this PR does introduce
Current behaviour
Users must call
closeon any stray transports if they want their program to be able to exit.New behaviour
Users may either call
unrefdirectly or pass a new optionautoUnref: trueto allow a program to exit which has only an engine.io transport on the event loop.Other information (e.g. related issues)
unrefis a common feature for APIs that can prevent the event loop from exiting. See also: