Skip to content

Commit ae8073b

Browse files
mojazamojazayeriCopilot
authored
[rush-daemon][WS2.3] Add warm engine component factory (#5949)
* [rush-daemon][WS2.3] Add warm engine component factory Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * [rush-daemon] Fix warm engine test project identity Build session integration operations from the freshly loaded Rush configuration and cover serialized concurrent reconciliation. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: mojaza <mojazayeri@users.noreply.github.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 5d2f03f commit ae8073b

10 files changed

Lines changed: 1537 additions & 10 deletions
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@rushstack/rush-daemon",
5+
"comment": "Add an opt-in all-project engine component factory with explicit phase/plugin shape, retained invalidation reconciliation, fail-closed graph recreation boundaries, and a deterministic engine shutdown contract.",
6+
"type": "minor"
7+
}
8+
],
9+
"packageName": "@rushstack/rush-daemon",
10+
"email": "mojazayeri@users.noreply.github.com"
11+
}

common/reviews/api/rush-daemon.api.md

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,36 @@
66

77
/// <reference types="node" />
88

9+
import type { GetInputsSnapshotAsyncFn } from '@microsoft/rush-lib';
910
import type { IDaemonPaths } from '@rushstack/rush-daemon-transport';
1011
import type { IInputsSnapshot } from '@microsoft/rush-lib';
1112
import type { IOperationGraph } from '@microsoft/rush-lib';
13+
import type { Operation } from '@microsoft/rush-lib';
1214
import { RushConfiguration } from '@microsoft/rush-lib';
15+
import type { RushConfigurationProject } from '@microsoft/rush-lib';
1316
import type { RushSession } from '@microsoft/rush-lib';
1417

18+
// @beta
19+
export type CreateWorkspaceEngineComponentsAsync = (options: ICreateWorkspaceEngineComponentsOptions) => Promise<IWorkspaceEngineComponents>;
20+
1521
// @beta
1622
export type CreateWorkspaceSessionComponentsAsync = (options: ICreateWorkspaceSessionComponentsOptions) => Promise<IWorkspaceSessionComponents>;
1723

