Skip to content

fix(p2p/compression) - upload header synchronization - #2919

Merged
levb merged 9 commits into
mainfrom
lev-ancestor-header-cache
Jun 11, 2026
Merged

levb merged 9 commits into
mainfrom
lev-ancestor-header-cache

Conversation

@levb

@levb levb commented Jun 3, 2026 •

Copy link
Copy Markdown
Contributor

Problem

A descendant B's header doesn't reflect ancestor A's data until B finishes uploading. Resume B early — peer-served, mid-build — and reads of A through B's header can't pick the right path (uncompressed vs .zstd) or hit a peer that just transitioned. Result: silent wrong-bytes or ErrObjectNotExist against the wrong CT.

Mechanism

Refresh loads A's own {A}/{filetype}.header sidecar, extracts A's full-file self-FT, reopens upstream at the resolved CT, and atomically stores {upstream, fullDiffFrameTable} into StorageDiff.source as one unit. Idempotent latch: concurrent calls short-circuit.

Diff highlights

  • StorageDiff: source atomic pointer; lock-free resolve (4 branches); RefreshSource → reloadSource(cause) → reloadSourceLocked when need to refresh
  • File.createDiff: owns the flow for creating the diff including obtaining an authoritative Size.
  • peerSeekable.Size: returns PeerTransitionedError on any miss (no caller-FT means no safe CT guess).
  • PeerRouted marker for type-assert discrimination at the build/peerclient boundary.
  • header.LoadHeader → (*Header, int, error), storage.UncompressedFrameTable sentinel, RangeOpener interface with per-call FT.
  • Telemetry: orchestrator.storage.diff.frame_table_refresh (duration + bytes + count) attributed by cause × file_type.

@linear-code

linear-code Bot commented Jun 3, 2026 •

Copy link
Copy Markdown
Contributor

ENG-4011

ENG-3658

@cla-bot cla-bot Bot added the cla-signed label Jun 3, 2026
@codecov

codecov Bot commented Jun 3, 2026 •

Copy link
Copy Markdown

❌ 3 Tests Failed:

Tests completed Failed Passed Skipped
2798 3 2795 7
View the full list of 3 ❄️ flaky test(s)
github.com/e2b-dev/infra/tests/integration/internal/tests/api/sandboxes::TestSandboxListPaginationRunningLargerLimit

Flake rate in main: 37.67% (Passed 1034 times, Failed 625 times)

Stack Traces | 93.6s run time
=== RUN   TestSandboxListPaginationRunningLargerLimit
    sandbox_list_test.go:327: Created sandbox 1/12: i2abpj7360p6ocbd8kdra
    sandbox_list_test.go:327: Created sandbox 2/12: immjskw0hls1gp9pvsrnu
    sandbox_list_test.go:327: Created sandbox 3/12: ipxciveswqx9hhcuogko3
    sandbox_list_test.go:327: Created sandbox 4/12: is51w9ryircuvje799xik
    sandbox_list_test.go:327: Created sandbox 5/12: ipzkcuvru6rr2slm0egpn
    sandbox_list_test.go:327: Created sandbox 6/12: i654urfkqj5bo9o4y3eao
    sandbox_list_test.go:327: Created sandbox 7/12: i4j2maznzfy3ubdxlbiuw
    sandbox_list_test.go:327: Created sandbox 8/12: iwxx77ep1q47lzrh7ew9d
    sandbox_list_test.go:327: Created sandbox 9/12: iqln3h225egxy9nt0iqyk
    sandbox_list_test.go:327: Created sandbox 10/12: i27vmkvoqrjabk4g2i8he
    sandbox_list_test.go:327: Created sandbox 11/12: ir0sk04j7rc8it5wcr934
    sandbox_list_test.go:327: Created sandbox 12/12: iwhbkxzbnyr0nlsvcp8wb
    sandbox_list_test.go:330: 
        	Error Trace:	.../api/sandboxes/sandbox_list_test.go:340
        	            				.../hostedtoolcache/go/1.26.3.../src/runtime/asm_amd64.s:1771
        	Error:      	"[]" should have 12 item(s), but has 0
    sandbox_list_test.go:330: 
        	Error Trace:	.../api/sandboxes/sandbox_list_test.go:330
        	Error:      	Condition never satisfied
        	Test:       	TestSandboxListPaginationRunningLargerLimit
--- FAIL: TestSandboxListPaginationRunningLargerLimit (93.62s)
github.com/e2b-dev/infra/tests/integration/internal/tests/orchestrator::TestSandboxMemoryIntegrity

Flake rate in main: 52.45% (Passed 1027 times, Failed 1133 times)

