Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"changes": [
{
"packageName": "@microsoft/rush",
"comment": "Add a per-iteration, host-driven runner persist policy so IPC runners can be kept hot or torn down per operation per iteration, instead of fixing persistence at plugin construction.",
"type": "minor"
}
]
}
3 changes: 3 additions & 0 deletions common/reviews/api/rush-lib.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ export interface ICobuildLockProvider {
// @alpha
export interface IConfigurableOperation extends IBaseOperationExecutionResult {
enabled: boolean;
shouldRunnerPersist: boolean;
}

// @public
Expand Down Expand Up @@ -613,6 +614,7 @@ export interface IOperationExecutionResult extends IBaseOperationExecutionResult
readonly logFilePaths: ILogFilePaths | undefined;
readonly nonCachedDurationMs: number | undefined;
readonly problemCollector: IProblemCollector;
readonly shouldRunnerPersist: boolean;
readonly silent: boolean;
readonly status: OperationStatus;
readonly stdioSummarizer: StdioSummarizer;
Expand Down Expand Up @@ -721,6 +723,7 @@ export interface IOperationRunnerContext {
createLogFile: boolean;
logFileSuffix?: string;
}): Promise<T>;
readonly shouldRunnerPersist: boolean;
status: OperationStatus;
stopwatch: IStopwatchResult;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ export interface IConfigurableOperation extends IBaseOperationExecutionResult {
* True if the operation should execute in this iteration, false otherwise.
*/
enabled: boolean;

/**
* True if the operation's runner should remain active after this iteration, false otherwise.
* Defaults to true.
Comment thread
bmiddha marked this conversation as resolved.
*/
shouldRunnerPersist: boolean;
}

/**
Expand Down Expand Up @@ -94,6 +100,10 @@ export interface IOperationExecutionResult extends IBaseOperationExecutionResult
* True if the operation should execute in this iteration, false otherwise.
*/
readonly enabled: boolean;
/**
* True if the operation's runner should remain active after this iteration, false otherwise.
*/
readonly shouldRunnerPersist: boolean;
/**
* Object tracking execution timing.
*/
Expand Down
5 changes: 5 additions & 0 deletions libraries/rush-lib/src/logic/operations/IOperationRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ export interface IOperationRunnerContext {
*/
status: OperationStatus;

/**
* True if the runner should remain active after this execution, false otherwise.
*/
readonly shouldRunnerPersist: boolean;

/**
* The environment in which the operation is being executed.
* A return value of `undefined` indicates that it should inherit the environment from the parent process.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ export interface IIPCOperationRunnerOptions {
initialCommand: string;
incrementalCommand: string | undefined;
commandForHash: string;
persist: boolean;
ignoredParameterValues: ReadonlyArray<string>;
}

Expand Down Expand Up @@ -58,7 +57,6 @@ export class IPCOperationRunner implements IOperationRunner {
private readonly _initialCommand: string;
private readonly _incrementalCommand: string | undefined;
private readonly _commandForHash: string;
private readonly _persist: boolean;
private readonly _ignoredParameterValues: ReadonlyArray<string>;

private _ipcProcess: ChildProcess | undefined;
Expand All @@ -72,7 +70,6 @@ export class IPCOperationRunner implements IOperationRunner {
initialCommand,
incrementalCommand,
commandForHash,
persist,
ignoredParameterValues
} = options;
this.name = name;
Expand All @@ -83,7 +80,6 @@ export class IPCOperationRunner implements IOperationRunner {
this._incrementalCommand = incrementalCommand;
this._commandForHash = commandForHash;

this._persist = persist;
this._ignoredParameterValues = ignoredParameterValues;
}

Expand Down Expand Up @@ -202,7 +198,6 @@ export class IPCOperationRunner implements IOperationRunner {
subProcess.on('message', finishHandler);
subProcess.on('error', reject);
subProcess.on('exit', onExit);

this._processReadyPromise!.then(() => {
isConnected = true;
terminal.writeLine('Child supports IPC protocol. Sending "run" command...');
Expand All @@ -213,7 +208,7 @@ export class IPCOperationRunner implements IOperationRunner {
}, reject);
});

if (isConnected && !this._persist) {
Comment thread
bmiddha marked this conversation as resolved.
if (isConnected && !context.shouldRunnerPersist) {
await this.closeAsync();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ export class IPCOperationRunnerPlugin implements IPhasedCommandPlugin {
initialCommand,
incrementalCommand,
commandForHash,
persist: true,
ignoredParameterValues
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ export class OperationExecutionRecord implements IOperationRunnerContext, IOpera
*/
public enabled: boolean;

/**
* If true, this operation's runner should remain active after this iteration.
*/
public shouldRunnerPersist: boolean = true;

/**
* This number represents how far away this Operation is from the furthest "root" operation (i.e.
* an operation with no consumers). This helps us to calculate the critical path (i.e. the
Expand Down
30 changes: 28 additions & 2 deletions libraries/rush-lib/src/logic/operations/OperationGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ export class OperationGraph implements IOperationGraph {
}
}

public async closeRunnersAsync(operations?: Operation[]): Promise<void> {
public async closeRunnersAsync(operations?: Iterable<Operation>): Promise<void> {
const promises: Promise<void>[] = [];
const recordMap: ReadonlyMap<Operation, OperationExecutionRecord> =
this._currentIteration?.records ?? this.resultByOperation;
Expand Down Expand Up @@ -873,9 +873,35 @@ export class OperationGraph implements IOperationGraph {
});
}

const recordsToClose: OperationExecutionRecord[] = [];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This regressed again. Runner closing must happen during the operation's own execution phase, or else we have RAM exhaustion concerns.

for (const record of executionRecords.values()) {
if (!record.shouldRunnerPersist) {
recordsToClose.push(record);
}
}
function reportRunnerCleanupFailure(record: OperationExecutionRecord, error: Error): void {
Comment thread
bmiddha marked this conversation as resolved.
record.error = error;
record.status = OperationStatus.Failure;
_reportOperationErrorIfAny(record);
state.hasAnyFailures = true;
}
if (recordsToClose.length > 0) {
try {
await this.closeRunnersAsync(recordsToClose.map((record) => record.operation));
} catch (e) {
for (const record of recordsToClose) {
reportRunnerCleanupFailure(record, e);
}
}
}
for (const record of executionRecords.values()) {
record.stdioSummarizer.close();
record.problemCollector.close();
}

const status: OperationStatus = (() => {
if (bailStatus) return bailStatus;
if (state.hasAnyFailures) return OperationStatus.Failure;
if (bailStatus) return bailStatus;
if (state.hasAnyAborted) return OperationStatus.Aborted;
if (state.hasAnyNonAllowedWarnings) return OperationStatus.SuccessWithWarning;
if (iterationContext.totalOperations === 0) return OperationStatus.NoOp;
Expand Down
Loading