You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(orch): serve resume from a provisional header during dedup
A resume that overlaps an in-flight pause of the same sandbox can block in the
storage-template-memfile span: the local memfile template can't be built until
the deduped header resolves, which only happens after dedup's compare+defrag.
The in-flight memfd serving added earlier removes the drain wait but not this
header wait, because it is upstream of the diff source.
At pause, build a provisional local header (identity storage offsets, dirty
pages attributed to a fresh provisional build id) from the dirty bitmap and the
parent header, and register a memfd-backed diff source for that build id. The
local template is built from the provisional header immediately, so a concurrent
resume serves dirty pages straight from the still-mapped memfd instead of
blocking. Once the deduped header resolves it is swapped in (SwapHeader), routing
dirty pages to the compacted deduped diff; the distinct build id makes the swap
race-free since build.File resolves the source from the same header snapshot as
the offset. The swap is conditional (compare-and-swap from the still-provisional
header) so a late swap can't clobber a newer header the upload may have
finalized first. The provisional header is local-only: the upload continues to use
the deduped header, so the persisted artifact is unchanged.
A pause must never parent a snapshot off the provisional header: it maps dirty
pages to a synthetic build id with no storage object, so an uploaded header
that inherited those mappings (from a resume paused again before the swap) would
be unreadable on a cold or cross-node resume. build.File carries the deduped
header as a durable-header future, and processMemorySnapshot resolves the parent
via DurableHeader (waiting for the deduped header if a swap is still pending)
rather than the live, possibly-provisional header. The peer-to-peer header
source serves the durable header for the same reason, so a cross-node resume in
the window receives the deduped mappings (waiting if a swap is pending) instead
of the synthetic provisional build id it cannot resolve. Scheduling metadata
(storageTemplate.SchedulingMetadata) reads the durable header too, but
NON-BLOCKING (DurableHeaderNow): it runs on the create/resume response path, so
while the deduped header is still pending it reports rootfs-only metadata rather
than block the response for the whole dedup — and never carries the provisional
build id.
The dedup goroutine holds the memfd until the swap, bounded by a grace timeout; the
swap goroutine signals on every exit (success or error), and buildProvisionalMemfile
signals up front when it declines to build a provisional source, so the fallback
releases at drain-time and the grace is only a true last-resort backstop — a counter
(orchestrator.memfd.swap_grace_elapsed) surfaces it if it ever fires. Both the main
memfile diff and the provisional diff are pinned in the build store for the provisional
window: they share a DedupedMemfdCache (and memfd), and resume reads refresh only the
provisional entry, so disk-pressure eviction of either would break the in-flight serve
— evicting the main entry Closes it (cancelling dedup, tearing down the memfd), evicting
the provisional entry makes a still-provisional read miss the store and fetch a
never-uploaded id. Both are unpinned after the swap.
Gated by memfd-dedup-inflight-serve (default off); composes with the in-flight
drain serving (provisional covers the compare window, in-flight the drain).
Signed-off-by: Nikita Kalyazin <nikita.kalyazin@e2b.dev>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
0 commit comments