Skip to content

fix(storage): don't assume V4+ ancestor gaps are uncompressed - #3447

Merged
arkamar merged 5 commits into
mainfrom
fix/header-backfill-v4-ancestor-gap
Jul 29, 2026
Merged

fix(storage): don't assume V4+ ancestor gaps are uncompressed#3447
arkamar merged 5 commits into
mainfrom
fix/header-backfill-v4-ancestor-gap

Conversation

@arkamar

@arkamar arkamar commented Jul 29, 2026

Copy link
Copy Markdown
Member

On every header version, LoadHeader filled a zero BuildData for any build the mapping referenced but Builds did not cover. That is fair on V3, which has neither a Builds section nor compression, but on V4+ the gap means the entry was lost, not that the build is uncompressed.

createDiff reads the zero entry as CompressionNone and composes the suffix-less buildID/memfile. For a compressed ancestor that object does not exist, and because a zero entry also latches UncompressedFullFrameTable the source is never re-resolved, so every fault into that ancestor fails for the lifetime of the diff.

Restricting the backfill on V4+ to the header's own build keeps the one case that needs it — runV3 never writes a self entry and only runs with compression off — while leaving ancestor gaps absent, so createDiff resolves them from the build's own header instead. The trade is one header GET per V3-era ancestor on first fault, giving back #2999.

LoadHeader materialized a zero BuildData for every build a header's mapping
referenced but its Builds section lacked, on every header version. On V3 that
is faithful: the format has no Builds section and no compression, so "no entry"
does mean uncompressed. On V4+ it is a fabrication — those writers record a
size, and a frame table when compressed, for every build they knew about, so a
gap means the information was lost.

createDiff reads the zero entry as CompressionNone and composes the
suffix-less "<build>/memfile". For a zstd ancestor that object does not exist,
and because a zero entry also latches UncompressedFullFrameTable the source is
never re-resolved, so every fault into that ancestor fails for the lifetime of
the diff. Seen on a V5 memfile header whose mapping referenced one more build
than its Builds section covered: the uncovered ancestor still owned live
mappings and was stored compressed, so resumes failed with a UFFD data-fetch
error after retries, permanently and reproducibly.

Restrict the backfill on V4+ to the header's own build. runV3 stores a header
at the version it inherited from a V4/V5 parent and never writes a self entry,
and it only runs with compression off, so that sentinel stays correct. Ancestor
gaps are now left absent, which routes createDiff through refreshHeader and
resolves the true path and frame table from the referenced build's own header.
No stored data changes and affected snapshots become resumable again.

Cost: a missing entry on V4+ is indistinguishable from a lost one, so V3-era
ancestors reached from a V4/V5 header also lose their free sentinel and now pay
one header GET each on first fault. That gives back the optimization in #2999,
deliberately, because it is the same ambiguity that made compressed ancestors
unreadable. Watch orchestrator.storage.diff.frame_table_refresh with
cause=proactive to size the real cost; if it matters, an Exists probe on the
suffix-less name is far cheaper than fetching a header that can run to
hundreds of KiB.

TestFillMissingBuildsAsSentinels keeps its V5 header and its inputs; only the
three assertions whose meaning inverted are updated, so the diff records the
behaviour change on the exact input that used to be mis-handled. The new _V3
sibling pins the format the sentinels still apply to. The new storage_diff case
loads its header through LoadHeader rather than hand-building it: every existing
test in that file skips the backfill entirely, which is why this went unnoticed.
@cla-bot cla-bot Bot added the cla-signed label Jul 29, 2026
@cursor

cursor Bot commented Jul 29, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches snapshot resume and diff resolution for V4+ memfile headers; wrong behavior previously bricked resumes, and the fix trades extra header fetches on first fault for ambiguous ancestor gaps.

Overview
LoadHeader no longer fabricates zero BuildData entries for missing ancestor builds on V4+ headers; only the header’s own build still gets the uncompressed sentinel, while V3 keeps backfilling every mapped build. That stops createDiff from treating lost compressed ancestor metadata as uncompressed and permanently faulting on the wrong memfile path, and instead lets the no-entry path refresh from the ancestor’s real header. Tests cover V3 backfill, pause/serialize round-trips for absent ancestor gaps, and an integration read that goes through LoadHeader.

Reviewed by Cursor Bugbot for commit 3c447aa. Bugbot is set up for automated code reviews on this repo. Configure here.

@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

❌ 6 Tests Failed:

Tests completed Failed Passed Skipped
3648 6 3642 9
View the full list of 8 ❄️ flaky test(s)
github.com/e2b-dev/infra/tests/integration/internal/tests/api/templates::TestTemplateBuildDistroFamilies

Flake rate in main: 23.44% (Passed 49 times, Failed 15 times)

Stack Traces | 0s run time
=== RUN   TestTemplateBuildDistroFamilies
=== PAUSE TestTemplateBuildDistroFamilies
=== CONT  TestTemplateBuildDistroFamilies
--- FAIL: TestTemplateBuildDistroFamilies (0.00s)
github.com/e2b-dev/infra/tests/integration/internal/tests/api/templates::TestTemplateBuildDistroFamilies/Arch

Flake rate in main: 23.81% (Passed 48 times, Failed 15 times)

