Skip to content

chore(build): Parallelize CDN bundle builds#20334

Open
mydea wants to merge 2 commits intodevelopfrom
fn/parallel-browser-bundles
Open

chore(build): Parallelize CDN bundle builds#20334
mydea wants to merge 2 commits intodevelopfrom
fn/parallel-browser-bundles

Conversation

@mydea
Copy link
Copy Markdown
Member

@mydea mydea commented Apr 15, 2026

Previously, running yarn build:bundles for the browser package is one of the biggest blocker for build time. This is because this builds ~90 bundles in series via rollup. This PR adds a small script to run multiple rollup builds in parallel, hopefully speeding this up significantly. Locally I saw an improvement from ~150s to ~70s.

Summary

  • Adds dev-packages/rollup-utils/rollupParallel.mjs — a ~30 line script that runs rollup config arrays concurrently using the rollup JS API (concurrency = CPU count)
  • Uses it for @sentry/browser's build:bundle, which builds 93 rollup configs (31 entrypoints × 3 variants: .js, .min.js, .debug.min.js)

Rollup processes config arrays sequentially. This was the critical path for yarn build — the browser bundle step alone took ~175s. With parallel execution it completes in ~65s (~2.6x speedup).

The other packages with build:bundle (replay, feedback, replay-canvas, wasm) only have 3-6 configs each, so the overhead isn't worth it there.

Test plan

  • yarn build in packages/browser produces all 93 bundles + sourcemaps
  • Bundle sizes match sequential build output
  • CI build passes

🤖 Generated with Claude Code

Add a lightweight parallel rollup runner that processes config arrays
concurrently using the rollup JS API. Use it for @sentry/browser's
build:bundle which runs 93 rollup builds (31 entrypoints × 3 variants).

This reduces the browser bundle build from ~175s to ~65s (~2.6x faster),
which was the critical path for the full `yarn build`.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mydea mydea changed the title perf(browser): Parallelize CDN bundle builds chore(build): Parallelize CDN bundle builds Apr 15, 2026
@mydea mydea self-assigned this Apr 15, 2026
Comment thread dev-packages/rollup-utils/rollupParallel.mjs Outdated
Comment thread dev-packages/rollup-utils/rollupParallel.mjs
}

const { default: configs } = await import(pathToFileURL(resolve(configPath)).href);
const concurrency = cpus().length;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Zero concurrency from empty cpus() skips all builds

Medium Severity

os.cpus() can return an empty array in certain container or CI environments (e.g., when /proc is unavailable), making concurrency equal to 0. This causes Array.from({ length: 0 }, worker) to spawn zero workers, so Promise.all([]) resolves immediately and the script exits successfully without building any bundles — a silent, hard-to-debug failure. Using Math.max(1, cpus().length) or os.availableParallelism() (guaranteed ≥ 1) would prevent this.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 790f53f. Configure here.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 15, 2026

size-limit report 📦

Path Size % Change Change
@sentry/browser 25.78 kB - -
@sentry/browser - with treeshaking flags 24.27 kB - -
@sentry/browser (incl. Tracing) 42.77 kB - -
@sentry/browser (incl. Tracing, Profiling) 47.4 kB - -
@sentry/browser (incl. Tracing, Replay) 81.69 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 71.22 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 86.39 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 98.6 kB - -
@sentry/browser (incl. Feedback) 42.59 kB - -
@sentry/browser (incl. sendFeedback) 30.45 kB - -
@sentry/browser (incl. FeedbackAsync) 35.45 kB - -
@sentry/browser (incl. Metrics) 27.07 kB - -
@sentry/browser (incl. Logs) 27.2 kB - -
@sentry/browser (incl. Metrics & Logs) 27.89 kB - -
@sentry/react 27.53 kB - -
@sentry/react (incl. Tracing) 45.09 kB - -
@sentry/vue 30.61 kB - -
@sentry/vue (incl. Tracing) 44.63 kB - -
@sentry/svelte 25.8 kB - -
CDN Bundle 28.46 kB - -
CDN Bundle (incl. Tracing) 43.82 kB - -
CDN Bundle (incl. Logs, Metrics) 29.83 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 44.89 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 68.73 kB - -
CDN Bundle (incl. Tracing, Replay) 80.78 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 81.83 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 86.31 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 87.34 kB - -
CDN Bundle - uncompressed 83.12 kB - -
CDN Bundle (incl. Tracing) - uncompressed 129.95 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 87.27 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 133.36 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 210.63 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 247.21 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 250.6 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 260.12 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 263.51 kB - -
@sentry/nextjs (client) 47.52 kB - -
@sentry/sveltekit (client) 43.24 kB - -
@sentry/node-core 57.94 kB +0.01% +5 B 🔺
@sentry/node 174.8 kB +0.01% +10 B 🔺
@sentry/node - without tracing 97.88 kB +0.03% +23 B 🔺
@sentry/aws-serverless 115.13 kB +0.01% +8 B 🔺

View base workflow run

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b8fd53b. Configure here.

await bundle.close();
done++;
process.stdout.write(`\r [${done}/${configs.length}] builds completed`);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concurrent builds share mutable rollup plugin instances

Medium Severity

The configs produced by makeBundleConfigVariants share the same rollup plugin instances by reference (e.g., nodeResolvePlugin, sucrasePlugin, licensePlugin, terserPlugin, setSdkSourcePlugin) across their 3 variant configs (.js, .min.js, .debug.min.js). The mergePlugins function in deepMerge concatenates arrays without cloning plugin objects. When two such sibling configs are picked up by different workers simultaneously, concurrent rollup() calls operate on the same plugin instances. Rollup plugins can maintain internal state via closures and hooks like buildStart/closeBundle, and concurrent use of shared instances is an unsupported pattern that could cause subtle, hard-to-reproduce build corruption — especially if any plugin dependency is updated to become stateful.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b8fd53b. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant