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:
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:
you use NodeJS v18+
you use --mutex network
your install that holds the mutex takes longer than 90s
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();
https://nodejs.org/en/blog/release/v18.0.0#http-timeouts
Node 18 added a new behaviour to the
http
server:Alongside a (not explicitly documented in the release notes) feature -
server.connectionsCheckingInterval
which defaults to 30000 (30 seconds).It appears that
connectionsCheckingInterval
gets checked beforeheadersTimeout
- so afterconnectionsCheckingInterval + 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 theclose
orerror
events - it just triggers theend
eventSo 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:
--mutex network
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 aend
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