Skip to content

Commit d2bf2f0

Browse files
committed
fix(core): improve daemon reliability with reconnection and socket handling
- Add exponential backoff reconnection for daemon client - Implement version check handshake in socket messenger - Improve socket error handling and prevent hanging on errors - Keep watch process alive during file change listening - Simplify socket messenger by removing socketPath parameter - Move socket creation into listen() to prevent unhandled errors - Add graceful daemon reconnection with user feedback - Restart daemon on lock file and version changes - Prevent watch connections from exiting during reconnection - Improve in-flight request handling during daemon reconnection - Add periodic check for daemon being outdated - Extract shared connections for file watchers and graph listeners - Simplify pending message handling in reconnection - Silence noisy reconnection logs during normal restarts - Fix file-server executor to use output.log instead of logger
1 parent aa38b25 commit d2bf2f0

File tree

25 files changed

+2155
-196
lines changed

25 files changed

+2155
-196
lines changed

packages/js/src/executors/node/node.impl.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,8 +324,16 @@ export async function* nodeExecutor(
324324
includeDependentProjects: true,
325325
},
326326
async (err, data) => {
327-
if (err === 'closed') {
328-
logger.error(`Watch error: Daemon closed the connection`);
327+
if (err === 'reconnecting') {
328+
// Silent - daemon restarts automatically on lockfile changes
329+
return;
330+
} else if (err === 'reconnected') {
331+
// Silent - reconnection succeeded
332+
return;
333+
} else if (err === 'closed') {
334+
logger.error(
335+
`Failed to reconnect to daemon after multiple attempts`
336+
);
329337
process.exit(1);
330338
} else if (err) {
331339
logger.error(`Watch error: ${err?.message ?? 'Unknown'}`);

packages/js/src/executors/tsc/lib/batch/watch.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,14 @@ export async function watchTaskProjectsPackageJsonFileChanges(
1717
const unregisterFileWatcher = await daemonClient.registerFileWatcher(
1818
{ watchProjects: projects },
1919
(err, data) => {
20-
if (err === 'closed') {
21-
logger.error(`Watch error: Daemon closed the connection`);
20+
if (err === 'reconnecting') {
21+
// Silent - daemon restarts automatically on lockfile changes
22+
return;
23+
} else if (err === 'reconnected') {
24+
// Silent - reconnection succeeded
25+
return;
26+
} else if (err === 'closed') {
27+
logger.error(`Failed to reconnect to daemon after multiple attempts`);
2228
process.exit(1);
2329
} else if (err) {
2430
logger.error(`Watch error: ${err?.message ?? 'Unknown'}`);
@@ -50,8 +56,14 @@ export async function watchTaskProjectsFileChangesForAssets(
5056
includeGlobalWorkspaceFiles: true,
5157
},
5258
(err, data) => {
53-
if (err === 'closed') {
54-
logger.error(`Watch error: Daemon closed the connection`);
59+
if (err === 'reconnecting') {
60+
// Silent - daemon restarts automatically on lockfile changes
61+
return;
62+
} else if (err === 'reconnected') {
63+
// Silent - reconnection succeeded
64+
return;
65+
} else if (err === 'closed') {
66+
logger.error(`Failed to reconnect to daemon after multiple attempts`);
5567
process.exit(1);
5668
} else if (err) {
5769
logger.error(`Watch error: ${err?.message ?? 'Unknown'}`);

packages/js/src/utils/assets/copy-assets-handler.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,14 @@ export class CopyAssetsHandler {
171171
includeGlobalWorkspaceFiles: true,
172172
},
173173
(err, data) => {
174-
if (err === 'closed') {
175-
logger.error(`Watch error: Daemon closed the connection`);
174+
if (err === 'reconnecting') {
175+
// Silent - daemon restarts automatically on lockfile changes
176+
return;
177+
} else if (err === 'reconnected') {
178+
// Silent - reconnection succeeded
179+
return;
180+
} else if (err === 'closed') {
181+
logger.error(`Failed to reconnect to daemon after multiple attempts`);
176182
process.exit(1);
177183
} else if (err) {
178184
logger.error(`Watch error: ${err?.message ?? 'Unknown'}`);

packages/js/src/utils/watch-for-single-file-changes.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,14 @@ export async function watchForSingleFileChanges(
1111
const unregisterFileWatcher = await daemonClient.registerFileWatcher(
1212
{ watchProjects: [projectName] },
1313
(err, data) => {
14-
if (err === 'closed') {
15-
logger.error(`Watch error: Daemon closed the connection`);
14+
if (err === 'reconnecting') {
15+
// Silent - daemon restarts automatically on lockfile changes
16+
return;
17+
} else if (err === 'reconnected') {
18+
// Silent - reconnection succeeded
19+
return;
20+
} else if (err === 'closed') {
21+
logger.error(`Failed to reconnect to daemon after multiple attempts`);
1622
process.exit(1);
1723
} else if (err) {
1824
logger.error(`Watch error: ${err?.message ?? 'Unknown'}`);

packages/nx/src/command-line/graph/graph.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -820,8 +820,16 @@ function createFileWatcher() {
820820
allowPartialGraph: true,
821821
},
822822
debounce(async (error, changes) => {
823-
if (error === 'closed') {
824-
output.error({ title: `Watch error: Daemon closed the connection` });
823+
if (error === 'reconnecting') {
824+
// Silent - daemon restarts automatically on lockfile changes
825+
return;
826+
} else if (error === 'reconnected') {
827+
// Silent - reconnection succeeded
828+
return;
829+
} else if (error === 'closed') {
830+
output.error({
831+
title: `Failed to reconnect to daemon after multiple attempts`,
832+
});
825833
process.exit(1);
826834
} else if (error) {
827835
output.error({ title: `Watch error: ${error?.message ?? 'Unknown'}` });

packages/nx/src/command-line/watch/watch.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -210,13 +210,16 @@ export async function watch(args: WatchArguments) {
210210
includeGlobalWorkspaceFiles: args.includeGlobalWorkspaceFiles,
211211
},
212212
async (err, data) => {
213-
if (err === 'closed') {
213+
if (err === 'reconnecting') {
214+
// Silent - daemon restarts automatically on lockfile changes
215+
return;
216+
} else if (err === 'reconnected') {
217+
// Silent - reconnection succeeded
218+
return;
219+
} else if (err === 'closed') {
214220
output.error({
215-
title: 'Watch connection closed',
216-
bodyLines: [
217-
'The daemon has closed the connection to this watch process.',
218-
'Please restart your watch command.',
219-
],
221+
title: 'Failed to reconnect to daemon after multiple attempts',
222+
bodyLines: ['Please restart your watch command.'],
220223
});
221224
process.exit(1);
222225
} else if (err !== null) {
@@ -238,4 +241,9 @@ export async function watch(args: WatchArguments) {
238241
}
239242
);
240243
args.verbose && output.logSingleLine('watch process waiting...');
244+
245+
// Keep the process alive while watching for file changes
246+
// The file watcher callbacks will handle incoming events
247+
// The process will exit when Ctrl+C is pressed or if the connection closes
248+
await new Promise(() => {});
241249
}

0 commit comments

Comments
 (0)