Skip to content

Commit d66b663

Browse files
authored
fix(core): copy native file atomically to avoid hanging graph creation (#30695)
<!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior <!-- This is the behavior we have today --> When copying the native file out of node_modules into tmp from multiple processes (aka plugin workers)... this can result in Node deadlocking. ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> Copying the native file out of node_modules into tmp from multiple processes will first copy to unique places in `/tmp` but then atomically rename to the final location where it is still loaded from. This at least fixes a flaky test in `nx:test` where the explicit dependencies tests were timing out after 35 seconds ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #
1 parent b56df7b commit d66b663

File tree

4 files changed

+19
-5
lines changed

4 files changed

+19
-5
lines changed

packages/nx/src/config/nx-json.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { existsSync } from 'fs';
22
import { dirname, join } from 'path';
33

44
import type { ChangelogRenderOptions } from '../../release/changelog-renderer';
5-
import { validReleaseVersionPrefixes } from '../command-line/release/version';
5+
import type { validReleaseVersionPrefixes } from '../command-line/release/version';
66
import { readJsonFile } from '../utils/fileutils';
77
import type { PackageManager } from '../utils/package-manager';
88
import { workspaceRoot } from '../utils/workspace-root';

packages/nx/src/native/index.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const { join, basename } = require('path');
2-
const { copyFileSync, existsSync, mkdirSync } = require('fs');
2+
const { copyFileSync, existsSync, mkdirSync, renameSync } = require('fs');
33
const Module = require('module');
44
const { nxVersion } = require('../utils/versions');
55
const { getNativeFileCacheLocation } = require('./native-file-cache-location');
@@ -71,14 +71,28 @@ Module._load = function (request, parent, isMain) {
7171

7272
// we copy the file to a workspace-scoped tmp directory and prefix with nxVersion to avoid stale files being loaded
7373
const nativeFileCacheLocation = getNativeFileCacheLocation();
74+
// This is a path to copy to, not the one that gets loaded
75+
const tmpTmpFile = join(
76+
nativeFileCacheLocation,
77+
nxVersion + '-' + Math.random() + fileName
78+
);
79+
// This is the path that will get loaded
7480
const tmpFile = join(nativeFileCacheLocation, nxVersion + '-' + fileName);
81+
82+
// If the file to be loaded already exists, just load it
7583
if (existsSync(tmpFile)) {
7684
return originalLoad.apply(this, [tmpFile, parent, isMain]);
7785
}
7886
if (!existsSync(nativeFileCacheLocation)) {
7987
mkdirSync(nativeFileCacheLocation, { recursive: true });
8088
}
81-
copyFileSync(nativeLocation, tmpFile);
89+
// First copy to a unique location for each process
90+
copyFileSync(nativeLocation, tmpTmpFile);
91+
92+
// Then rename to the final location
93+
renameSync(tmpTmpFile, tmpFile);
94+
95+
// Load from the final location
8296
return originalLoad.apply(this, [tmpFile, parent, isMain]);
8397
} else {
8498
// call the original _load function for everything else

packages/nx/src/plugins/package-json/create-nodes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { existsSync } from 'node:fs';
33
import { dirname, join } from 'node:path';
44

55
import { NxJsonConfiguration, readNxJson } from '../../config/nx-json';
6-
import { ProjectConfiguration } from '../../config/workspace-json-project-json';
6+
import type { ProjectConfiguration } from '../../config/workspace-json-project-json';
77
import { toProjectName } from '../../config/to-project-name';
88
import { readJsonFile, readYamlFile } from '../../utils/fileutils';
99
import { combineGlobPatterns } from '../../utils/globs';

packages/nx/src/project-graph/plugins/load-resolved-plugin.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { PluginConfiguration } from '../../config/nx-json';
22
import { LoadedNxPlugin } from './loaded-nx-plugin';
3-
import { NxPlugin } from './public-api';
3+
import type { NxPlugin } from './public-api';
44

55
export async function loadResolvedNxPluginAsync(
66
pluginConfiguration: PluginConfiguration,

0 commit comments

Comments
 (0)