fix(server-s3): transcript snapshot copies asynchronously (F42 follow-up)

copyIfNewer read/wrote a member file of up to 256 MiB with readSync/writeSync,
blocking the event loop for every user. It now copies with fs.promises 1 MiB
at a time from the runAs.openSafeAsync handle (owner/type check kept), keeps
the 256 MiB member cap and zero-run holes, writes a temp file and renames it
into place, and joins a copy already in flight for the same destination.
A failed copy is logged and never rejects; snapshotTranscript is async and
all three import callers await it.
This commit is contained in:
2026-10-09 15:17:55 +07:00
parent 84e2a9f0ac
commit 3ab750c765
6 changed files with 130 additions and 33 deletions
+3 -1
View File
@@ -574,7 +574,9 @@ a pane really is. Each case below is refused or bounded, with a test in
a whole member file is read only up to a cap (1 MiB for a login or a
settings file, 16 MiB for an index or a workflow journal, 256 MiB for a
SQLite copy, 64 MiB otherwise); a member transcript over 256 MiB is neither
parsed nor snapshotted, and the snapshot keeps a sparse file's holes.
parsed nor snapshotted, and the snapshot keeps a sparse file's holes. The
snapshot copy is asynchronous (1 MiB at a time), so a big one never stalls
the dashboard for other users, and it lands by rename — never half-written.
- **A request that makes a route throw** answers 500 instead of stopping the
server (a member folder of a gone user crashed `POST /api/lanes/worktree`).
- **Who a pane runs as.** A non-admin owns no workspace outside their home