Skip to content

fix(engine): auto worker count stays within the heap budget - #5149

Merged
miguel-heygen merged 3 commits into
mainfrom
dexport/heap-worker-cap
Oct 7, 2026
Merged

miguel-heygen merged 3 commits into
mainfrom
dexport/heap-worker-cap

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

What

Automatic capture-worker sizing now never exceeds what the render process's V8 heap can feed. A render that asks for --workers N is untouched.

Why

A desktop export on a 24 GB Mac (43 s, 1080x1920, 22 videos, 67 audio clips, 41 images) auto-picked 5 workers. The producer's own warning said the 4192 MB heap supports about 4, and the render died at 62% with JavaScript heap out of memory. The heap budget (heapBasedWorkers) was computed and reported but never enforced.

The ceiling is real on every Mac running the desktop app: the app runs the CLI inside Electron's Node, whose V8 heap is a hard 4192 MB because of pointer compression (a 4 GB cage). Measured with Electron 44.2.0 in run-as-node mode: NODE_OPTIONS=--max-old-space-size=2048 is honored (2144 MB), but 6144 and 8192 are ignored and the limit stays 4192 MB; v8.setFlagsFromString does the same. A system Node honors 8192 (8240 MB). So raising the heap from the app is not possible, and the worker count has to respect the budget.

How

computeWorkerSizing caps the final auto count at heapBasedWorkers and reports the bound as heap. exceedsHeapAdvisory can now only be true for an explicit request.

Verified

  • New test fakes a 14-core, 24 GB host with a 4192 MB heap: auto gives 4 workers bound by heap; an explicit 6 stays 6; an 8192 MB heap leaves the count at 5.
  • Run on a Linux box: 58/58 in parallelCoordinator.test.ts. With the cap removed the new test fails (red seen on that box).
  • Not run: a real render of the reporter's project.

Notes

  • buildHeapAdvisoryWarning in captureCost.ts can no longer fire for an auto-sized render; left in place for a follow-up.
  • Four workers is still the ceiling inside the app. Holding less heap per worker (so larger projects can use more) is a separate change.

Size

Under the 100-line floor on purpose: a lone urgent fix (desktop export heap OOM, P0) that no other open PR can carry.

…n feed

A 24 GB Mac exporting a 43 s portrait project with 22 videos picked 5 capture workers against a 4192 MB heap that feeds about 4, and the render died at 62% with JavaScript heap out of memory. The heap budget was only advisory; it now caps the automatic count (bound 'heap'). An explicit --workers N is untouched.
@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Edit accuracy: accurate 2059 (base branch 2059), smooth 1602 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (0)

… fails

Review findings on #5149: the os and v8 mocks were restored only if every assertion passed, and two comments still called the heap cap advisory.
@miguel-heygen
miguel-heygen marked this pull request as ready for review October 7, 2026 04:34

@jrusso1020 jrusso1020 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approving at ccd56ae3.

The cap is the right shape. It's one clamp after the contention cap, applied to auto sizing only. The explicit path returns earlier with "explicit", so --workers N is untouched, as the body says. heapBasedWorkers is floored at 1, so the clamp can't produce 0 workers. exceedsHeapAdvisory can now only be true for an explicit request.

Ran:

  • parallelCoordinator.test.ts: 58 of 58.
  • With the four-line clamp removed, "never auto-picks more workers than the V8 heap can feed" fails (57 of 58).

Non-blocking, worth knowing before it lands. The cap isn't specific to Electron. It applies to every auto-sized render on any Node whose heap is about 4 GB, which is stock Node's default. On my box, Node 22 reports heap_size_limit = 4144 MB, which gives (4144 − 1024) / 640 = 4 workers.

  • CLI users: defaultSafeMaxWorkers allows up to 16, so anyone on a large machine without NODE_OPTIONS who auto-picked 5–16 workers will now run 4.
  • Cloud producer: producer-internal's Dockerfile.prod doesn't set a heap size either, so its auto-sized renders take the same cap. I couldn't check how the deploy configures it.

The comment this PR removes warned about exactly this ("enforcing a guessed budget could silently cut worker counts fleet-wide", PRINFRA-341). The 640 MB per worker comes from one field report and doesn't scale with resolution. Capping is the safe direction for an OOM, so I'm not blocking on it. Before or right after merge, it would be good to either:

  • check the existing workersBoundBy / workers_heap_* render telemetry for how often the cloud and CLI auto-picked more than 4, or
  • give the cloud producer a larger --max-old-space-size so it keeps its parallelism.

A system Node honors that flag, as your Electron measurement showed.

Nit: buildHeapAdvisoryWarning and its test now cover a branch that auto sizing can't reach, as the Notes say. Fine as a follow-up.

— Rames

@miguel-heygen
miguel-heygen added this pull request to the merge queue Oct 7, 2026
Merged via the queue into main with commit 9e0d05d Oct 7, 2026
220 of 221 checks passed
@miguel-heygen
miguel-heygen deleted the dexport/heap-worker-cap branch October 7, 2026 06:30
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.

2 participants