fix(perf): stop loading hard-link-directory on every bit invocation; dual-metric self-diagnosing fs-read guard - #10599
Conversation
…make fs-read guard self-diagnosing The nightly e2e_test_bbit job has been red since 2026-07-23: the released bundle's 'bit status' crossed the MAX_FILES_READ_STATUS=1515 guard (1532, creeping to 1541 by 2.0.74). Root cause, found by diffing BIT_DEBUG_READ_FILE output of bvm-installed 2.0.33 (last green) vs 2.0.35 (first red): #10515's dependency change reshaped the bundle's pnpm hoisted layout, so @teambit/toolbox.fs.hard-link-directory plus a private fs-extra tree load from a nested copy under the compiler package instead of a shared hoisted copy (~+21 reads). The nightly creep (+1-3) is the same mechanism - each nightly re-resolves transitive deps and hoisting shifts add nested duplicates. Three changes: - compiler.task.ts imports hard-link-directory lazily inside the build task - it is build-time-only, yet its top-level import pulled ~36 files into every bootstrap/status in the bundle layout. - the bit-status guard failure now prints WHICH files are new vs a snapshot (files-snapshot-status.txt, generated from the released 2.0.74 bundle), same as the bootstrap guard - a red is a 2-minute diagnosis, not a 3-week mystery. Layout-mismatched diffs (repo vs bundle flavor) degrade to a count + sample instead of noise. - threshold 1515 -> 1650: headroom for bundle-layout drift, which the diff in the error message now makes visible and attributable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PR Summary by QodoReduce status filesystem reads and add dual-metric diagnostics
AI Description
Diagram
High-Level Assessment
Files changed (3)
|
Code Review by Qodo
1.
|
…ten threshold to 1600; print full file list on failure Addresses the Qodo finding on #10599: the snapshot was generated from a local macOS run of the released bundle - correct flavor, but a superset of what CI's leaner fixture reads. Regenerated inside a linux/amd64 node:22.22.0 container from the released linux-x64 bundle (1,878 entries; the mac and linux sets share 1,860 of ~1,870 files, so flavor was never the issue - offline supersets are inherent). Two hardenings follow from accepting that limit: - the guard now calls makeSnapshot() on failure, printing the complete current install-file list, so every red carries the exact data to inspect masked files and regenerate a perfect snapshot from real CI output - threshold 1650 -> 1600: the post-fix baseline is a projection (1541 - ~36 lazy-import reads = ~1505) and drift events arrive in ~+20 lumps; 1600 = projection + error margin + two events. Re-tighten to measured + ~50 once post-fix nightlies establish the real baseline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…l reads Replaces the threshold-bump approach. The raw-reads metric conflated two things: bit loading more code (the regression the guard exists to catch) and pnpm hoisting layout drift duplicating physical copies of the same modules (packaging noise that reshuffles every nightly bundle). Measured across the incident that motivated this: 2.0.33 -> 2.0.35 physical unique files went 1836 -> 1857 (dup copies 211 -> 232) while logical modules stayed exactly 1625 -> 1625 - the three-week nightly red was 100% duplication noise, 0% new code. The guard now counts unique logical modules (paths normalized past the last node_modules/ segment), which: - is immune to the entire layout-drift noise class - no headroom needed, the interim 1650/1600 bumps are gone - converges for both install flavors (repo .pnpm layout and released hoisted bundle), so one snapshot serves both (files-snapshot-status.txt is now logical ids, 1,645 entries from the released linux-x64 bundle) - still catches real regressions (verified: an injected module is reported by name; measured logical baselines 1625-1645 vs threshold 1700) Every run also prints a physical/logical/duplication report line so CI logs accumulate true per-environment baselines - the threshold tightens to measured+~30 once those land, and duplication trends stay visible without failing anyone. Bootstrap guard unchanged (stable, its own physical snapshot); migrate it the same way once its CI report lines accumulate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Added a second guard in 4874ced: the logical-module metric alone is blind to duplication — if bit starts loading two copies/versions of the same package (real extra I/O and memory), logical modules stay flat. Since duplication was a core motivation for this test, the guard now also fails when duplicate copies (physical files minus logical modules) exceed a ceiling (350; measured baselines: ~99 repo flavor, 211–233 released bundles). The failure output names the most-duplicated packages and the versioned locations their copies load from, e.g.: Also merged latest master into the branch. Verified: lint green; the bit-status e2e passes on the repo flavor (report line: total reads 1616, physical 1463, logical 1364, duplicates 99), and the duplication failure path renders correctly when the ceiling is artificially lowered. |
|
Code review by qodo was updated up to the latest commit 4b980ad |
Why
e2e_test_bbit(nightly, 32 nodes) has been red since 2026-07-23 — 19 of the last 21 failures are one single test out of ~2,900: thebit statusfilesystem-read guard (MAX_FILES_READ_STATUS = 1515, actual 1532 → 1541 and creeping).Root cause
Diffing
BIT_DEBUG_READ_FILEoutput of bvm-installed 2.0.33 (last green) vs 2.0.35 (first red) — only two commits apart — shows zero new logical modules (1623 → 1623). Instead, #10515's dependency change reshaped the released bundle's pnpm hoisted layout: thehard-link-directorycluster (plus a privatefs-extratree) moved from a small nested copy under one package to a larger nested copy under the compiler package (net +21 physical reads, duplicate copies 212 → 233). The nightly +1–3 creep is the same mechanism — every nightly bundle re-resolves latest transitive deps, and hoisting shifts occasionally add nested duplicates.Changes
compiler.task.ts: lazy-importhard-link-directory— it is used only inside the build task, yet its top-level import pulled ~36 files into every bootstrap/bit statusin the bundle layout. Verified the compiled dist defers the require until the task executes.bit statusguard now tracks two metrics instead of one raw count:node_modules/, so all copies of the same module count once. Catches bit loading more code, is immune to the hoisting-drift noise class above, and converges across install flavors — one snapshot (files-snapshot-status.txt) serves repo and bundle runs alike. Measured baselines: 1364 (repo), 1623–1645 (bundles).total reads / physical files / logical modules / duplicate copiesreport line, accumulating true per-environment baselines in CI logs for tightening both thresholds from measurements.Verification
npm run lintgreen (oxlint + tsc).bit statuse2e passes on the repo flavor (total reads 1616, physical 1463, logical 1364, duplicates 99); the duplication failure path verified by artificially lowering the ceiling.bit compile teambit.compilation/compiler— dist verified lazy:await Promise.resolve().then(() => require('@teambit/toolbox.fs.hard-link-directory')).