yarnpkg / yarn

The 1.x line is frozen - features and bugfixes now happen on https://github.com/yarnpkg/berry
https://classic.yarnpkg.com
Other
41.37k stars 2.72k forks source link

Using `--mutex network` on Node 18 will cause the waiting process to exit silently after 90s #8960

Open bradzacher opened 1 year ago

bradzacher commented 1 year ago

https://nodejs.org/en/blog/release/v18.0.0#http-timeouts

Node 18 added a new behaviour to the http server:

server.headersTimeout which limits the amount of time the parser will wait to receive the complete HTTP headers is now set to 60000 (60 seconds) by default.

Alongside a (not explicitly documented in the release notes) feature - server.connectionsCheckingInterval which defaults to 30000 (30 seconds).

It appears that connectionsCheckingInterval gets checked before headersTimeout - so after connectionsCheckingInterval + headersTimeout = 90s the server will consider a connection timed out if it hasn't sent any headers.

The latest v1 of yarn (by design) opens a connection to the mutex-owning process, and sends no data:

https://github.com/yarnpkg/yarn/blob/158d96dce95313d9a00218302631cd263877d164/src/cli/index.js#L450-L458

So on Node18 after 90s the mutex-owning http server declares the connection as timed out, and closes it. However this timeout does not trigger the close or error events - it just triggers the end event

Emitted when the other end of the socket signals the end of transmission, thus ending the readable side of the socket

So the socket cleans itself up, then the enclosing promise is no longer reachable, and node GC's it - meaning there are no open handles remaining. This causes the mutex waiting process to just quietly exit with exit code 0.

TL;DR - if all of these are true, you will get broken installs:

The fix is either to specify headersTimeout: <some large enough value> to cause the server to never classify a connection as timed out, or to add a end even handler to the socket so that it can restart if it was ended prematurely.

example patch that matches the default requestTimeout value of 5mins

diff --git a/node_modules/yarn/lib/cli.js b/node_modules/yarn/lib/cli.js
index 35ed698..ccccd61 100755
--- a/node_modules/yarn/lib/cli.js
+++ b/node_modules/yarn/lib/cli.js
@@ -88432,7 +88432,12 @@ var main = exports.main = function () {

         function startServer() {
           var clients = new (_set || _load_set()).default();
-          var server = (_http || _load_http()).default.createServer(manager);
+          var server = (_http || _load_http()).default.createServer({
+            // https://github.com/yarnpkg/yarn/issues/8960
+            // make the server never consider a dangling connection as timed out
+            headersTimeout: 300000,
+            requestTimeout: 300000,
+          }, manager);

           // The server must not prevent us from exiting
           server.unref();