Stack Traces | 301s run time
=== RUN   TestTemplateBuildDistroFamilies/Arch
=== PAUSE TestTemplateBuildDistroFamilies/Arch
=== CONT  TestTemplateBuildDistroFamilies/Arch
    build_template_test.go:159: test-distro-arch: [info] Building template cs8iukrs1oup5e4jf7i2/a8695f47-0943-4bd1-b54c-9cbc3adbeffc
    build_template_test.go:159: test-distro-arch: [info] [base] FROM archlinux:base [92783431813c1525ce11fdf675e5f05309f1ad27383c7d264d4206c279a34fbb]
    build_template_test.go:159: test-distro-arch: [info] Base Docker image size: 133 MB
    build_template_test.go:159: test-distro-arch: [info] Creating file system and pulling Docker image
    build_template_test.go:159: test-distro-arch: [info] Uncompressing layer sha256:b29eedb0cba68b40ef1c21ad33ace0b0b65e43c800ce9ae3fb885ed388c8ff1f 133 MB
    build_template_test.go:159: test-distro-arch: [info] Uncompressing layer sha256:79c8b8c2b0d09dc3a8ac0a624ff34d637e7d300e2aa444e5daf77dea4d38cdd4 8.7 kB
    build_template_test.go:159: test-distro-arch: [info] Uncompressing layer sha256:bedd843c9cbbcb248831d66212fd634a2cc6a255ede1601d56bb6fc8dba9c391 14 MB
    build_template_test.go:159: test-distro-arch: [info] Uncompressing layer sha256:e216621d8a0a7a4486d6a228759b29bc378eff95e6ea9469854b8624b267dd13 137 B
    build_template_test.go:159: test-distro-arch: [info] Layers extracted
    build_template_test.go:159: test-distro-arch: [info] Root filesystem structure: bin, boot, dev, etc, home, lib, lib64, mnt, opt, proc, root, run, sbin, srv, sys, tmp, usr, var
    build_template_test.go:159: test-distro-arch: [info] Provisioning sandbox template
    build_template_test.go:159: test-distro-arch: [info] Provisioning was successful, cleaning up
    build_template_test.go:159: test-distro-arch: [info] Sandbox template provisioned
    build_template_test.go:159: test-distro-arch: [info] [base] DEFAULT USER user [f3d3bf06d501aa6f9d41fb8eb035aec7972af51ad446b8faa9123e46679a495b]
    distro_build_test.go:66: 
        	Error Trace:	.../api/templates/build_template_test.go:124
        	            				.../api/templates/distro_build_test.go:66
        	Error:      	Received unexpected error:
        	            	Get "http://localhost:.../builds/a8695f47-0943-4bd1-b54c-9cbc3adbeffc/status?logsOffset=14&level=info": context deadline exceeded
        	Test:       	TestTemplateBuildDistroFamilies/Arch
--- FAIL: TestTemplateBuildDistroFamilies/Arch (300.62s)
github.com/e2b-dev/infra/tests/integration/internal/tests/api/templates::TestUpdateTemplateWithInvalidAPIKey

Flake rate in main: 11.19% (Passed 119 times, Failed 15 times)

Stack Traces | 154s run time
=== RUN   TestUpdateTemplateWithInvalidAPIKey
=== PAUSE TestUpdateTemplateWithInvalidAPIKey
=== CONT  TestUpdateTemplateWithInvalidAPIKey
    template_update_test.go:107: Build failed: {<nil> An internal error occurred. Please try again or contact support with the build ID. <nil>}
