Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): lock graph creation when running in another process #29408

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions docs/generated/devkit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ It only uses language primitives and immutable objects
### Classes

- [AggregateCreateNodesError](../../devkit/documents/AggregateCreateNodesError)
- [StaleProjectGraphCacheError](../../devkit/documents/StaleProjectGraphCacheError)

### Interfaces

Expand Down
144 changes: 144 additions & 0 deletions docs/generated/devkit/StaleProjectGraphCacheError.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# Class: StaleProjectGraphCacheError

## Hierarchy

- `Error`

↳ **`StaleProjectGraphCacheError`**

## Table of contents

### Constructors

- [constructor](../../devkit/documents/StaleProjectGraphCacheError#constructor)

### Properties

- [cause](../../devkit/documents/StaleProjectGraphCacheError#cause): unknown
- [message](../../devkit/documents/StaleProjectGraphCacheError#message): string
- [name](../../devkit/documents/StaleProjectGraphCacheError#name): string
- [stack](../../devkit/documents/StaleProjectGraphCacheError#stack): string
- [prepareStackTrace](../../devkit/documents/StaleProjectGraphCacheError#preparestacktrace): Function
- [stackTraceLimit](../../devkit/documents/StaleProjectGraphCacheError#stacktracelimit): number

### Methods

- [captureStackTrace](../../devkit/documents/StaleProjectGraphCacheError#capturestacktrace)

## Constructors

### constructor

• **new StaleProjectGraphCacheError**(): [`StaleProjectGraphCacheError`](../../devkit/documents/StaleProjectGraphCacheError)

#### Returns

[`StaleProjectGraphCacheError`](../../devkit/documents/StaleProjectGraphCacheError)

#### Overrides

Error.constructor

## Properties

### cause

• `Optional` **cause**: `unknown`

#### Inherited from

Error.cause

---

### message

• **message**: `string`

#### Inherited from

Error.message

---

### name

• **name**: `string`

#### Inherited from

Error.name

---

### stack

• `Optional` **stack**: `string`

#### Inherited from

Error.stack

---

### prepareStackTrace

▪ `Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any`

Optional override for formatting stack traces

**`See`**

https://v8.dev/docs/stack-trace-api#customizing-stack-traces

#### Type declaration

▸ (`err`, `stackTraces`): `any`

##### Parameters

| Name | Type |
| :------------ | :----------- |
| `err` | `Error` |
| `stackTraces` | `CallSite`[] |

##### Returns

`any`

#### Inherited from

Error.prepareStackTrace

---

### stackTraceLimit

▪ `Static` **stackTraceLimit**: `number`

#### Inherited from

Error.stackTraceLimit

## Methods

### captureStackTrace

▸ **captureStackTrace**(`targetObject`, `constructorOpt?`): `void`

Create .stack property on a target object

#### Parameters

| Name | Type |
| :---------------- | :--------- |
| `targetObject` | `object` |
| `constructorOpt?` | `Function` |

#### Returns

`void`

#### Inherited from

Error.captureStackTrace
8 changes: 7 additions & 1 deletion docs/generated/devkit/readCachedProjectGraph.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
# Function: readCachedProjectGraph

▸ **readCachedProjectGraph**(): [`ProjectGraph`](../../devkit/documents/ProjectGraph)
▸ **readCachedProjectGraph**(`minimumComputedAt?`): [`ProjectGraph`](../../devkit/documents/ProjectGraph)

Synchronously reads the latest cached copy of the workspace's ProjectGraph.

#### Parameters

| Name | Type | Description |
| :------------------- | :------- | :----------------------------------------------------------------------------- |
| `minimumComputedAt?` | `number` | The minimum timestamp that the cached ProjectGraph must have been computed at. |

#### Returns

[`ProjectGraph`](../../devkit/documents/ProjectGraph)
Expand Down
1 change: 1 addition & 0 deletions docs/generated/packages/devkit/documents/nx_devkit.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ It only uses language primitives and immutable objects
### Classes

- [AggregateCreateNodesError](../../devkit/documents/AggregateCreateNodesError)
- [StaleProjectGraphCacheError](../../devkit/documents/StaleProjectGraphCacheError)

### Interfaces

Expand Down
2 changes: 1 addition & 1 deletion packages/devkit/src/executors/parse-target-string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export function parseTargetString(
targetString: string,
projectGraphOrCtx?: ProjectGraph | ExecutorContext
): Target {
let projectGraph =
let projectGraph: ProjectGraph =
projectGraphOrCtx && 'projectGraph' in projectGraphOrCtx
? projectGraphOrCtx.projectGraph
: (projectGraphOrCtx as ProjectGraph);
Expand Down
2 changes: 2 additions & 0 deletions packages/nx/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,5 @@ assert_fs = "1.0.10"
# This is only used for unit tests
swc_ecma_dep_graph = "0.109.1"
tempfile = "3.13.0"
# We only explicitly use tokio for async tests
tokio = "1.38.0"
22 changes: 2 additions & 20 deletions packages/nx/bin/nx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { performance } from 'perf_hooks';
import { setupWorkspaceContext } from '../src/utils/workspace-context';
import { daemonClient } from '../src/daemon/client/client';
import { removeDbConnections } from '../src/utils/db-connection';
import { signalToCode } from '../src/utils/exit-codes';
import { registerCleanupFn } from '../src/utils/cleanup';

function main() {
if (
Expand Down Expand Up @@ -276,26 +276,8 @@ const getLatestVersionOfNx = ((fn: () => string) => {
return () => cache || (cache = fn());
})(_getLatestVersionOfNx);

function nxCleanup(signal?: NodeJS.Signals) {
registerCleanupFn(() => {
removeDbConnections();
if (signal) {
process.exit(signalToCode(signal));
} else {
process.exit();
}
}

process.on('exit', () => {
nxCleanup();
});
process.on('SIGINT', () => {
nxCleanup('SIGINT');
});
process.on('SIGTERM', () => {
nxCleanup('SIGTERM');
});
process.on('SIGHUP', () => {
nxCleanup('SIGHUP');
});

main();
12 changes: 4 additions & 8 deletions packages/nx/src/command-line/graph/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import { ConfigurationSourceMaps } from '../../project-graph/utils/project-confi
import { createTaskHasher } from '../../hasher/create-task-hasher';
import { ProjectGraphError } from '../../project-graph/error-types';
import { isNxCloudUsed } from '../../utils/nx-cloud-utils';
import { registerCleanupFn } from '../../utils/cleanup';

export interface GraphError {
message: string;
Expand Down Expand Up @@ -667,14 +668,9 @@ async function startServer(
}
});

const handleTermination = async (exitCode: number) => {
if (unregisterFileWatcher) {
unregisterFileWatcher();
}
process.exit(exitCode);
};
process.on('SIGINT', () => handleTermination(128 + 2));
process.on('SIGTERM', () => handleTermination(128 + 15));
if (unregisterFileWatcher) {
registerCleanupFn(() => unregisterFileWatcher);
}

return new Promise<{ app: Server; url: URL }>((res) => {
app.listen(port, host, () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,12 @@ async function processFilesAndCreateAndSerializeProjectGraph(
};
}
}

writeCache(
g.projectFileMapCache,
g.projectGraph,
projectConfigurationsResult.sourceMaps,
errors
);
if (errors.length > 0) {
return {
error: new DaemonProjectGraphError(
Expand All @@ -316,7 +321,6 @@ async function processFilesAndCreateAndSerializeProjectGraph(
serializedSourceMaps: null,
};
} else {
writeCache(g.projectFileMapCache, g.projectGraph);
return g;
}
} catch (err) {
Expand Down
5 changes: 4 additions & 1 deletion packages/nx/src/devkit-exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ export type {
ProjectsMetadata,
} from './project-graph/plugins';

export { AggregateCreateNodesError } from './project-graph/error-types';
export {
AggregateCreateNodesError,
StaleProjectGraphCacheError,
} from './project-graph/error-types';

export { createNodesFromFiles } from './project-graph/plugins';

Expand Down
38 changes: 7 additions & 31 deletions packages/nx/src/executors/run-commands/run-commands.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import {
PseudoTerminal,
PseudoTtyProcess,
} from '../../tasks-runner/pseudo-terminal';
import { signalToCode } from '../../utils/exit-codes';
import {
loadAndExpandDotEnvFile,
unloadDotEnvFile,
} from '../../tasks-runner/task-env';
import { registerCleanupFn } from '../../utils/cleanup';

export const LARGE_BUFFER = 1024 * 1000000;
let pseudoTerminal: PseudoTerminal | null;
Expand Down Expand Up @@ -640,40 +640,16 @@ function registerProcessListener() {
});
});

// Terminate any task processes on exit
process.on('exit', () => {
registerCleanupFn((signal) => {
childProcesses.forEach((p) => {
if ('connected' in p ? p.connected : p.isAlive) {
p.kill();
}
});
});
process.on('SIGINT', () => {
childProcesses.forEach((p) => {
if ('connected' in p ? p.connected : p.isAlive) {
p.kill('SIGTERM');
}
});
// we exit here because we don't need to write anything to cache.
process.exit(signalToCode('SIGINT'));
});
process.on('SIGTERM', () => {
childProcesses.forEach((p) => {
if ('connected' in p ? p.connected : p.isAlive) {
p.kill('SIGTERM');
}
});
// no exit here because we expect child processes to terminate which
// will store results to the cache and will terminate this process
});
process.on('SIGHUP', () => {
childProcesses.forEach((p) => {
if ('connected' in p ? p.connected : p.isAlive) {
p.kill('SIGTERM');
if (signal) {
p.kill(signal);
} else {
p.kill();
}
}
});
// no exit here because we expect child processes to terminate which
// will store results to the cache and will terminate this process
});
}

Expand Down
9 changes: 9 additions & 0 deletions packages/nx/src/native/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ export declare class ChildProcess {
onOutput(callback: (message: string) => void): void
}

export declare class FileLock {
locked: boolean
constructor(lockFilePath: string)
unlock(): void
check(): boolean
wait(): Promise<void>
lock(): void
}

export declare class HashPlanner {
constructor(nxJson: NxJson, projectGraph: ExternalObject<ProjectGraph>)
getPlans(taskIds: Array<string>, taskGraph: TaskGraph): Record<string, string[]>
Expand Down
1 change: 1 addition & 0 deletions packages/nx/src/native/native-bindings.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ if (!nativeBinding) {
}

module.exports.ChildProcess = nativeBinding.ChildProcess
module.exports.FileLock = nativeBinding.FileLock
module.exports.HashPlanner = nativeBinding.HashPlanner
module.exports.ImportResult = nativeBinding.ImportResult
module.exports.NxCache = nativeBinding.NxCache
Expand Down
Loading
Loading