fix: fall back to copy when hard links fail in pnpm linker#7067
fix: fall back to copy when hard links fail in pnpm linker#7067VforVitality wants to merge 3 commits intoyarnpkg:masterfrom
Conversation
| if (shouldFallbackToCopy(err)) { | ||
| const content = await destinationFs.readFilePromise(indexPath); | ||
| await destinationFs.writeFilePromise(destination, content); | ||
| if (sourceMode !== defaultMode) |
There was a problem hiding this comment.
Missing curly bracket
| if (sourceMode !== defaultMode) | |
| if (sourceMode !== defaultMode) { |
| import {NodeFS} from '../sources/NodeFS'; | ||
| import {xfs, PortablePath, ppath, Filename, npath} from '../sources'; |
There was a problem hiding this comment.
Linter fix
| import {NodeFS} from '../sources/NodeFS'; | |
| import {xfs, PortablePath, ppath, Filename, npath} from '../sources'; | |
| import {NodeFS} from '../sources/NodeFS'; | |
| import {xfs, ppath, Filename} from '../sources'; |
|
@VforVitality it would be nice to get this done. It would probably fix an issue I see when hard linking files from outside of a docker into the image. |
bd34268 to
98ae0a7
Compare
|
Hi @tobiasweibel The thing is that we started using a 'same-way-patched' yarn (4.7.0 in our case) and even though it works with the fallback, we experienced some performance degradation on ntfs. So I went deeper with my research and turned out that I'm only not sure if this improvement should be a part of this PR? Anyone, any opinion? Shall it just fix the issue or also to try to improve performance a bit? |
When using nodeLinker: pnpm, the content-addressable index creates hard links from the index to node_modules/.store/. This fails in several scenarios: - EXDEV: index and project on different volumes (common in Docker, CI, and Windows multi-drive setups) - EMLINK: inode hard link limit exceeded (Linux/macOS) - UNKNOWN (link syscall): NTFS hard link limit of 1024 per file (Windows); libuv does not map ERROR_TOO_MANY_LINKS to EMLINK - EPERM (link syscall): insufficient privileges for hard links This commit adds a shouldFallbackToCopy() helper that detects these errors, and wraps the linkPromise call in copyFileViaIndex() with a try/catch that gracefully degrades to copying the file content. This matches the behavior of the real pnpm CLI. Fixes yarnpkg#5326
98ae0a7 to
e311aff
Compare
Co-authored-by: Tobias Weibel <tobias.weibel@autodesk.com>
Rather than a threshold, perhaps we could just feature-detect it before starting the link itself? |
|
I don't like that this happens silently. Copying instead of hardlinking defeats one of the main benefits of the I think this should be at least gated behind a configuration |
What would be default behavior then? When configuration is not provided, shall
I haven't found a better solution. Threshold is also not that good, it tries to predict the future and there are no guarantee. |
Yes. That's the existing behavior. Maybe with an added note about the config in the error message.
"Very specific files" here includes "all files on a different drive from the global CAS". Imagine a user switching to the pnpm linker because it "saves disk space", then finding out 10 projects later that it saved no space at all because they put all their projects on a different drive. |
I cannot agree. Current behavior is a bug. We try to fix it. Why shall we behave as bug?
Well, this might be the case. At maximum, I think we could put a message to the console. Shall we? Btw in our case, how we landed down here: we wanted to use pnpm linked to detect phantom deps (not to save space). Because of cross-device links issue, we just disabled yarn's global cache. It's not obvious, but works well :) |
Summary
When using
odeLinker: pnpm, the content-addressable index creates hard links from the global index to
ode_modules/.store/. This fails in several common scenarios:
This PR adds graceful degradation: when \linkPromise\ fails with any of these errors, the file content is copied from the index instead. This matches the behavior of the real pnpm CLI, which also falls back to copying.
Changes
Testing
Unit tests
All 8 test suites pass (104 passed, 5 platform-skipped):
\
yarn test:unit yarnpkg-fslib # ✅ 8 passed, 0 failed
\\
Real-world validation
Tested against a large monorepo (~40 workspaces) with @mui/icons-material\ (~17,000 .d.ts\ files with identical content hashing to the same index entry):
Fixes #5326