diff --git a/packages/nx/src/daemon/client/client.ts b/packages/nx/src/daemon/client/client.ts index 6f781e07868a8..c99c8e7f03b0b 100644 --- a/packages/nx/src/daemon/client/client.ts +++ b/packages/nx/src/daemon/client/client.ts @@ -582,6 +582,12 @@ export class DaemonClient { } async startInBackground(): Promise { + if (global.NX_PLUGIN_WORKER) { + throw new Error( + 'Fatal Error: Something unexpected has occurred. Plugin Workers should not start a new daemon process. Please report this issue.' + ); + } + mkdirSync(DAEMON_DIR_FOR_CURRENT_WORKSPACE, { recursive: true }); if (!existsSync(DAEMON_OUTPUT_LOG_FILE)) { writeFileSync(DAEMON_OUTPUT_LOG_FILE, ''); diff --git a/packages/nx/src/daemon/server/server.ts b/packages/nx/src/daemon/server/server.ts index 00c3c72ba339c..03a176a658987 100644 --- a/packages/nx/src/daemon/server/server.ts +++ b/packages/nx/src/daemon/server/server.ts @@ -10,7 +10,7 @@ import { PackageJson } from '../../utils/package-json'; import { nxVersion } from '../../utils/versions'; import { setupWorkspaceContext } from '../../utils/workspace-context'; import { workspaceRoot } from '../../utils/workspace-root'; -import { writeDaemonJsonProcessCache } from '../cache'; +import { getDaemonProcessIdSync, writeDaemonJsonProcessCache } from '../cache'; import { getFullOsSocketPath, isWindows, @@ -518,6 +518,17 @@ export async function startServer(): Promise { server.listen(getFullOsSocketPath(), async () => { try { serverLogger.log(`Started listening on: ${getFullOsSocketPath()}`); + + setInterval(() => { + if (getDaemonProcessIdSync() !== process.pid) { + return handleServerProcessTermination({ + server, + reason: 'this process is no longer the current daemon (native)', + sockets: openSockets, + }); + } + }).unref(); + // this triggers the storage of the lock file hash daemonIsOutdated(); diff --git a/packages/nx/src/project-graph/plugins/isolation/plugin-worker.ts b/packages/nx/src/project-graph/plugins/isolation/plugin-worker.ts index a5f9a82e0edd2..5a0569828c6c9 100644 --- a/packages/nx/src/project-graph/plugins/isolation/plugin-worker.ts +++ b/packages/nx/src/project-graph/plugins/isolation/plugin-worker.ts @@ -12,12 +12,23 @@ if (process.env.NX_PERF_LOGGING === 'true') { } global.NX_GRAPH_CREATION = true; - +global.NX_PLUGIN_WORKER = true; +let connected = false; let plugin: LoadedNxPlugin; const socketPath = process.argv[2]; const server = createServer((socket) => { + connected = true; + // This handles cases where the host process was killed + // after the worker connected but before the worker was + // instructed to load the plugin. + const loadTimeout = setTimeout(() => { + console.error( + `Plugin Worker exited because no plugin was loaded within 10 seconds of starting up.` + ); + process.exit(1); + }, 10000).unref(); socket.on( 'data', consumeMessagesFromSocket((raw) => { @@ -27,6 +38,7 @@ const server = createServer((socket) => { } return consumeMessage(socket, message, { load: async ({ plugin: pluginConfiguration, root }) => { + if (loadTimeout) clearTimeout(loadTimeout); process.chdir(root); try { const [promise] = loadNxPlugin(pluginConfiguration, root); @@ -138,6 +150,8 @@ const server = createServer((socket) => { // since the worker is spawned per host process. As such, // we can safely close the worker when the host disconnects. socket.on('end', () => { + // Destroys the socket once it's fully closed. + socket.destroySoon(); // Stops accepting new connections, but existing connections are // not closed immediately. server.close(() => { @@ -146,13 +160,20 @@ const server = createServer((socket) => { } catch (e) {} process.exit(0); }); - // Destroys the socket once it's fully closed. - socket.destroySoon(); }); }); server.listen(socketPath); +setTimeout(() => { + if (!connected) { + console.error( + 'The plugin worker is exiting as it was not connected to within 5 seconds.' + ); + process.exit(1); + } +}, 5000).unref(); + const exitHandler = (exitCode: number) => () => { server.close(); try {