fix(nodevm): share npm modules - #9170
Conversation
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueThanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
CLI Test Results (Linux) 1 files 223 suites 48s ⏱️ Results for commit 81d2f50. ♻️ This comment has been updated with latest results. |
df37a33 to
c1c1ce7
Compare
|
@sid-bruno as requested, I ran this branch against the real 2,170-request collection from #9074 (16 top-level folders, collection-level scripts requiring 1. Breaking change: skipped requests no longer resolve with
|
| module | classification | why |
|---|---|---|
moment |
SENSITIVE → per-context | its own source uses res, test, console as identifiers (deprecation warnings use console.warn) |
@faker-js/faker |
SENSITIVE → per-context | poisoned transitively via dist/chunk-ZKNYQOPP.cjs |
nanoid |
shared | clean |
So the two packages that caused #9074 are exactly the ones the heuristic cannot share. Minified/bundled dists make this common — almost any non-trivial package mentions console, test, assert or res somewhere in its tree. With the rename from §1 reverted locally so the collection actually passes, the flag-on run gives: 2170/2170 passed, peak 2.53 GB RSS, 345 s — functional, but ~3× the memory and ~1.7× the runtime of the original #9078 approach on the same run, almost entirely because faker/moment keep getting re-evaluated per script.
4. One leaked socket per request (pre-existing, #9079)
Both runs held one open TCP connection per executed request (~1,900 sockets open near the end) — that's the throwaway keep-alive agent issue from #9079, orthogonal to this branch but worth remembering for CI boxes with a 1,024 fd limit.
Comparison on this collection (same targets, same machine)
| build | flag | result | peak runner RSS |
|---|---|---|---|
| 4.0.0 (baseline) | — | 2170/2170 | 9.5 GB |
| #9078 as merged+reworked → this branch | off | 926/2170 (§1) | 20.6 GB |
| this branch | --shared-script-modules |
960/2170 (§1) | 1.7 GB (not comparable, most requests failed early) |
| this branch + §1 rename reverted | --shared-script-modules |
2170/2170 | 2.53 GB |
| #9078 original (ALS + facades) | — | 2170/2170 | 0.9 GB |
| 3.0.3 | — | 2170/2170 | 2.0 GB |
Happy to re-run any variant (or a tweaked classifier) against this collection — turnaround is ~10 minutes.
Good catch, thanks, this is from #9080 so that's out of whatever I was changing but we'll get to it as well
Hmm, this would need a deeper check what all got added into the CLI process
A more robust one would end up needing a lot more analysis and the
The |
|
Thanks for the detailed follow-up! On (3), completely your call — one clarification on what the merged #9078 approach actually risks, since "identity" covers a few different things: the facades keep On (4): #9079 already implements exactly that cleanup phase — throwaway agents destroyed once the response is in (success, error, and before each redirect hop re-creates them, OAuth2 token agents included), keep-alive preserved on the wire, with an integration test that fails on main (6 sockets open at the 6th request) and passes with the fix. Feel free to take it as-is or fold it in here. And whenever a build with the skip-status fix is up, say the word — the collection run takes me ~10 minutes. |
fair enough
Accidental breaking implementation from #9080, shouldn't have caused it, I wouldn't want to fix it in this PR causing a scope leak in the PR but have informed this internally for it to be addressed |
|
Makes sense — and confirmed |
8cda0e6 to
7622ec2
Compare
|
Re-ran the 2,170-request collection against the current head (7622ec2, the AsyncLocalStorage + facade approach): 2170/2170 passed, peak runner RSS 1.08 GB — the memory regression is gone and the skip-status break is resolved. 👍 The one remaining thing this run shows is the socket side: it held ~2,178 open connections (one per request) for the whole run — i.e. #9079 is still needed on top of this. Not a blocker for this PR, just confirming the two are independent as expected. Nice work on the rework! node-vm unit suite is also green (63/63) with the primitive-value guard you added. |
|
I'm still looking into ways to fix that without having to do the scan that I was doing previously |
|
Reproduced Why it failsThe failing assertion is The old static scan solved this by detecting such modules, but (as we saw earlier) it also mis-flags faker/moment on incidental identifier matches, so they lose sharing. An approach that avoids the scan: detect the touch at runtimeInstead of scanning source, watch whether a module actually reads a Bruno global while it is loading. The facade getter is the single choke point, so it's one extra line there plus a load-stack:
faker/moment never read Results on my 2,170-request collection
So the correctness fix costs ~nothing in memory — faker/moment stay shared. Prototype branch (on top of your head, one file changed): dgyesbreghs@40f4a8e — take it, adapt it, or ignore it. Glad to iterate or re-benchmark any variant. |
|
A mark and re-evaluate is the same thing I'm going through, it does reduce the overall heap but might still be a tad bit larger than what was in 3.0.3, I'll push the implementation in a bit, should be able to get better numbers post that |
Description
TBD
Notes:
bru instanceof Objectreturnsfalseonmainbecause of cross-realm object (VMs), ALS facades don’t cause that, and in the shared host context they can flip it totrue, so identity(===) /instanceofchecks diverge from existing behaviour.Contribution Checklist:
Note: Keeping the PR small and focused helps make it easier to review and merge. If you have multiple changes you want to make, please consider submitting them as separate pull requests.
Publishing to New Package Managers
Please see here for more information.