24+
// @beta
25+
export interface IClassifyWorkspaceInvalidationsOptions {
26+
// (undocumented)
27+
readonly changedPaths: ReadonlyArray<string>;
28+
// (undocumented)
29+
readonly rushConfiguration: RushConfiguration;
30+
}
31+
32+
// @beta
33+
export interface ICreateWorkspaceEngineComponentsOptions extends IWorkspaceEngineShape {
34+
readonly projectSelection: ReadonlySet<RushConfigurationProject>;
35+
// (undocumented)
36+
readonly rushConfiguration: RushConfiguration;
37+
}
38+
1839
// @beta
1940
export interface ICreateWorkspaceSessionComponentsOptions {
2041
// (undocumented)
@@ -25,6 +46,18 @@ export interface ICreateWorkspaceSessionComponentsOptions {
2546
readonly rushConfiguration: RushConfiguration;
2647
}
2748

49+
// @beta
50+
export interface IMapWorkspaceInvalidationsOptions {
51+
// (undocumented)
52+
readonly changedPaths: ReadonlyArray<string>;
53+
// (undocumented)
54+
readonly currentInputsSnapshot: IInputsSnapshot;
55+
// (undocumented)
56+
readonly nextInputsSnapshot: IInputsSnapshot;
57+
// (undocumented)
58+
readonly operationGraph: IOperationGraph;
59+
}
60+
2861
// @public
2962
export interface IRequestLease {
3063
// (undocumented)
@@ -58,6 +91,54 @@ export interface IRushDaemonServeOptions extends IRushDaemonHostOptions {
5891
readonly shutdownSignal?: AbortSignal;
5992
}
6093

94+
// @beta
95+
export type IsWorkspaceEngineRecreationRequiredAsync = (options: IClassifyWorkspaceInvalidationsOptions) => Promise<boolean>;
96+
97+
// @beta
98+
export interface IWorkspaceEngineComponentFactoryOptions {
99+
// (undocumented)
100+
readonly createEngineComponentsAsync: CreateWorkspaceEngineComponentsAsync;
101+
// (undocumented)
102+
readonly isEngineRecreationRequiredAsync?: IsWorkspaceEngineRecreationRequiredAsync;
103+
// (undocumented)
104+
readonly mapInvalidationsToOperationsAsync: MapWorkspaceInvalidationsToOperationsAsync;
105+
// (undocumented)
106+
readonly shape: IWorkspaceEngineShape;
107+
}
108+
109+
// @beta
110+
export interface IWorkspaceEngineComponents extends AsyncDisposable {
111+
[Symbol.asyncDispose](): Promise<void>;
112+
// (undocumented)
113+
readonly getInputsSnapshotAsync: GetInputsSnapshotAsyncFn;
114+
// (undocumented)
115+
readonly inputsSnapshot: IInputsSnapshot;
116+
// (undocumented)
117+
readonly operationGraph: IOperationGraph;
118+
// (undocumented)
119+
readonly rushSession: RushSession;
120+
}
121+
122+
// @beta
123+
export interface IWorkspaceEngineShape {
124+
// (undocumented)
125+
readonly phaseNames: ReadonlyArray<string>;
126+
// (undocumented)
127+
readonly pluginNames: ReadonlyArray<string>;
128+
}
129+
130+
// @beta
131+
export interface IWorkspaceInvalidationReconciliation {
132+
// (undocumented)
133+
readonly inputsSnapshot: IInputsSnapshot;
134+
// (undocumented)
135+
readonly invalidatedOperationCount: number;
136+
// (undocumented)
137+
readonly isFullInvalidation: boolean;
138+
// (undocumented)
139+
readonly sequence: number;
140+
}
141+
61142
// @beta
62143
export interface IWorkspaceInvalidationSnapshot {
63144
readonly changedPaths: ReadonlyArray<string>;
@@ -74,6 +155,8 @@ export interface IWorkspaceInvalidationWatcher extends AsyncDisposable {
74155

75156
// @beta
76157
export interface IWorkspaceSession extends AsyncDisposable {
158+
// (undocumented)
159+
readonly engineShape: IWorkspaceEngineShape | undefined;
77160
// (undocumented)
78161
readonly inputsSnapshot: IInputsSnapshot | undefined;
79162
// (undocumented)
@@ -83,19 +166,25 @@ export interface IWorkspaceSession extends AsyncDisposable {
83166
// (undocumented)
84167
readonly operationGraph: IOperationGraph | undefined;
85168
// (undocumented)
169+
reconcileInvalidationsAsync(): Promise<IWorkspaceInvalidationReconciliation | undefined>;
170+
// (undocumented)
86171
readonly rushConfiguration: RushConfiguration;
87172
// (undocumented)
88173
readonly rushSession: RushSession | undefined;
89174
}
90175

91176
// @beta
92177
export interface IWorkspaceSessionComponents extends AsyncDisposable {
178+
// (undocumented)
179+
readonly engineShape?: IWorkspaceEngineShape;
93180
// (undocumented)
94181
readonly inputsSnapshot?: IInputsSnapshot;
95182
// (undocumented)
96183
readonly operationGraph?: IOperationGraph;
97184
readonly projectWatcher?: IWorkspaceInvalidationWatcher;
98185
// (undocumented)
186+
readonly reconcileInvalidationsAsync?: () => Promise<IWorkspaceInvalidationReconciliation>;
187+
// (undocumented)
99188
readonly rushSession?: RushSession;
100189
}
101190

@@ -125,6 +214,9 @@ export interface IWorkspaceSessionOptions {
125214
readonly rushVersion: string;
126215
}
127216

217+
// @beta
218+
export type MapWorkspaceInvalidationsToOperationsAsync = (options: IMapWorkspaceInvalidationsOptions) => Promise<Iterable<Operation>>;
219+
128220
// @public
129221
export enum RequestExclusivityClass {
130222
// (undocumented)
@@ -171,11 +263,29 @@ export class RushDaemonHost {
171263
// @beta
172264
export function serveRushDaemonAsync(options: IRushDaemonServeOptions): Promise<void>;
173265

266+
// @beta
267+
export class WorkspaceEngineComponentFactory {
268+
constructor(options: IWorkspaceEngineComponentFactoryOptions);
269+
// (undocumented)
270+
readonly createAsync: CreateWorkspaceSessionComponentsAsync;
271+
// (undocumented)
272+
readonly shape: IWorkspaceEngineShape;
273+
}
274+
275+
// @beta
276+
export class WorkspaceEngineRecreationRequiredError extends Error {
277+
constructor();
278+
}
279+
174280
// @beta
175281
export class WorkspaceInvalidationTracker {
176282
acknowledgeThrough(sequence: number): void;
177283
getSnapshot(): IWorkspaceInvalidationSnapshot;
284+
// @internal (undocumented)
285+
get hasUnattributedUnknownChanges(): boolean;
178286
invalidate(changedPath?: string): void;
287+
// @internal (undocumented)
288+
invalidateForInitialization(): void;
179289
markWatcherUnhealthy(): void;
180290
}
181291

@@ -184,13 +294,16 @@ export class WorkspaceSession implements IWorkspaceSession {
184294
[Symbol.asyncDispose](): Promise<void>;
185295
static createAsync(options: IWorkspaceSessionOptions): Promise<WorkspaceSession>;
186296
// (undocumented)
187-
readonly inputsSnapshot: IInputsSnapshot | undefined;
297+
get engineShape(): IWorkspaceEngineShape | undefined;
298+
// (undocumented)
299+
get inputsSnapshot(): IInputsSnapshot | undefined;
188300
// (undocumented)
189301
readonly invalidations: WorkspaceInvalidationTracker;
190302
// (undocumented)
191303
readonly metadata: IWorkspaceSessionMetadata;
192304
// (undocumented)
193305
readonly operationGraph: IOperationGraph | undefined;
306+
reconcileInvalidationsAsync(): Promise<IWorkspaceInvalidationReconciliation | undefined>;
194307
// (undocumented)
195308
readonly rushConfiguration: RushConfiguration;
196309
// (undocumented)

libraries/rush-daemon/README.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,18 @@ The host loads `RushConfiguration` once before signaling readiness and keeps a h
1212
active for the daemon lifetime. Its invalidation tracker retains changes while no clients are
1313
connected so a later request can reconcile them. The tracker starts with a conservative unknown
1414
invalidation covering session startup, and excessive distinct paths are compacted into the same
15-
full-workspace signal. Reusable operation graph, plugin, and input snapshot state can be supplied
16-
through the session component factory; the default session does not construct those command-specific
17-
resources while the reusable runner lifetime tracked by
18-
[rushstack#5895](https://github.com/microsoft/rushstack/issues/5895) remains incomplete.
15+
full-workspace signal.
16+
17+
`WorkspaceEngineComponentFactory` provides the opt-in seam for a command integration to supply a real
18+
all-project operation graph, its `RushSession`, and a refreshable inputs snapshot. The integration must
19+
declare the complete phase and plugin shape because Rush plugins can currently vary that shape by command.
20+
The factory validates graph ownership, serializes retained invalidation reconciliation, and maps path-specific
21+
changes through the integration. The engine owner must supply one deterministic async disposer because
22+
`IOperationGraph` does not yet expose an operation that both stops the lifetime and awaits runner cleanup.
23+
After the initial conservative startup reconciliation, changes to Rush configuration, project package manifests,
24+
or integration-classified plugin graph inputs fail closed with `WorkspaceEngineRecreationRequiredError` before
25+
the input baseline advances or the invalidation is acknowledged. The startup watcher-registration boundary has
26+
no paths to classify and therefore remains a full invalidation. The routing layer must replace the complete
27+
workspace session rather than run a stale graph.
28+
The default daemon executable does not construct or route this graph while the command-independent plugin shape and per-iteration runner
29+
lifetime tracked by [rushstack#5895](https://github.com/microsoft/rushstack/issues/5895) remain incomplete.

0 commit comments

Comments
 (0)