-
Notifications
You must be signed in to change notification settings - Fork 44
perf(SDK-6463): prune excluded dirs from zip/md5 walks — minutes-long 'Creating tests.zip' stall on monorepos #1142
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1103,6 +1103,20 @@ exports.getFilesToIgnore = (runSettings, excludeFiles, logging = true) => { | ||||||||||||||||
| return ignoreFiles; | |||||||||||||||||
| } | |||||||||||||||||
|
|
|||||||||||||||||
| // SDK-6463 (perf): derive directory-pruning patterns from the ignore list. | |||||||||||||||||
|
Bhargavi-BS marked this conversation as resolved.
Outdated
|
|||||||||||||||||
| // readdir-glob (used both by archiver.glob for tests.zip and by hashUtil for the | |||||||||||||||||
| // spec md5 check) applies `ignore` per-entry AFTER walking, so it still descends | |||||||||||||||||
| // into node_modules/.git/dist etc. On large monorepos that walk alone takes | |||||||||||||||||
| // minutes ("Creating tests.zip" stalls). Its `skip` option instead prevents | |||||||||||||||||
| // DESCENDING into matching directories. Any ignore pattern ending in '/**' is | |||||||||||||||||
| // safe to reuse as a skip: if a directory matches '<x>/**' then every descendant | |||||||||||||||||
| // also matches it, so pruning cannot change which files end up included. | |||||||||||||||||
| exports.getDirectorySkipPatterns = (ignoreFiles) => { | |||||||||||||||||
| return (ignoreFiles || []).filter((pattern) => | |||||||||||||||||
| typeof pattern === 'string' && pattern.endsWith('/**') | |||||||||||||||||
| ); | |||||||||||||||||
| } | |||||||||||||||||
|
|
|||||||||||||||||
| exports.getNumberOfSpecFiles = (bsConfig, args, cypressConfig, turboScaleSession=false) => { | |||||||||||||||||
| let defaultSpecFolder | |||||||||||||||||
| let testFolderPath | |||||||||||||||||
|
|
@@ -1722,13 +1736,20 @@ exports.fetchZipSize = (fileName) => { | ||||||||||||||||
| } | |||||||||||||||||
| } | |||||||||||||||||
|
|
|||||||||||||||||
| const getDirectorySize = async function(dir) { | |||||||||||||||||
| const getDirectorySize = async function(dir, deadline) { | |||||||||||||||||
| try{ | |||||||||||||||||
| // SDK-6463 (perf): this telemetry-only walk recursively stats every file (it is | |||||||||||||||||
| // pointed at node_modules in runs.js). On large monorepos it blocked the pipeline | |||||||||||||||||
| // between archiving and uploading for tens of seconds. Stop descending once the | |||||||||||||||||
| // deadline passes — folder size is best-effort telemetry, never worth stalling a run. | |||||||||||||||||
| if (deadline && Date.now() > deadline) { | |||||||||||||||||
| return 0; | |||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any downstream effects from this?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Verified the full flow — no functional downstream effects:
Behavior when the 5s deadline fires: the reported The trade: bounded, slightly-lossy telemetry vs. blocking the archive→upload pipeline for tens of seconds on large monorepos (the reported case). Happy to raise the cap or make it env-configurable if instrumentation accuracy is a concern.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pushed an improvement on this in 609d50d that removes the concern entirely: the walk no longer blocks anything.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Verified both modes, structurally and with real runs. Structure — the await sits on the shared path before the modes ever diverge: The value is awaited once, at a single point common to both modes, before Empirical — real runs on a 144k-file synthetic monorepo (customer-style
For reference, published 1.36.12 on the same repo: 5s zip creation and 13s in the pre-zip md5 phase — this branch reduces both to ~0 (and on the reporting customer's Windows machine with a real NX tree, the zip phase alone was 86s). |
|||||||||||||||||
| } | |||||||||||||||||
| const subdirs = (await readdir(dir)); | |||||||||||||||||
| const files = await Promise.all(subdirs.map(async (subdir) => { | |||||||||||||||||
| const res = path.resolve(dir, subdir); | |||||||||||||||||
| const s = (await stat(res)); | |||||||||||||||||
| return s.isDirectory() ? getDirectorySize(res) : (s.size); | |||||||||||||||||
| return s.isDirectory() ? getDirectorySize(res, deadline) : (s.size); | |||||||||||||||||
| })); | |||||||||||||||||
| return files.reduce((a, f) => a+f, 0); | |||||||||||||||||
| }catch(e){ | |||||||||||||||||
|
|
@@ -1738,10 +1759,15 @@ const getDirectorySize = async function(dir) { | ||||||||||||||||
| } | |||||||||||||||||
| }; | |||||||||||||||||
|
|
|||||||||||||||||
| exports.fetchFolderSize = async (dir) => { | |||||||||||||||||
| exports.fetchFolderSize = async (dir, timeoutMs = 5000) => { | |||||||||||||||||
| try { | |||||||||||||||||
| if(fs.existsSync(dir)){ | |||||||||||||||||
| return (await getDirectorySize(dir) / 1024 / 1024); | |||||||||||||||||
| const deadline = Date.now() + timeoutMs; | |||||||||||||||||
| const size = (await getDirectorySize(dir, deadline) / 1024 / 1024); | |||||||||||||||||
| if (Date.now() > deadline) { | |||||||||||||||||
| logger.debug(`Folder size calculation for ${dir} exceeded ${timeoutMs}ms; reporting partial size.`); | |||||||||||||||||
| } | |||||||||||||||||
| return size; | |||||||||||||||||
| } | |||||||||||||||||
| return 0; | |||||||||||||||||
| } catch (error) { | |||||||||||||||||
|
|
|||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.