Stack Traces | 61.1s run time
=== RUN   TestSandboxMemoryIntegrity
=== PAUSE TestSandboxMemoryIntegrity
=== CONT  TestSandboxMemoryIntegrity
    sandbox_memory_integrity_test.go:27: Build completed successfully
--- FAIL: TestSandboxMemoryIntegrity (61.09s)
github.com/e2b-dev/infra/tests/integration/internal/tests/orchestrator::TestSandboxMemoryIntegrity/tmpfs_hash

Flake rate in main: 52.45% (Passed 1017 times, Failed 1122 times)

Stack Traces | 195s run time
=== RUN   TestSandboxMemoryIntegrity/tmpfs_hash
=== PAUSE TestSandboxMemoryIntegrity/tmpfs_hash
=== CONT  TestSandboxMemoryIntegrity/tmpfs_hash
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{start:{pid:1259}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stdout:"Total memory: 985 MB\nUsed memory before tmpfs mount: 187 MB\nFree memory before tmpfs mount: 797 MB\nMemory to use in integrity test (60% of free, min 64MB): 478 MB\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"478+0 records in\n478+0 records out\n501219328 bytes (501 MB, 478 MiB) copied, 2.50387 s, 200 MB/s\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"\t"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"C"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"o"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"m"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"m"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"a"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"nd being timed: \"dd if=/dev/urandom of=/mnt/testfile bs=1M count=478\"\n\tUser time (seconds): 0.00\n\tSystem time (seconds)"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:":"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:" 2.44"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"\n\tPerc"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ent o"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"f CPU"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:" this job got: 97%"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"\n\tElap"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"sed ("}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"wall "}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"clock) t"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ime ("}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"h:mm:"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ss or"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:" m:"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ss): 0:02.50\n\tAverage shared text si"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"z"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"e (kbytes): "}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"0\n\tA"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ve"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"r"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"age unshared data"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:" size (kb"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ytes): "}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"0\n\tAverage"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:" stack"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:" size"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:" (kby"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"tes): "}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"0\n\tAver"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"age t"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"otal "}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"size ("}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"kbyte"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"s): 0\n\tM"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"aximu"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"m res"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ident set size (kbytes): 2708\n\tAverage resid"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ent set size"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:" (kbyt"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"es): "}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"0\n\tMa"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"jor ("}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"requiring"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:" I/O) "}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"page "}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"faults"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:": 2\n\t"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"Minor (r"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"eclaim"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ing a"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:" fram"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"e)"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:" "}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"page faults: 344\n\tVoluntary cont"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ext sw"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"itches"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:": 3\n\tInvolu"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ntary"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:" cont"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ext swit"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ches:"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:" 11\n\tSwap"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"s: 0\n\tFile system inputs: 176\n\tFile system outputs: 0\n\tSocket messages sent: 0\n\tSocket messages received: 0\n\tSignals delivered: 0\n\tPage size (bytes): 4096\n\tExit status: 0\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stdout:"Used memory after tmpfs mount and file fill: 669 MB\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{end:{exited:true status:"exit status 0"}}
    sandbox_memory_integrity_test.go:70: Command [bash] completed successfully in sandbox i4z3c4gra49f4u20gwdx0
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
    sandbox_memory_integrity_test.go:80: Command [bash] output: event:{start:{pid:1276}}
Executing command bash in sandbox ioffaukhv04m3ro8iieud (user: root)
    sandbox_memory_integrity_test.go:80: Command [bash] output: event:{data:{stdout:"807b48b9bcd99f94406a9327f7b72eb0decedd648275fddb823a5b9c26782c7d\n"}}
    sandbox_memory_integrity_test.go:80: Command [bash] output: event:{end:{exited:true status:"exit status 0"}}
    sandbox_memory_integrity_test.go:80: Command [bash] completed successfully in sandbox i4z3c4gra49f4u20gwdx0
    sandbox_memory_integrity_test.go:80: Command [bash] output: event:{start:{pid:1279}}
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
Executing command bash in sandbox i4z3c4gra49f4u20gwdx0 (user: root)
    sandbox_memory_integrity_test.go:110: 
        	Error Trace:	.../tests/orchestrator/sandbox_memory_integrity_test.go:81
        	            				.../hostedtoolcache/go/1.26.3.../src/runtime/asm_amd64.s:1771
        	Error:      	Received unexpected error:
        	            	failed to execute command bash in sandbox i4z3c4gra49f4u20gwdx0: unavailable: HTTP status 502 Bad Gateway
    sandbox_memory_integrity_test.go:110: 
        	Error Trace:	.../tests/orchestrator/sandbox_memory_integrity_test.go:78
        	            				.../tests/orchestrator/sandbox_memory_integrity_test.go:110
        	Error:      	Condition never satisfied
        	Test:       	TestSandboxMemoryIntegrity/tmpfs_hash