--- FAIL: TestUpdateTemplateWithInvalidAPIKey (154.23s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir

Flake rate in main: 26.13% (Passed 212 times, Failed 75 times)

Stack Traces | 1.25s run time
=== RUN   TestListDir
=== PAUSE TestListDir
=== CONT  TestListDir
--- FAIL: TestListDir (1.25s)
Executing command cat in sandbox ihclz7ijqmfz0qkjoxj3l (user: root)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_0_lists_only_root_directory

Flake rate in main: 30.43% (Passed 48 times, Failed 21 times)

Stack Traces | 0.02s run time
=== RUN   TestListDir/depth_0_lists_only_root_directory
=== PAUSE TestListDir/depth_0_lists_only_root_directory
=== CONT  TestListDir/depth_0_lists_only_root_directory
    filesystem_test.go:96: 
        	Error Trace:	.../tests/envd/filesystem_test.go:96
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_0_lists_only_root_directory
--- FAIL: TestListDir/depth_0_lists_only_root_directory (0.02s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_1_lists_root_directory

Flake rate in main: 23.30% (Passed 158 times, Failed 48 times)

Stack Traces | 0.01s run time
=== RUN   TestListDir/depth_1_lists_root_directory
=== PAUSE TestListDir/depth_1_lists_root_directory
=== CONT  TestListDir/depth_1_lists_root_directory
    filesystem_test.go:96: 
        	Error Trace:	.../tests/envd/filesystem_test.go:96
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_1_lists_root_directory
--- FAIL: TestListDir/depth_1_lists_root_directory (0.01s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)

Flake rate in main: 50.00% (Passed 1 times, Failed 1 times)

Stack Traces | 0.01s run time
=== RUN   TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
=== PAUSE TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
=== CONT  TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
    filesystem_test.go:96: 
        	Error Trace:	.../tests/envd/filesystem_test.go:96
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
--- FAIL: TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory) (0.01s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_3_lists_all_directories_and_files

Flake rate in main: 23.14% (Passed 93 times, Failed 28 times)

Stack Traces | 0.01s run time
=== RUN   TestListDir/depth_3_lists_all_directories_and_files
=== PAUSE TestListDir/depth_3_lists_all_directories_and_files
=== CONT  TestListDir/depth_3_lists_all_directories_and_files
    filesystem_test.go:96: 
        	Error Trace:	.../tests/envd/filesystem_test.go:96
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_3_lists_all_directories_and_files
--- FAIL: TestListDir/depth_3_lists_all_directories_and_files (0.01s)

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@ValentaTomas
ValentaTomas marked this pull request as ready for review July 29, 2026 20:55

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM — the backfill fix is narrowly scoped and the new tests pin the exact before/after behavior on both V3 and V4+ headers; the inline nit is a stale comment elsewhere, not a functional issue.

Extended reasoning...

Overview

This PR changes backfillMissingV3UncompressedBuilds in packages/shared/pkg/storage/header/serialization.go so that on V4+ headers, only the header's own build gets a zero-BuildData sentinel inserted for mapping-referenced builds missing from Builds; ancestor gaps are now left absent instead of being fabricated as 'uncompressed'. This routes createDiff (in packages/orchestrator/pkg/sandbox/build/storage_diff.go, not touched by this PR) through its existing refreshHeader fallback for those ancestors, resolving the true compression path instead of permanently faulting on a zstd-compressed ancestor treated as uncompressed. Tests in both serialization_test.go and storage_diff_test.go are updated/added to pin the new behavior, including a regression test that round-trips through LoadHeader (the existing tests in that file all hand-built headers and skipped the backfill entirely, which is why the original bug went unnoticed).

Security risks

None. This is internal storage-header bookkeeping with no user input, auth, or crypto surface.

Level of scrutiny

This touches the snapshot/resume critical path (header backfill feeds directly into diff/UFFD fault resolution), so it warrants more than a rubber stamp. However, the change itself is small and surgical (one added conditional in one function), the reasoning in the PR description is precise about why V3 vs V4+ differ (V3 has no Builds section or compression at all; V4+ writers always record an entry for every build they know about, so a gap is lost information, not an implicit 'uncompressed'), and I traced the consuming code in storage_diff.go's createDiff to confirm the default (no-entry) branch already handles this correctly via probe-then-refresh — this is exactly the path the new TestStorageDiff_AbsentEntryOnV4ResolvesFromOwnHeader test exercises. I also checked the V5 backfill test's inverted assertions against the V3-only sibling test and confirmed both are internally consistent with the new v4plus gate.

Other factors

Test coverage is strong: TestFillMissingBuildsAsSentinels (V5, updated), the new TestFillMissingBuildsAsSentinels_V3, and the new TestStorageDiff_AbsentEntryOnV4ResolvesFromOwnHeader together pin both the old-format sentinel behavior and the new-format absent-gap behavior. The one inline finding is a documentation nit (a comment in an untouched file, storage_diff.go, whose invariant is invalidated by this change) rather than a functional bug, so it doesn't block approval.

Comment thread packages/shared/pkg/storage/header/serialization.go
…le invariant

Two follow-ups on the backfill change.

createDiff's hasEntry=false comment still claimed storage-loaded headers
always carry an entry, so only peer-served ones reach that branch. This
change is precisely what makes that false: a V4+ ancestor gap now lands
there by design. Left as-is the next reader could "simplify" the branch
back into the 404.

The fix rests on GetBuildFrameData returning nil (unknown) rather than
UncompressedFrameTable for an absent entry — nil is what routes resolve()
to the ancestor's own header instead of reading the suffix-less name.
Nothing pinned that: returning the uncompressed sentinel for absent
entries would restore the permanent 404 with every existing assertion
still green. Assert it, mirroring the V3 case that asserts the opposite.
@ValentaTomas

Copy link
Copy Markdown
Member

Reviewed this end to end against a live reproduction, and traced the read path rather than just the diff. The approach is right and I verified the full chain works: absent entry → createDiff default branch → OpenSeekable at the basic name is lazy so it does not 404 → not peer-routed → refreshHeaderopenFromLoadedHeader latches the authoritative full frame table → StorageDiff.resolve returns that table first, so the chunker reads the compressed object at correct offsets. resolve's nil-callerFT branch is a second backstop (reloadProactive) if a diff ever exists without a latched table.

Two things worth noting, pushed as a follow-up commit rather than left as comments:

  1. createDiff's hasEntry=false comment still asserted that storage-loaded headers always carry an entry, so only peer-served headers reach that branch. This PR is exactly what invalidates that — a V4+ ancestor gap now lands there by design. That is the kind of stale invariant someone later "simplifies" straight back into the 404, so I rewrote it to name both sources.

  2. The fix depends on GetBuildFrameData returning nil (unknown) for an absent entry rather than UncompressedFrameTable — nil is what routes to the ancestor's own header instead of the suffix-less name. Nothing pinned that: a later change returning the uncompressed sentinel for absent entries would restore the permanent 404 with every existing assertion still green. Now asserted, mirroring the V3 test that asserts the opposite.

On safety, I ran a negative control: reverting just the production guard makes both TestFillMissingBuildsAsSentinels and TestStorageDiff_AbsentEntryOnV4ResolvesFromOwnHeader fail, so the coverage genuinely guards the behaviour instead of merely describing it. Full pkg/storage/header and pkg/sandbox/build suites pass (the latter needs a Linux container for the build constraints).

Two smaller observations, no change requested. Mapping.Builds() excludes uuid.Nil (holes use a separate index sentinel), so hole mappings never reach the backfill and cannot lose a sentinel — worth knowing since the fault path relies on nil checks upstream. And the self-entry carve-out is load-bearing beyond runV3: SelfBuildData documents that a missing self entry is only possible on peer-served headers, which restricting the skip to bid != Metadata.BuildId preserves.

Independently, I can confirm this is not a storage-compaction side effect — the referenced objects all exist in their compressed form, none were written or touched by compaction tooling, and the failures predate the current campaign. Not approving or merging, leaving that to code owners.

The gap contract has three legs the existing tests don't chain together:
newDiffHeader copies only entries the parent has, writeV4BuildsSection
writes only entries that exist, and the next load's backfill leaves the
inherited gap absent. Any of them fabricating an entry for a
mapping-referenced build persists "authoritatively uncompressed" into the
child header, where the load-time fix can no longer help — the 404 returns,
but only for grandchild resumes, with every existing test still green.

Verified by negative control: fabricating entries in either newDiffHeader
or writeV4BuildsSection fails the test at the corresponding step.
@ValentaTomas

Copy link
Copy Markdown
Member

Second audit pass, focused on every consumer an absent entry can now reach:

  • dedup.go (parentFetchKey, countExternalBlocks): nil FT degrades to windowBytes-bucketed fetch keys — planning granularity only, no correctness impact. copy-build/inspect-build: nil-safe or presence-checked.
  • The !hasEntry probe can't eagerly 404: OpenSeekable is lazy on all providers (GCS/AWS/fs), and on the non-peer path the probe upstream is replaced by openFromLoadedHeader before any read.
  • runV3 ancestor reached through a gap: its own header's backfilled zero self entry → SelfBuildDataFullFromTable(nil) = UncompressedFullFrameTable, so the diff still latches authoritative-uncompressed and size 0 is re-statted. No refresh loop.
  • metadataFormatVersion masks feature bits, so a future V6 defaults to the restrictive (gap-preserving) backfill.

One leg was unpinned: gap persistence across a pause generation. newDiffHeader copies only entries the parent has and writeV4BuildsSection writes only entries that exist — if either ever fabricated an entry per mapping-referenced build, the gap would persist as a zero entry ("authoritatively uncompressed") in the child header, where this fix can no longer help, and the 404 would return for grandchild resumes with every existing test green. Added TestToDiffHeader_AncestorGapRoundTripsAbsent (pause → serialize → reload), negative-controlled against both fabrication points.

@arkamar
arkamar merged commit bfdbb24 into main Jul 29, 2026
43 checks passed
@arkamar
arkamar deleted the fix/header-backfill-v4-ancestor-gap branch July 29, 2026 23:13
charlie-e2b added a commit that referenced this pull request Jul 30, 2026
🤖 I have created a release *beep* *boop*
---


## 0.0.1 (2026-07-30)


### Features

* **api:** add sandbox IAM workload token configuration
([13ddb3d](13ddb3d))
* **api:** add sandbox workload identity permission
([#3319](#3319))
([13ddb3d](13ddb3d))
* **api:** SOCKS5 egress proxy on sandbox network config (BYOP)
([#2642](#2642))
([1fc3820](1fc3820))
* **cfg:** add DISABLE_STARTUP_RECLAIM flag
([#3081](#3081))
([7677ca6](7677ca6))
* **clickhouse:** implement multi-cluster fan-out for events and stats
([#2925](#2925))
([39594c6](39594c6))
* dynamic sandbox log routing and ClickHouse-backed log reads
([#3236](#3236))
([1b19a3b](1b19a3b))
* **envd:** give envd realtime IO priority, reset for user processes
([#2681](#2681))
([f4bd1b2](f4bd1b2))
* **envd:** split collapse stats into real migrations vs already-huge
([#3021](#3021))
([0d77614](0d77614))
* **envd:** support user-defined file metadata via xattrs
([#2732](#2732))
([da8fbe4](da8fbe4))
* **featureflags:** support per-service context providers
([#3100](#3100))
([65297c1](65297c1))
* freeze user cgroup across pause/resume to keep envd /init responsive
([#2688](#2688))
([eceb741](eceb741))
* **metrics:** break down pause-snapshot latency by step
([#3426](#3426))
([657559e](657559e))
* **metrics:** label pause telemetry by fs_only
([#3425](#3425))
([411b63e](411b63e))
* **observability:** add kill_reason to sandbox.lifecycle.killed
([#2833](#2833))
([e45418f](e45418f))
* **observability:** include kill_reason in kill-path structured logs
([#2846](#2846))
([33c49f7](33c49f7))
* **orch:** add envd-version to LaunchDarkly sandbox context
([#3051](#3051))
([37d3b92](37d3b92))
* **orch:** add less, nftables, iputils-ping, and jq to base
provisioning ([#2736](#2736))
([a1e010e](a1e010e))
* **orch:** collapse envd's heap into 2 MiB hugepages before pause to
cut cold-resume faults
([#2997](#2997))
([6677f73](6677f73))
* **orch:** debug a sandbox guest kernel with resume-build -gdb
([#3040](#3040))
([37bb0dc](37bb0dc))
* **orch:** decouple warm resume from memfile dedup
([#3166](#3166))
([77f25a0](77f25a0))
* **orch:** distro-aware template base-image provisioning
([#3411](#3411))
([f8c7b5b](f8c7b5b))
* **orchestrator/cgroup:** list and destroy leaked sandbox cgroups
([#3086](#3086))
([bce1d84](bce1d84))
* **orchestrator/nbd:** inspect and disconnect connected devices
([#3087](#3087))
([4d47148](4d47148))
* **orchestrator/network:** list slot namespaces
([#3089](#3089))
([c23dbc7](c23dbc7))
* **orchestrator/network:** list slot namespaces
([#3090](#3090))
([fbfce25](fbfce25))
* **orchestrator:** add -force-reboot to resume-build to cold-boot
memory-snaphsot builds
([#3208](#3208))
([cf8f15b](cf8f15b))
* **orchestrator:** add allocated resource metrics for sandboxes
([#2943](#2943))
([95cb6d3](95cb6d3))
* **orchestrator:** add dummy orchestrator binary for local API dev
([#2744](#2744))
([ab56e25](ab56e25))
* **orchestrator:** add NetworkAssignHook for sandbox lifecycle
extensions ([#3290](#3290))
([3261963](3261963))
* **orchestrator:** add soft-delete marker label to the check metric
([#3144](#3144))
([1ce64f8](1ce64f8))
* **orchestrator:** add v4HeaderForUncompressed FF bit
([#2669](#2669))
([1f459ee](1f459ee))
* **orchestrator:** always include execution metrics in sandbox webhook
events ([#2852](#2852))
([440edfe](440edfe))
* **orchestrator:** classify envd-init by exit type
([#3139](#3139))
([1e39a4f](1e39a4f))
* **orchestrator:** graceful sandbox drain on shutdown
([#3069](#3069))
([6ce68e3](6ce68e3))
* **orchestrator:** graceful template-build drain on shutdown
([#3079](#3079))
([1b3001c](1b3001c))
* **orchestrator:** improved read-path telemetry
([#3063](#3063))
([bc3fe84](bc3fe84))
* **orchestrator:** LD-gated ClickHouse write fan-out feature flag
([#3152](#3152))
([f046fcf](f046fcf))
* **orchestrator:** make build-reserved-disk-space-mb default 256MB
([#3065](#3065))
([d473f98](d473f98))
* **orchestrator:** record upload compression metrics
([#2761](#2761))
([9092e35](9092e35))
* **orchestrator:** report hugepage metrics to API
([#3182](#3182))
([7735bae](7735bae))
* **orchestrator:** run startup reclaim on boot
([#3123](#3123))
([79b838e](79b838e))
* **orchestrator:** single-instance flock on startup
([#3143](#3143))
([1320d6e](1320d6e))
* **orchestrator:** soft-delete consumer enforcement for storage index
([#3034](#3034))
([fbfc918](fbfc918))
* **orchestrator:** tag envd-init meters with start_type
([#3125](#3125))
([4466b48](4466b48))
* **orchestrator:** track and report last status change timestamp
([#2980](#2980))
([f79be77](f79be77))
* **orchestrator:** track sandbox lifecycles
([#2998](#2998))
([057f20c](057f20c))
* **orchestrator:** write layer sizes (logical/mapped/diff) to object
metadata ([#3122](#3122))
([11869c0](11869c0))
* **orch:** harvest resume-prefetch trace on pause
([#3067](#3067))
([97bd4a5](97bd4a5))
* **orch:** last-cycle memory prefetch on resume
([#3258](#3258))
([b22e820](b22e820))
* **orch:** make resume-build -gdb work on real nodes + add copy-build
-gdb ([#3108](#3108))
([c684bd2](c684bd2))
* **orch:** opt-in DSCP marker for sandbox egress (SANDBOX_EGRESS_DSCP)
([#3039](#3039))
([a98cf2c](a98cf2c))
* **orch:** per-start UFFD startup working-set metric
([#2960](#2960))
([dc386b2](dc386b2))
* **orch:** premade NixOS base-image support
([#3412](#3412))
([776ba39](776ba39))
* **orch:** record envd init duration histogram on failure with success
attribute ([#2749](#2749))
([afa7458](afa7458))
* **orch:** snapshot fragmentation metrics
([#2931](#2931))
([842b007](842b007))
* per-team events TTL limit (tier + addons)
([#3181](#3181))
([f76b2cb](f76b2cb))
* **shared:** add OTEL instrumentation to AWS S3 storage client
([#3172](#3172))
([25b0fd1](25b0fd1))
* **storage:** per-role storage URLs, env-free storage library
([#3246](#3246))
([fcbe909](fcbe909))
* **storage:** stamp provenance custom metadata on uploaded objects
(incl. headers) ([#3033](#3033))
([ba8604e](ba8604e))
* **storage:** write-through compressed templates to NFS on upload
([#2827](#2827))
([57503c1](57503c1))


### Bug Fixes

* added api and orch
([#3454](#3454))
([fda5e45](fda5e45))
* **block:** rephrase misleading error message in pwritevAll
([#2816](#2816))
([1555f1b](1555f1b))
* **cache:** use 512-byte units for stat.Blocks in FileSize
([#2949](#2949))
([0f632a9](0f632a9))
* **clean-nfs-cache:** exclude zombies from delete_age
([#3191](#3191))
([3fa2aeb](3fa2aeb))
* **compression:** correctness findings from compression audit
([#2803](#2803))
([d21a6a9](d21a6a9))
* **copy-build:** resolve compression suffix for build data files
([#2859](#2859))
([8966f7e](8966f7e))
* correct 3 CVES ([#3218](#3218))
([076823b](076823b))
* **envd:** stop freezing socat cgroup across pause/resume
([#2923](#2923))
([8b6f2b9](8b6f2b9))
* **inspect-build:** adapt validate to new Chunker upstream API
([#2989](#2989))
([2e0d3da](2e0d3da))
* **nbd:** adjust status poll sleep from 100ns to 100µs
([02bf51b](02bf51b))
* **nbd:** change NBD status poll sleep from 100ns to 100µs to avoid
useless busy spinning
([#2884](#2884))
([02bf51b](02bf51b))
* **nfsproxy:** deflake TestRoundTrip EADDRINUSE
([#2987](#2987))
([55f4d18](55f4d18))
* **orch:** denormalize upload metric file type
([#2865](#2865))
([b1646ca](b1646ca))
* **orch:** disable the chronyd seccomp filter on Alpine when using PHC
([#3453](#3453))
([e58af28](e58af28))
* **orchestrator:** anchor rsync CWD to root in template file copy
([#2835](#2835))
([7160db9](7160db9))
* **orchestrator:** atomically replace metadata
([#3321](#3321))
([0c4ad6b](0c4ad6b))
* **orchestrator:** avoid serializing upload headers twice
([#2762](#2762))
([9b7b149](9b7b149))
* **orchestrator:** chunk readiness bug in P2P-&gt;compressed
([#3185](#3185))
([74a6e5b](74a6e5b))
* **orchestrator:** deschedule flaky eviction-loop race in TestDiffSto…
([#3173](#3173))
([88ff17c](88ff17c))
* **orchestrator:** discard poisoned nftables conn on firewall errors
([#3008](#3008))
([03f10e0](03f10e0))
* **orchestrator:** drop stale pre-init logs
([#3297](#3297))
([8ec4be5](8ec4be5))
* **orchestrator:** emit compression ratios as fractions, not BP
([#2772](#2772))
([866f4c1](866f4c1))
* **orchestrator:** export dirty-page stall counter from process start
([#2992](#2992))
([badc8ad](badc8ad))
* **orchestrator:** harden Firecracker process shutdown
([#2996](#2996))
([df662e7](df662e7))
* **orchestrator:** harden shutdown network cleanup
([#3000](#3000))
([de2f391](de2f391))
* **orchestrator:** implement Docker COPY merge semantics in template
builds ([#3283](#3283))
([9174104](9174104))
* **orchestrator:** keep dedup empty-pages telemetry scan-only
([#2991](#2991))
([35d0832](35d0832))
* **orchestrator:** let build-cache threshold flag raise above its fal…
([#3175](#3175))
([06393c3](06393c3))
* **orchestrator:** log missing egress proxy in startup reclaim instead
of defaulting silently
([#3116](#3116))
([6ca3163](6ca3163))
* **orchestrator:** make copy-build handle filesystem-only snapshots
([#3299](#3299))
([62add04](62add04))
* **orchestrator:** measure ext4 free space from block groups
([#3282](#3282))
([f18f05f](f18f05f))
* **orchestrator:** normalize upload metric file labels
([#2767](#2767))
([6dec8b3](6dec8b3))
* **orchestrator:** order egress config/firewall updates to close BYOP
enable race ([#3313](#3313))
([7faa59e](7faa59e))
* **orchestrator:** order envd.service after local-fs.target
([#3043](#3043))
([ea2663e](ea2663e))
* **orchestrator:** order envd.service after systemd-tmpfiles-setup
([#3130](#3130))
([9481811](9481811))
* **orchestrator:** pause upload retain retry
([#2993](#2993))
([4f81799](4f81799))
* **orchestrator:** pin tap device host-side MAC address
([#3271](#3271))
([3c786ba](3c786ba))
* **orchestrator:** pin UFFD copy source buffers
([#2745](#2745))
([837fa91](837fa91))
* **orchestrator:** preserve full ENV value across stdout chunks
([#2740](#2740))
([4822e6d](4822e6d))
* **orchestrator:** read V3 ancestors as uncompressed instead of failing
([#2994](#2994))
([c479dd3](c479dd3))
* **orchestrator:** reject standby while draining
([#3325](#3325))
([475a7ee](475a7ee))
* **orchestrator:** report real V4 header compression ratio
([#2771](#2771))
([ecd344e](ecd344e))
* **orchestrator:** resolve remaining P2P/compression/V5 issues
([#3015](#3015))
([1e4379e](1e4379e))
* **orchestrator:** sanitize OCI pull errors
([#3096](#3096))
([a3af6c0](a3af6c0))
* **orchestrator:** scope rootfs hash to provision default
([#3129](#3129))
([475f955](475f955))
* **orchestrator:** stop Checks health-loop leaking
([#2739](#2739))
([17e6e60](17e6e60))
* **orchestrator:** survive SIGBUS from failing disks under mmap'd
caches ([#3385](#3385))
([728bba3](728bba3))
* **orchestrator:** tolerate missing header for legacy templates
([#3026](#3026))
([8a44bfe](8a44bfe))
* **orch:** fall back to ID_LIKE with a warning instead of rejecting
([#3459](#3459))
([7167818](7167818))
* **orch:** prevent NBD dispatch read-loop stall on WRITE_ZEROES (behind
flag) ([#3048](#3048))
([efd3d4d](efd3d4d))
* **orch:** split scheduling base build id per artifact
([#2920](#2920))
([3e35a2a](3e35a2a))
* **orch:** validate copy-build -gdb buckets before the snapshot copy
([#3446](#3446))
([586ad74](586ad74))
* **shared:** never report a failed envd command stream as success
([#3281](#3281))
([69c06b6](69c06b6))
* **storage:** compression upload & cache correctness fixes
([#3231](#3231))
([980748f](980748f))
* **storage:** don't assume V4+ ancestor gaps are uncompressed
([#3447](#3447))
([bfdbb24](bfdbb24))
* **uffd:** dedupe deferred page faults
([#2864](#2864))
([9680a41](9680a41))
* WrapContextAsUserError should not misclassify internal timeouts as
user cancellations
([#3155](#3155))
([8f83959](8f83959))


### Performance Improvements

* **build:** cache resolved Diff per BuildId within File.ReadAt
([#2838](#2838))
([53de07f](53de07f))
* **build:** parallelize fragmented backing reads
([#2872](#2872))
([c7655a7](c7655a7))
* **clean-nfs-cache:** restore dirfd-relative statx
([#2766](#2766))
([6bdbedb](6bdbedb))
* **header:** add V5 columnar varint header format
([#2847](#2847))
([9dd931b](9dd931b))
* **header:** pack cached Header.Mapping into a compact form
([#2844](#2844))
([7f0b13c](7f0b13c))
* **orchestrator:** add memfile dedup density threshold
([#2862](#2862))
([7ccfa02](7ccfa02))
* **orchestrator:** avoid V3-ancestor header refresh
([#2999](#2999))
([cb6aa0b](cb6aa0b))
* **orch:** metrics for dirty page throttling
([#2858](#2858))
([d2aa554](d2aa554))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: e2b-release-please[bot] <298072688+e2b-release-please[bot]@users.noreply.github.com>
Co-authored-by: Charlie Wyse <charlie.wyse@e2b.dev>
jakubno pushed a commit that referenced this pull request Aug 3, 2026
On every header version, LoadHeader filled a zero BuildData for any
build the mapping referenced but Builds did not cover. That is fair on
V3, which has neither a Builds section nor compression, but on V4+ the
gap means the entry was lost, not that the build is uncompressed.

createDiff reads the zero entry as CompressionNone and composes the
suffix-less buildID/memfile. For a compressed ancestor that object does
not exist, and because a zero entry also latches
UncompressedFullFrameTable the source is never re-resolved, so every
fault into that ancestor fails for the lifetime of the diff.

Restricting the backfill on V4+ to the header's own build keeps the one
case that needs it — runV3 never writes a self entry and only runs with
compression off — while leaving ancestor gaps absent, so createDiff
resolves them from the build's own header instead. The trade is one
header GET per V3-era ancestor on first fault, giving back #2999.

Co-authored-by: Tomas Valenta <49156497+ValentaTomas@users.noreply.github.com>
jakubno pushed a commit that referenced this pull request Aug 3, 2026
🤖 I have created a release *beep* *boop*
---


## 0.0.1 (2026-07-30)


### Features

* **api:** add sandbox IAM workload token configuration
([13ddb3d](13ddb3d))
* **api:** add sandbox workload identity permission
([#3319](#3319))
([13ddb3d](13ddb3d))
* **api:** SOCKS5 egress proxy on sandbox network config (BYOP)
([#2642](#2642))
([1fc3820](1fc3820))
* **cfg:** add DISABLE_STARTUP_RECLAIM flag
([#3081](#3081))
([7677ca6](7677ca6))
* **clickhouse:** implement multi-cluster fan-out for events and stats
([#2925](#2925))
([39594c6](39594c6))
* dynamic sandbox log routing and ClickHouse-backed log reads
([#3236](#3236))
([1b19a3b](1b19a3b))
* **envd:** give envd realtime IO priority, reset for user processes
([#2681](#2681))
([f4bd1b2](f4bd1b2))
* **envd:** split collapse stats into real migrations vs already-huge
([#3021](#3021))
([0d77614](0d77614))
* **envd:** support user-defined file metadata via xattrs
([#2732](#2732))
([da8fbe4](da8fbe4))
* **featureflags:** support per-service context providers
([#3100](#3100))
([65297c1](65297c1))
* freeze user cgroup across pause/resume to keep envd /init responsive
([#2688](#2688))
([eceb741](eceb741))
* **metrics:** break down pause-snapshot latency by step
([#3426](#3426))
([f551118](f551118))
* **metrics:** label pause telemetry by fs_only
([#3425](#3425))
([4be33ba](4be33ba))
* **observability:** add kill_reason to sandbox.lifecycle.killed
([#2833](#2833))
([e45418f](e45418f))
* **observability:** include kill_reason in kill-path structured logs
([#2846](#2846))
([33c49f7](33c49f7))
* **orch:** add envd-version to LaunchDarkly sandbox context
([#3051](#3051))
([37d3b92](37d3b92))
* **orch:** add less, nftables, iputils-ping, and jq to base
provisioning ([#2736](#2736))
([a1e010e](a1e010e))
* **orch:** collapse envd's heap into 2 MiB hugepages before pause to
cut cold-resume faults
([#2997](#2997))
([6677f73](6677f73))
* **orch:** debug a sandbox guest kernel with resume-build -gdb
([#3040](#3040))
([37bb0dc](37bb0dc))
* **orch:** decouple warm resume from memfile dedup
([#3166](#3166))
([77f25a0](77f25a0))
* **orch:** distro-aware template base-image provisioning
([#3411](#3411))
([1abece1](1abece1))
* **orchestrator/cgroup:** list and destroy leaked sandbox cgroups
([#3086](#3086))
([bce1d84](bce1d84))
* **orchestrator/nbd:** inspect and disconnect connected devices
([#3087](#3087))
([4d47148](4d47148))
* **orchestrator/network:** list slot namespaces
([#3089](#3089))
([c23dbc7](c23dbc7))
* **orchestrator/network:** list slot namespaces
([#3090](#3090))
([fbfce25](fbfce25))
* **orchestrator:** add -force-reboot to resume-build to cold-boot
memory-snaphsot builds
([#3208](#3208))
([cf8f15b](cf8f15b))
* **orchestrator:** add allocated resource metrics for sandboxes
([#2943](#2943))
([95cb6d3](95cb6d3))
* **orchestrator:** add dummy orchestrator binary for local API dev
([#2744](#2744))
([ab56e25](ab56e25))
* **orchestrator:** add NetworkAssignHook for sandbox lifecycle
extensions ([#3290](#3290))
([3261963](3261963))
* **orchestrator:** add soft-delete marker label to the check metric
([#3144](#3144))
([1ce64f8](1ce64f8))
* **orchestrator:** add v4HeaderForUncompressed FF bit
([#2669](#2669))
([1f459ee](1f459ee))
* **orchestrator:** always include execution metrics in sandbox webhook
events ([#2852](#2852))
([440edfe](440edfe))
* **orchestrator:** classify envd-init by exit type
([#3139](#3139))
([1e39a4f](1e39a4f))
* **orchestrator:** graceful sandbox drain on shutdown
([#3069](#3069))
([6ce68e3](6ce68e3))
* **orchestrator:** graceful template-build drain on shutdown
([#3079](#3079))
([1b3001c](1b3001c))
* **orchestrator:** improved read-path telemetry
([#3063](#3063))
([bc3fe84](bc3fe84))
* **orchestrator:** LD-gated ClickHouse write fan-out feature flag
([#3152](#3152))
([f046fcf](f046fcf))
* **orchestrator:** make build-reserved-disk-space-mb default 256MB
([#3065](#3065))
([d473f98](d473f98))
* **orchestrator:** record upload compression metrics
([#2761](#2761))
([9092e35](9092e35))
* **orchestrator:** report hugepage metrics to API
([#3182](#3182))
([7735bae](7735bae))
* **orchestrator:** run startup reclaim on boot
([#3123](#3123))
([79b838e](79b838e))
* **orchestrator:** single-instance flock on startup
([#3143](#3143))
([1320d6e](1320d6e))
* **orchestrator:** soft-delete consumer enforcement for storage index
([#3034](#3034))
([fbfc918](fbfc918))
* **orchestrator:** tag envd-init meters with start_type
([#3125](#3125))
([4466b48](4466b48))
* **orchestrator:** track and report last status change timestamp
([#2980](#2980))
([f79be77](f79be77))
* **orchestrator:** track sandbox lifecycles
([#2998](#2998))
([057f20c](057f20c))
* **orchestrator:** write layer sizes (logical/mapped/diff) to object
metadata ([#3122](#3122))
([11869c0](11869c0))
* **orch:** harvest resume-prefetch trace on pause
([#3067](#3067))
([97bd4a5](97bd4a5))
* **orch:** last-cycle memory prefetch on resume
([#3258](#3258))
([ea94196](ea94196))
* **orch:** make resume-build -gdb work on real nodes + add copy-build
-gdb ([#3108](#3108))
([5385594](5385594))
* **orch:** opt-in DSCP marker for sandbox egress (SANDBOX_EGRESS_DSCP)
([#3039](#3039))
([a98cf2c](a98cf2c))
* **orch:** per-start UFFD startup working-set metric
([#2960](#2960))
([dc386b2](dc386b2))
* **orch:** premade NixOS base-image support
([#3412](#3412))
([4bd42d2](4bd42d2))
* **orch:** record envd init duration histogram on failure with success
attribute ([#2749](#2749))
([afa7458](afa7458))
* **orch:** snapshot fragmentation metrics
([#2931](#2931))
([842b007](842b007))
* per-team events TTL limit (tier + addons)
([#3181](#3181))
([f76b2cb](f76b2cb))
* **shared:** add OTEL instrumentation to AWS S3 storage client
([#3172](#3172))
([25b0fd1](25b0fd1))
* **storage:** per-role storage URLs, env-free storage library
([#3246](#3246))
([fcbe909](fcbe909))
* **storage:** stamp provenance custom metadata on uploaded objects
(incl. headers) ([#3033](#3033))
([ba8604e](ba8604e))
* **storage:** write-through compressed templates to NFS on upload
([#2827](#2827))
([57503c1](57503c1))


### Bug Fixes

* added api and orch
([#3454](#3454))
([d56e0a8](d56e0a8))
* **block:** rephrase misleading error message in pwritevAll
([#2816](#2816))
([1555f1b](1555f1b))
* **cache:** use 512-byte units for stat.Blocks in FileSize
([#2949](#2949))
([0f632a9](0f632a9))
* **clean-nfs-cache:** exclude zombies from delete_age
([#3191](#3191))
([3fa2aeb](3fa2aeb))
* **compression:** correctness findings from compression audit
([#2803](#2803))
([d21a6a9](d21a6a9))
* **copy-build:** resolve compression suffix for build data files
([#2859](#2859))
([8966f7e](8966f7e))
* correct 3 CVES ([#3218](#3218))
([076823b](076823b))
* **envd:** stop freezing socat cgroup across pause/resume
([#2923](#2923))
([8b6f2b9](8b6f2b9))
* **inspect-build:** adapt validate to new Chunker upstream API
([#2989](#2989))
([2e0d3da](2e0d3da))
* **nbd:** adjust status poll sleep from 100ns to 100µs
([02bf51b](02bf51b))
* **nbd:** change NBD status poll sleep from 100ns to 100µs to avoid
useless busy spinning
([#2884](#2884))
([02bf51b](02bf51b))
* **nfsproxy:** deflake TestRoundTrip EADDRINUSE
([#2987](#2987))
([55f4d18](55f4d18))
* **orch:** denormalize upload metric file type
([#2865](#2865))
([b1646ca](b1646ca))
* **orch:** disable the chronyd seccomp filter on Alpine when using PHC
([#3453](#3453))
([dfa9764](dfa9764))
* **orchestrator:** anchor rsync CWD to root in template file copy
([#2835](#2835))
([7160db9](7160db9))
* **orchestrator:** atomically replace metadata
([#3321](#3321))
([0c4ad6b](0c4ad6b))
* **orchestrator:** avoid serializing upload headers twice
([#2762](#2762))
([9b7b149](9b7b149))
* **orchestrator:** chunk readiness bug in P2P-&gt;compressed
([#3185](#3185))
([74a6e5b](74a6e5b))
* **orchestrator:** deschedule flaky eviction-loop race in TestDiffSto…
([#3173](#3173))
([88ff17c](88ff17c))
* **orchestrator:** discard poisoned nftables conn on firewall errors
([#3008](#3008))
([03f10e0](03f10e0))
* **orchestrator:** drop stale pre-init logs
([#3297](#3297))
([8ec4be5](8ec4be5))
* **orchestrator:** emit compression ratios as fractions, not BP
([#2772](#2772))
([866f4c1](866f4c1))
* **orchestrator:** export dirty-page stall counter from process start
([#2992](#2992))
([badc8ad](badc8ad))
* **orchestrator:** harden Firecracker process shutdown
([#2996](#2996))
([df662e7](df662e7))
* **orchestrator:** harden shutdown network cleanup
([#3000](#3000))
([de2f391](de2f391))
* **orchestrator:** implement Docker COPY merge semantics in template
builds ([#3283](#3283))
([9174104](9174104))
* **orchestrator:** keep dedup empty-pages telemetry scan-only
([#2991](#2991))
([35d0832](35d0832))
* **orchestrator:** let build-cache threshold flag raise above its fal…
([#3175](#3175))
([06393c3](06393c3))
* **orchestrator:** log missing egress proxy in startup reclaim instead
of defaulting silently
([#3116](#3116))
([6ca3163](6ca3163))
* **orchestrator:** make copy-build handle filesystem-only snapshots
([#3299](#3299))
([62add04](62add04))
* **orchestrator:** measure ext4 free space from block groups
([#3282](#3282))
([f18f05f](f18f05f))
* **orchestrator:** normalize upload metric file labels
([#2767](#2767))
([6dec8b3](6dec8b3))
* **orchestrator:** order egress config/firewall updates to close BYOP
enable race ([#3313](#3313))
([7faa59e](7faa59e))
* **orchestrator:** order envd.service after local-fs.target
([#3043](#3043))
([ea2663e](ea2663e))
* **orchestrator:** order envd.service after systemd-tmpfiles-setup
([#3130](#3130))
([9481811](9481811))
* **orchestrator:** pause upload retain retry
([#2993](#2993))
([4f81799](4f81799))
* **orchestrator:** pin tap device host-side MAC address
([#3271](#3271))
([3c786ba](3c786ba))
* **orchestrator:** pin UFFD copy source buffers
([#2745](#2745))
([837fa91](837fa91))
* **orchestrator:** preserve full ENV value across stdout chunks
([#2740](#2740))
([4822e6d](4822e6d))
* **orchestrator:** read V3 ancestors as uncompressed instead of failing
([#2994](#2994))
([c479dd3](c479dd3))
* **orchestrator:** reject standby while draining
([#3325](#3325))
([475a7ee](475a7ee))
* **orchestrator:** report real V4 header compression ratio
([#2771](#2771))
([ecd344e](ecd344e))
* **orchestrator:** resolve remaining P2P/compression/V5 issues
([#3015](#3015))
([1e4379e](1e4379e))
* **orchestrator:** sanitize OCI pull errors
([#3096](#3096))
([a3af6c0](a3af6c0))
* **orchestrator:** scope rootfs hash to provision default
([#3129](#3129))
([475f955](475f955))
* **orchestrator:** stop Checks health-loop leaking
([#2739](#2739))
([17e6e60](17e6e60))
* **orchestrator:** survive SIGBUS from failing disks under mmap'd
caches ([#3385](#3385))
([8694d08](8694d08))
* **orchestrator:** tolerate missing header for legacy templates
([#3026](#3026))
([8a44bfe](8a44bfe))
* **orch:** fall back to ID_LIKE with a warning instead of rejecting
([#3459](#3459))
([73399b3](73399b3))
* **orch:** prevent NBD dispatch read-loop stall on WRITE_ZEROES (behind
flag) ([#3048](#3048))
([efd3d4d](efd3d4d))
* **orch:** split scheduling base build id per artifact
([#2920](#2920))
([3e35a2a](3e35a2a))
* **orch:** validate copy-build -gdb buckets before the snapshot copy
([#3446](#3446))
([9be382f](9be382f))
* **shared:** never report a failed envd command stream as success
([#3281](#3281))
([69c06b6](69c06b6))
* **storage:** compression upload & cache correctness fixes
([#3231](#3231))
([980748f](980748f))
* **storage:** don't assume V4+ ancestor gaps are uncompressed
([#3447](#3447))
([f828d12](f828d12))
* **uffd:** dedupe deferred page faults
([#2864](#2864))
([9680a41](9680a41))
* WrapContextAsUserError should not misclassify internal timeouts as
user cancellations
([#3155](#3155))
([8f83959](8f83959))


### Performance Improvements

* **build:** cache resolved Diff per BuildId within File.ReadAt
([#2838](#2838))
([53de07f](53de07f))
* **build:** parallelize fragmented backing reads
([#2872](#2872))
([c7655a7](c7655a7))
* **clean-nfs-cache:** restore dirfd-relative statx
([#2766](#2766))
([6bdbedb](6bdbedb))
* **header:** add V5 columnar varint header format
([#2847](#2847))
([9dd931b](9dd931b))
* **header:** pack cached Header.Mapping into a compact form
([#2844](#2844))
([7f0b13c](7f0b13c))
* **orchestrator:** add memfile dedup density threshold
([#2862](#2862))
([7ccfa02](7ccfa02))
* **orchestrator:** avoid V3-ancestor header refresh
([#2999](#2999))
([cb6aa0b](cb6aa0b))
* **orch:** metrics for dirty page throttling
([#2858](#2858))
([d2aa554](d2aa554))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: e2b-release-please[bot] <298072688+e2b-release-please[bot]@users.noreply.github.com>
Co-authored-by: Charlie Wyse <charlie.wyse@e2b.dev>
jakubno pushed a commit that referenced this pull request Aug 3, 2026
A pause clones its source header's Builds map, so a mapping-referenced
build whose entry was lost (e.g. the header was peer-served/incomplete
when an earlier pause persisted it) stays absent in every descendant
header. Since #3447 the read path recovers from such gaps, but each
descendant then pays a header refresh per gap forever.

Close the write path: when appendAncestorBuilds resolves nothing locally
and the entry is still missing, load the referenced build's own header
and copy its self entry before persisting. Builds without a header file
(legacy uncompressed) stay absent and keep resolving on the read path.
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.

2 participants