diff --git a/.changeset/chilled-ghosts-flow.md b/.changeset/chilled-ghosts-flow.md new file mode 100644 index 0000000000..ffe47476ad --- /dev/null +++ b/.changeset/chilled-ghosts-flow.md @@ -0,0 +1,7 @@ +--- +"@pnpm/plugin-commands-server": patch +"@pnpm/server": patch +pnpm: patch +--- + +Fix a bug causing the pnpm server to hang if a tarball worker was requested while another worker was exiting. diff --git a/store/plugin-commands-server/src/start.ts b/store/plugin-commands-server/src/start.ts index 5e8f4a9c9f..5878c81e86 100644 --- a/store/plugin-commands-server/src/start.ts +++ b/store/plugin-commands-server/src/start.ts @@ -64,7 +64,7 @@ export async function start ( } throw new PnpmError('SERVER_MANIFEST_LOCKED', `Canceling startup of server (pid ${process.pid}) because another process got exclusive access to server.json`) } - let server: null | { close: () => Promise } = null + let server: null | ReturnType = null onExit(() => { if (server !== null) { // Note that server.close returns a Promise, but we cannot wait for it because we may be @@ -118,6 +118,11 @@ export async function start ( // Set fd to null so we only attempt to close it once. fd = null await close(fdForClose) + + // Intentionally avoid returning control back to the caller until the server + // exits. This defers cleanup operations that should not run before the server + // finishes. + await server.waitForClose } async function getServerOptions ( diff --git a/store/server/src/createServer.ts b/store/server/src/createServer.ts index de34d7f12d..9a36ce5c70 100644 --- a/store/server/src/createServer.ts +++ b/store/server/src/createServer.ts @@ -166,7 +166,11 @@ export function createServer ( listener = server.listen(opts.port, opts.hostname) } - return { close } + const waitForClose = new Promise((resolve) => listener.once('close', () => { + resolve() + })) + + return { close, waitForClose } async function close () { listener.close()