fix(sessionArchiveStore): use stream-based copy for virtiofs/NFS compatibility#9
Open
ductiletoaster wants to merge 2 commits into
Open
Conversation
…atibility
`fs.copyFile` (used in `copySessionFileToArchive` and as the `EXDEV`
fallback in `moveFile`) invokes Linux's `copy_file_range(2)` syscall.
That syscall fails with `EPERM` on some filesystems — notably virtiofs
(used by Kata Containers) when the underlying export is NFS, and on
certain NFS server configurations directly. Node only falls back to
read+write on `EOPNOTSUPP` / `EXDEV`, not `EPERM`, so the error
surfaces to the caller and archive operations fail with:
Error: EPERM: operation not permitted, copyfile
'/home/node/.pi/agent/sessions/<project>/<id>.jsonl'
-> '/home/node/.pi-web/archived-sessions/<id>.jsonl'
Replace `copyFile(src, dest)` at both call sites with a small
`streamCopyFile` helper that pipes `createReadStream(src)` into
`createWriteStream(dest)` via `stream/promises.pipeline`. This forces
the userspace read+write path and works across virtiofs, NFS, and
local filesystems alike.
Throughput is slightly lower than the kernel-level copy on fast local
disks, but archived session files are small JSONL records — the
difference is unobservable in practice.
Reproduced on a Kubernetes Kata pod where `/home/node` is mounted
from an NFS-backed PVC via Kata's virtiofs daemon; `fs.copyFile`
returns EPERM, but `pipeline(createReadStream(...), createWriteStream(...))`
succeeds.
Owner
|
This approach looks reasonable to me. I tried a small follow-up on top of this PR that keeps the stream-copy approach but makes it safer: it writes to a temp file in the destination directory, then renames it into place on success. That way, if the stream copy fails, we don’t leave a partial archive file at the final path. The same-directory rename() should be metadata-only and should not hit the copy_file_range(2) path that causes the EPERM. Could you try this variant in the Kata/virtiofs/NFS environment where you reproduced the issue? |
Harmony pins the pi-web image to this fork branch via PI_WEB_FORK_REF (harmony Dockerfile). With auto: true the theme resolver follows prefers-color-scheme and renders light when the browser/OS reports light preference — undesired for the Harmony lab UI where dark is the intended look across all devices/browsers. Setting auto: false keeps pi-web-dark active regardless of the system preference. Users who want auto/light can flip the toggle in the theme picker; the change only affects the first-paint default. Fork-only — not intended upstream (upstream default of `auto: true` is the right call for a public app).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
fs.copyFile(used incopySessionFileToArchiveand as theEXDEVfallback inmoveFile) invokes Linux'scopy_file_range(2)syscall. That syscall fails withEPERMon some filesystems — notably virtiofs (used by Kata Containers) when the underlying export is NFS, and on certain NFS server configurations directly. Node only falls back to read+write onEOPNOTSUPP/EXDEV, notEPERM, so the error surfaces to the user and archive operations fail with:Reproducer
On a Kubernetes Kata Containers pod where
/home/nodeis mounted from an NFS-backed PVC via Kata's virtiofs daemon:vs. (works on the same paths):
Same behaviour reportedly hits on at least some plain NFSv4.1 server configurations where the server returns
EPERMforcopy_file_range.Change
Replace
copyFile(src, dest)at both call sites insessionArchiveStore.tswith a smallstreamCopyFilehelper that pipescreateReadStream(src)intocreateWriteStream(dest)viastream/promises.pipeline. This forces the userspace read+write path and works across virtiofs, NFS, and local filesystems alike.Throughput is slightly lower than the kernel-level copy on fast local disks, but archived session JSONL files are small — the difference is unobservable in practice.
Test plan
sessionArchiveStore.test.tsshould still pass (no behaviour change for happy-path local copies)EPERMnow succeedsHappy to add a unit test that mocks
fs.copyFileto throwEPERMand verifies the stream-based helper isn't affected, if you'd like.