--- FAIL: TestSandboxMemoryIntegrity/tmpfs_hash (194.98s)

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

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the block streaming chunker and build file reading logic to support on-demand frame table refreshing and recovery from peer transitions or missing object errors. It replaces the StreamingReader interface with RangeOpener and updates StorageDiff to dynamically refresh the frame table from the ancestor header when needed. No review comments were provided, and there are no critical findings to report.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@levb levb changed the title WIP fix(p2p/compression) - ENG-4011 fix(p2p/compression) - multiple layer upload header synchronization Jun 4, 2026
@levb levb changed the title fix(p2p/compression) - multiple layer upload header synchronization fix(p2p/compression) - upload header synchronization Jun 4, 2026
@levb
levb marked this pull request as ready for review June 4, 2026 17:27
@levb
levb marked this pull request as draft June 5, 2026 13:37
@levb
levb marked this pull request as ready for review June 8, 2026 14:26

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4d2309386f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/orchestrator/pkg/sandbox/build/build.go Outdated
Comment thread packages/orchestrator/pkg/sandbox/template/peerclient/seekable.go Outdated
Comment thread packages/orchestrator/pkg/sandbox/build/build.go Outdated
Comment thread packages/orchestrator/pkg/sandbox/build/cache.go

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 87365d6629

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/orchestrator/pkg/sandbox/build/storage_diff.go
Lev Brouk added 2 commits June 9, 2026 14:31
levb pushed a commit that referenced this pull request Jun 10, 2026
Add a FullFrameTable wrapper for the two narrow contexts where the FT
covers a whole file: (a) fresh out of compressStream / CompressBytes /
StoreFile / UploadFramed, and (b) the future StorageDiff fallback latch
(#2919). Producers return *FullFrameTable; downcast via Table() to feed
the hot read path or assign to BuildData.FrameData.

FrameTable itself stays the authoritative type — what BuildData stores,
what DeserializeFrameTable and TrimToRanges return, what ReadAt and
OpenRangeReader accept. No on-disk format change, no behavior change.
// peer says "go to storage" → refresh authoritative header → continue.
// No 404-driven recovery.
func initialSize(ctx context.Context, upstream storage.Seekable) (size int64, ok bool, err error) {
if _, peerRouted := upstream.(peerclient.PeerRouted); !peerRouted {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

can we avoid the cast "upstream.(peerclient.PeerRouted)" or not really?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is the signal - the upstream object was just resolved, and it's either a peerRouted or not... how else would I figure it out? We could add a return value and call a method but it'd be redundant.

Comment on lines 295 to 302
timer := time.NewTimer(transErr.RetryAfter)
defer timer.Stop()

select {
case <-timer.C:
case <-ctx.Done():
return false, ctx.Err()
return ctx.Err()
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

tiny syntactic improvement, but you can use time.After(duration) instead of creating and managing a timer yourself:

select {
case <-time.After(transErr.RetryAfter):
case <-ctx.Done():
	return false, ctx.Err()
}

}

if size == 0 {
// (d) and degenerate (a) where bd.Size was zero. Ask storage directly.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

(d) and (a)?

// resolve picks the (upstream, ft) the next read should use, given the
// caller's per-mapping FT hint. The contract: if there is no authoritative FT
// latched AND no peer currently serving this build, we MUST refresh before
// reading. The latched upstream was opened at the bootstrap-guessed CT path

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

"CT"?

return b.reloadSource(ctx, refreshCausePeerTransitioned)
}

// reloadSource is the idempotent ensure-latched entry. Both RefreshSource

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

"ensure-latched"?

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 34524c33a7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/orchestrator/pkg/sandbox/build/build.go
Comment on lines +224 to +242
func (b *StorageDiff) RefreshSource(ctx context.Context) error {
return b.reloadSource(ctx, refreshCausePeerTransitioned)
}

// reloadSource is the idempotent ensure-latched entry. Both RefreshSource
// (PeerTransitionedError) and resolve (read-time peer-left fallback) funnel
// through it; the cause attribute distinguishes them in telemetry. A concurrent
// caller that wins the mutex short-circuits when the latch is already
// populated, so parallel segment reads on a fresh StorageDiff pay only one
// header fetch.
func (b *StorageDiff) reloadSource(ctx context.Context, cause string) error {
b.refreshMu.Lock()
defer b.refreshMu.Unlock()
if b.source.Load().fullDiffFrameTable != nil {
return nil
}

return b.reloadSourceLocked(ctx, cause)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: do we need both these functions?

// authoritative empty &{} FT at construction, so resolve short-circuits and
// reloadSource is never called; V3 builds aren't peer-routed so
// PeerTransitionedError never fires against them either.
func (b *StorageDiff) reloadSourceLocked(ctx context.Context, cause string) error {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this looks to be called only in reloadSource, making merge of the 3 functions to 1

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll send a separate PR for this; agreed.

@levb
levb merged commit 1c73a49 into main Jun 11, 2026
53 checks passed
@levb
levb deleted the lev-ancestor-header-cache branch June 11, 2026 21:59
ValentaTomas added a commit that referenced this pull request Jun 11, 2026
Main is broken: #2919 moved the upstream `Seekable` out of
`block.NewChunker` and into `Chunker.Slice` (as a `storage.RangeOpener`
param), and #2985 merged alongside it without picking up the new
signatures, so `cmd/inspect-build` fails to compile (see [this
run](https://github.com/e2b-dev/infra/actions/runs/27380475452/job/80915690448)).

Updates `validate.go` to the new API: `openChunker` now also returns the
storage object, which is passed to each `Slice` call — mirroring the
production read path in `storage_diff.go`.
ValentaTomas added a commit that referenced this pull request Jun 12, 2026
…ng (#2994)

Since #2919, `createDiff`/`reloadSource` handle an ancestor build
missing from a V4+ header's `Builds` map by loading the ancestor's own
header and requiring a self entry (`SelfBuildData`). Pre-V4 ancestor
headers have no `Builds` map at all — `appendAncestorBuilds`
deliberately skips them on upload — so the lookup fails, `planRead`
errors, the guest gets EIO from NBD, and envd init returns 400. On
staging this kills every resume of pre-V4-era templates: ~650 creates/h
failing with INTERNAL since the `ed6f90a` deploy (previously 0),
observable as the disappearance of the 1 GiB snapshot cohort and all
pause-path rootfs uploads.

Fix: when the refreshed ancestor header is pre-V4, latch it as
authoritatively uncompressed (`UncompressedFullFrameTable`, data at the
basic path, size from storage) in both refresh paths — same semantics
the read path had before #2919. V4+ headers keep the strict self-entry
requirement (a missing self entry there still indicates a peer's
in-flight header and must fail loudly).

Regression test: V4 header mapping to a V3 ancestor with no Builds entry
now reads raw bytes from the uncompressed path.
Piwriw pushed a commit to Piwriw/infra that referenced this pull request Jun 12, 2026
Piwriw pushed a commit to Piwriw/infra that referenced this pull request Jun 12, 2026
…ev#2989)

Main is broken: e2b-dev#2919 moved the upstream `Seekable` out of
`block.NewChunker` and into `Chunker.Slice` (as a `storage.RangeOpener`
param), and e2b-dev#2985 merged alongside it without picking up the new
signatures, so `cmd/inspect-build` fails to compile (see [this
run](https://github.com/e2b-dev/infra/actions/runs/27380475452/job/80915690448)).

Updates `validate.go` to the new API: `openChunker` now also returns the
storage object, which is passed to each `Slice` call — mirroring the
production read path in `storage_diff.go`.
Piwriw pushed a commit to Piwriw/infra that referenced this pull request Jun 12, 2026
…ng (e2b-dev#2994)

Since e2b-dev#2919, `createDiff`/`reloadSource` handle an ancestor build
missing from a V4+ header's `Builds` map by loading the ancestor's own
header and requiring a self entry (`SelfBuildData`). Pre-V4 ancestor
headers have no `Builds` map at all — `appendAncestorBuilds`
deliberately skips them on upload — so the lookup fails, `planRead`
errors, the guest gets EIO from NBD, and envd init returns 400. On
staging this kills every resume of pre-V4-era templates: ~650 creates/h
failing with INTERNAL since the `ed6f90a` deploy (previously 0),
observable as the disappearance of the 1 GiB snapshot cohort and all
pause-path rootfs uploads.

Fix: when the refreshed ancestor header is pre-V4, latch it as
authoritatively uncompressed (`UncompressedFullFrameTable`, data at the
basic path, size from storage) in both refresh paths — same semantics
the read path had before e2b-dev#2919. V4+ headers keep the strict self-entry
requirement (a missing self entry there still indicates a peer's
in-flight header and must fail loudly).

Regression test: V4 header mapping to a V3 ancestor with no Builds entry
now reads raw bytes from the uncompressed 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.

3 participants