Repository navigation
[rush] rush-cli-client: every invocation loads all of @microsoft/rush-lib (~600 ms) before connecting to the daemon #6054
Description
Summary
With a warm daemon, rush-client daemon status takes 766 ms (median of 20 runs), while node -e 0 takes 40 ms and the socket connect + hello + pong takes about 7 ms. About 600 ms is spent require-ing @microsoft/rush-lib (its webpack bundle plus about 1,263 external modules) before the socket is opened. The same overhead applies to every warm rush-client build, and it dominates the IPC path, which is fast. A lean prototype that runs the same status call through @rushstack/rush-client-core without rush-lib takes 182 ms.
Repro steps
node -e 0 # ~40 ms
node -e "require('@microsoft/rush-lib')" # ~685 ms
rush-client daemon status # ~766 ms with a warm daemon
node -r <timing hook> rush-client build # socket connect happens at 660-710 ms; connect +1.5 ms; first daemon data +2 ms
Expected result: The client connect path only loads what it needs (transport, protocol, client-core, and a small rush.json lookup), and rush-lib loads lazily only for fallback, rushx, or auto-start.
Actual result: Every invocation pays the full engine load first.
Details
Import costs, measured in fresh processes: @microsoft/rush-lib 574-594 ms (+1,263 modules). @rushstack/rush-client-core alone 90-120 ms (+236 modules), because StartupLock.ts:6 imports LockFile from the @rushstack/node-core-library index (93 ms, 172 modules). Transport + protocol 35 ms (55 modules).
Root cause (main @ 60007c9):
apps/rush-cli-client/src/launchClient.ts:3-9: a top-levelimport { Rush, RushConfiguration, daemonEnvironmentVariables, resolveDaemonConfiguration } from '@microsoft/rush-lib', used only fortryFindRushJsonLocation,Rush.versionandresolveDaemonConfiguration.routing.ts:5: importsRushXCommandfrom rush-lib (needed only for rushx).launchClient.ts:25-26: import@microsoft/rush/lib/MinimalRushConfigurationand@rushstack/rush-daemon/lib/VersionSelectedDaemonLauncher, each of which transitively loads rush-lib.daemonConnectionOptions.ts:6, 10-16does the same.libraries/rush-client-core/src/StartupLock.ts:6: eager node-core-library index import.
Suggested fix: make the connect path free of rush-lib. Inline the rush.json lookup and version read, deep-import the daemon configuration resolver, and load rush-lib, MinimalRushConfiguration, VersionSelectedDaemonLauncher and RushXCommand lazily only on fallback, rushx, or auto-start paths. Lazy-load LockFile in rush-client-core. Add a startup-budget test (for example, assert that the rush-lib bundle is not in require.cache after a status call). Expected result: daemon status goes from 766 ms to roughly 90-180 ms, and warm no-op builds get about 600 ms faster.
This was found during an automated performance/behavior analysis of rush-client/rushd on Linux and independently reproduced (751 ms median status versus 34 ms for node -e 0).
Standard questions
| Question | Answer |
|---|---|
@microsoft/rush globally installed version? |
built from main @ 60007c9 (5.179.0) |
rushVersion from rush.json? |
5.179.0 |
pnpmVersion, npmVersion, or yarnVersion from rush.json? |
pnpm@10.27.0 |
(if pnpm) useWorkspaces from pnpm-config.json? |
true |
| Operating system? | Linux (WSL2 Ubuntu 24.04) |
| Would you consider contributing a PR? | Yes |
Node.js version (node -v)? |
22.23.2 |
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsClosed
Activity
[rush-cli-client] Connect to a warm daemon without loading @microsoft…