Skip to content

fix(build): gate the optimize phase's metadata re-upload on finalize's uploads - #3639

Open
JooKS-me wants to merge 1 commit into
e2b-dev:mainfrom
JooKS-me:fix/optimize-metadata-race
Open

JooKS-me wants to merge 1 commit into
e2b-dev:mainfrom
JooKS-me:fix/optimize-metadata-race

Conversation

@JooKS-me

Copy link
Copy Markdown

Problem

The optimize phase (packages/orchestrator/pkg/template/build/phases/optimize, from #1705)
runs right after finalize, but finalize's PauseAndUpload pushes its uploads — snapshot
data and the pre-prefetch metadata — onto the build's UploadErrGroup and returns
immediately. The only wait for that group runs at the builder level, after every
phase has completed.

So the optimize phase collects its intersection mapping and calls updateMetadata with
no ordering against the finalize goroutine: two writers race on the same metadata
object for the build
. An interleaving where the finalize upload finishes last clobbers
the just-uploaded prefetch mapping — a silent loss that would manifest as "prefetch
mapping sometimes missing" for freshly built templates, with nothing in the logs.

Solution

finalizeUploadsSettled() gates the optimize phase's metadata re-upload on the build's
UploadErrGroup:

  • Normal path: the wait returns after finalize's uploads settle, then
    updateMetadata publishes the prefetch-enriched metadata. The mapping can no longer
    interleave with the finalize upload.
  • Failed upload: publishing is skipped — the remote build is incomplete, and the
    same error resurfaces at the builder-level UploadErrGroup.Wait(), so the build
    still fails loudly rather than dangling a mapping over uploads that never landed.

The gate sits after collectMemoryPrefetchMapping (the expensive part), so the wait
overlaps the tail of the finalize upload rather than adding to the critical path.

…s uploads

The optimize phase runs right after finalize, but finalize's
PauseAndUpload pushes its uploads (snapshot data and the pre-prefetch
metadata) onto the build's UploadErrGroup and returns immediately —
the only wait for that group runs after every phase. The optimize
phase then collected its mapping and re-uploaded metadata with no
ordering against the finalize goroutine, so the two writers raced on
the same metadata object for the build: an interleaving where the
finalize upload finished last clobbered the just-uploaded prefetch
mapping (silent loss, manifests as "prefetch mapping sometimes
missing").

finalizeUploadsSettled now waits on the build's UploadErrGroup before
the optimize phase re-uploads metadata. A failed finalize upload skips
publishing — the remote build is incomplete, and the same error
resurfaces at the builder-level wait, so the build still fails loudly.

Test: TestFinalizeUploadsSettled covers the settled, failed, blocked
(ordering invariant: the gate does not return while the finalize
upload is in flight) and nil-group cases.

Co-Authored-By: Claude <noreply@anthropic.com>
@cla-bot

cla-bot Bot commented Sep 15, 2026

Copy link
Copy Markdown

We require contributors to sign our Contributor License Agreement, and we don't have @JooKS-me on file. You can sign our CLA at https://e2b.dev/docs/cla . Once you've signed, post a comment here that says '@cla-bot check'

@JooKS-me

Copy link
Copy Markdown
Author

@cla-bot check

@cla-bot cla-bot Bot added the cla-signed label Sep 15, 2026
@cla-bot

cla-bot Bot commented Sep 15, 2026

Copy link
Copy Markdown

The cla-bot has been summoned, and re-checked this pull request!

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant