Skip to content

Persist snapshot scheduling metadata#2873

Open
ValentaTomas wants to merge 7 commits into
mainfrom
valenta/snapshot-scheduling-metadata
Open

Persist snapshot scheduling metadata#2873
ValentaTomas wants to merge 7 commits into
mainfrom
valenta/snapshot-scheduling-metadata

Conversation

@ValentaTomas
Copy link
Copy Markdown
Member

@ValentaTomas ValentaTomas commented May 30, 2026

Persist compact snapshot scheduling metadata from pause/checkpoint so future placement can use real build-chain ancestry. Metadata persistence is best-effort and does not change snapshot success semantics.

Return compact build-chain metadata from snapshot creation so API placement can later use real build ancestry for resume affinity.
@cla-bot cla-bot Bot added the cla-signed label May 30, 2026
@cursor
Copy link
Copy Markdown

cursor Bot commented May 30, 2026

PR Summary

Medium Risk
Changes the Pause gRPC response shape and adds placement-related metadata on the snapshot path; persistence is best-effort but incorrect metadata could affect future scheduling decisions.

Overview
Pause and checkpoint now return compact snapshot scheduling metadata (base build, generation, mapping counts, and a capped build chain with per-build byte contributions) derived from memfile/rootfs headers at snapshot time. The API best-effort persists that payload under snapshot_scheduling_metadata in snapshot JSON metadata after a successful pause or template checkpoint, without changing whether those operations succeed. Re-upserting a snapshot keeps any existing scheduling metadata instead of overwriting it, and a LaunchDarkly flag caps how many chain entries are included.

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

Comment thread packages/orchestrator/pkg/server/sandboxes.go
@codecov
Copy link
Copy Markdown

codecov Bot commented May 30, 2026

❌ 5 Tests Failed:

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

Flake rate in main: 42.88% (Passed 742 times, Failed 557 times)

Stack Traces | 98.4s run time
=== RUN   TestSandboxListPaginationRunningLargerLimit
    sandbox_list_test.go:327: Created sandbox 1/12: ixzli64elmipm7w26sbqu
    sandbox_list_test.go:327: Created sandbox 2/12: i4l38ifs4h236swx56cw3
    sandbox_list_test.go:327: Created sandbox 3/12: ivpb2s55714nxgz4y15z9
    sandbox_list_test.go:327: Created sandbox 4/12: iqo7cxu9iiwue8itme7ai
    sandbox_list_test.go:327: Created sandbox 5/12: ihrmx5cbv29n0xhs6tq7s
    sandbox_list_test.go:327: Created sandbox 6/12: i7tcnmbsyj62tyb1uznce
    sandbox_list_test.go:327: Created sandbox 7/12: i4tqsqbq9ppenv125yjx5
    sandbox_list_test.go:327: Created sandbox 8/12: i41krss0787dxzgievsff
    sandbox_list_test.go:327: Created sandbox 9/12: ier606tbng5zxqw0w70kf
    sandbox_list_test.go:327: Created sandbox 10/12: iuk6gqt7ye34bylrdteey
    sandbox_list_test.go:327: Created sandbox 11/12: ifsa8l19v4uajhdwjswc7
    sandbox_list_test.go:327: Created sandbox 12/12: i8f2ryculc9jmj2qwkit0
    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 (98.39s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestCommandKillNextApp

Flake rate in main: 42.98% (Passed 731 times, Failed 551 times)

Stack Traces | 193s run time
=== RUN   TestCommandKillNextApp
=== PAUSE TestCommandKillNextApp
=== CONT  TestCommandKillNextApp
Executing command findmnt in sandbox in8ig2qfb7osdu5g588iw (user: root)
    process_test.go:28: Command [npx] output: event:{start:{pid:1264}}
Executing command /bin/bash in sandbox ixvwkbb3w9mf6hkmwn9l2
    process_test.go:28: Command [npx] output: event:{data:{stderr:"npm"}}
    process_test.go:28: Command [npx] output: event:{data:{stderr:" "}}
    process_test.go:28: Command [npx] output: event:{data:{stderr:"WARN"}}
    process_test.go:28: Command [npx] output: event:{data:{stderr:" "}}
    process_test.go:28: Command [npx] output: event:{data:{stderr:"exec The following package was not found and will be installed: create-next-app@16.2.6\n"}}
    process_test.go:28: Command [npx] output: event:{data:{stdout:"Creating a new Next.js app in .../home/user/nextapp.\n"}}
    process_test.go:28: Command [npx] output: event:{data:{stdout:"\n"}}
    process_test.go:28: Command [npx] output: event:{data:{stdout:"Using npm.\n\nInitializing project with template: app-tw \n\n"}}
    process_test.go:28: Command [npx] output: event:{data:{stdout:"\nInstalling dependencies:\n- next\n- react\n"}}
    process_test.go:28: Command [npx] output: event:{data:{stdout:"- react-dom\n\nInstalling devDependencies:\n- @tailwindcss/postcss\n"}}
    process_test.go:28: Command [npx] output: event:{data:{stdout:"- @types/node\n- @types/react\n- @types/react-dom\n- eslint\n- eslint-config-next\n- tailwindcss\n- typescript\n\n"}}
Executing command python in sandbox i24hruurgfg3t3sf41szr
    process_test.go:29: 
        	Error Trace:	.../tests/envd/process_test.go:29
        	Error:      	Received unexpected error:
        	            	failed to execute command npx in sandbox isk03lqiq1w7iqq4yexhc: invalid_argument: protocol error: incomplete envelope: unexpected EOF
        	Test:       	TestCommandKillNextApp
--- FAIL: TestCommandKillNextApp (192.90s)
github.com/e2b-dev/infra/tests/integration/internal/tests/orchestrator::TestSandboxMemoryIntegrity

Flake rate in main: 57.78% (Passed 735 times, Failed 1006 times)

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

Flake rate in main: 57.90% (Passed 725 times, Failed 997 times)

Stack Traces | 197s 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:1265}}
Executing command bash in sandbox iedgf53gham6ni2ohersj (user: root)
    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: 796 MB\nMemory to use in integrity test (60% of free, min 64MB): 477 MB\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"477+0 records in\n477+0 records out\n500170752 bytes (500 MB, 477 MiB) copied, 1.96548 s, 254 MB/s\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"\tCommand being timed: \""}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"dd if=/dev/urandom of=/mnt/testfile bs=1M count=477\"\n\tUse"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"r time ("}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"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:"0.00"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"\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:"S"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"y"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"s"}}
    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:"e"}}
    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:" "}}
    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:"i"}}
    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:"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:"("}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"s"}}
    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:"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:"n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"d"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"s"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:")"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:":"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:" "}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"1.96"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"\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:"P"}}
    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:"r"}}
    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:"e"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"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:" "}}
    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:"f"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:" "}}
    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:"P"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"U"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:" this job got: 99%\n\tElapsed (wall clock) time (h:mm:ss or m:ss): 0:01.97\n\tAverage shared text size (kbytes): 0\n\tAverage unshared data size (kbytes): 0\n\tAverage stack size (kbytes): 0\n\tAverage total size (kbytes): 0\n\tMaximum resident set size (kbytes): 2644\n\tAver"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"age resident se"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"t 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): 0\n\tMajor (requiring I/O) page faults: 2\n\tMinor (reclaiming a frame) page faults: 345\n\tVoluntary context switches: 3\n\tInvoluntary context switches: 21\n\tSwaps: 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: 672 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 ixojw12pf5j437dse5xkr
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
    sandbox_memory_integrity_test.go:80: Command [bash] output: event:{start:{pid:1281}}
Executing command bash in sandbox iwjftfb9sthhzs53p1age (user: root)
    sandbox_memory_integrity_test.go:80: Command [bash] output: event:{data:{stdout:"6fd5202da9a27d61f57a9eb69569bee88ff8ebd05bca7265e7212b9a4edfcfeb\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 ixojw12pf5j437dse5xkr
    sandbox_memory_integrity_test.go:80: Command [bash] output: event:{start:{pid:1284}}
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (user: root)
Executing command bash in sandbox ixojw12pf5j437dse5xkr (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 ixojw12pf5j437dse5xkr: 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 (197.16s)
github.com/e2b-dev/infra/tests/integration/internal/tests/proxies::TestEnvdAccessTokenAutoResumeViaProxy

Flake rate in main: 43.07% (Passed 731 times, Failed 553 times)

Stack Traces | 12s run time
=== RUN   TestEnvdAccessTokenAutoResumeViaProxy
=== PAUSE TestEnvdAccessTokenAutoResumeViaProxy
=== CONT  TestEnvdAccessTokenAutoResumeViaProxy
    traffic_access_token_test.go:357: 
        	Error Trace:	.../tests/proxies/traffic_access_token_test.go:357
        	Error:      	Received unexpected error:
        	            	Get "http://localhost:3002/health": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
        	Test:       	TestEnvdAccessTokenAutoResumeViaProxy
--- FAIL: TestEnvdAccessTokenAutoResumeViaProxy (11.97s)
github.com/e2b-dev/infra/tests/integration/internal/tests/proxies::TestSandboxAutoResumeViaProxy

Flake rate in main: 43.71% (Passed 729 times, Failed 566 times)

Stack Traces | 13.9s run time
=== RUN   TestSandboxAutoResumeViaProxy
=== PAUSE TestSandboxAutoResumeViaProxy
=== CONT  TestSandboxAutoResumeViaProxy
Executing command ls in sandbox iuz0i064vx6gelm60nj0y
    auto_resume_test.go:116: 
        	Error Trace:	.../tests/proxies/auto_resume_test.go:116
        	Error:      	Received unexpected error:
        	            	Get "http://localhost:3002": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
        	Test:       	TestSandboxAutoResumeViaProxy
--- FAIL: TestSandboxAutoResumeViaProxy (13.94s)

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

Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Code Review

In update_snapshot_scheduling_metadata.sql, using to_jsonb on a text parameter stores it as an escaped string scalar rather than a JSONB object, so the parameter should be cast directly to jsonb. In sandboxes.go, accessing res.SchedulingMetadata will cause a compilation error because the snapshotResult struct lacks this field. Additionally, NewSnapshotSchedulingMetadata in snapshot.go is missing nil checks for the Mapping fields of the headers, which can lead to a nil pointer dereference panic.

Comment thread packages/orchestrator/pkg/server/sandboxes.go
Comment thread packages/orchestrator/pkg/server/sandboxes.go
Comment thread packages/orchestrator/pkg/sandbox/snapshot.go
Populate pause/checkpoint responses from snapshot results and store scheduling metadata as JSONB.
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Pause fails after orchestrator pause
    • Added failSnapshotBuild call when updateSnapshotSchedulingMetadata fails after successful pause RPC to properly mark build status as failed.
  • ✅ Fixed: Metadata error finishes snapshot success
    • Added finish(err) call before returning when updateSnapshotSchedulingMetadata fails to prevent deferred finish(nil) from incorrectly marking snapshot as success.

Create PR

Or push these changes by commenting:

@cursor push caa2284235
Preview (caa2284235)
diff --git a/packages/api/internal/orchestrator/pause_instance.go b/packages/api/internal/orchestrator/pause_instance.go
--- a/packages/api/internal/orchestrator/pause_instance.go
+++ b/packages/api/internal/orchestrator/pause_instance.go
@@ -52,6 +52,8 @@
 	}
 	if schedulingMetadata != nil {
 		if err := o.updateSnapshotSchedulingMetadata(ctx, sbx.SandboxID, schedulingMetadata); err != nil {
+			o.failSnapshotBuild(ctx, result.BuildID, err)
+
 			return err
 		}
 	}

diff --git a/packages/api/internal/orchestrator/snapshot_template.go b/packages/api/internal/orchestrator/snapshot_template.go
--- a/packages/api/internal/orchestrator/snapshot_template.go
+++ b/packages/api/internal/orchestrator/snapshot_template.go
@@ -106,6 +106,8 @@
 		if err := o.updateSnapshotSchedulingMetadata(ctx, sbx.SandboxID, metadata); err != nil {
 			o.failSnapshotBuild(ctx, upsertResult.BuildID, err)
 
+			finish(err)
+
 			return SnapshotTemplateResult{}, err
 		}
 	}

You can send follow-ups to the cloud agent here.

Comment thread packages/api/internal/orchestrator/pause_instance.go
Comment thread packages/api/internal/orchestrator/snapshot_template.go
Do not fail a successful pause or checkpoint if persisting optional scheduling metadata fails.
@cursor
Copy link
Copy Markdown

cursor Bot commented May 30, 2026

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Scheduling metadata never returned
    • Added SchedulingMetadata field to snapshotResult struct and populated it from snapshot.SchedulingMetadata so gRPC callers now receive the metadata in Pause and Checkpoint responses.
  • ✅ Fixed: JSONB stores escaped string value
    • Changed SQL query from to_jsonb(@metadata::text) to (@metadata::text)::jsonb to properly parse JSON string as JSONB object instead of storing it as a string scalar.

Create PR

Or push these changes by commenting:

@cursor push 3c928ccbac
Preview (3c928ccbac)
diff --git a/packages/db/queries/snapshots/update_snapshot_scheduling_metadata.sql b/packages/db/queries/snapshots/update_snapshot_scheduling_metadata.sql
--- a/packages/db/queries/snapshots/update_snapshot_scheduling_metadata.sql
+++ b/packages/db/queries/snapshots/update_snapshot_scheduling_metadata.sql
@@ -3,7 +3,7 @@
 SET metadata = jsonb_set(
     metadata,
     '{snapshot_scheduling_metadata}',
-    to_jsonb(@metadata::text),
+    (@metadata::text)::jsonb,
     true
 )
 WHERE sandbox_id = @sandbox_id;

diff --git a/packages/db/queries/update_snapshot_scheduling_metadata.sql.go b/packages/db/queries/update_snapshot_scheduling_metadata.sql.go
--- a/packages/db/queries/update_snapshot_scheduling_metadata.sql.go
+++ b/packages/db/queries/update_snapshot_scheduling_metadata.sql.go
@@ -1,6 +1,6 @@
 // Code generated by sqlc. DO NOT EDIT.
 // versions:
-//   sqlc v1.31.1
+//   sqlc v1.29.0
 // source: update_snapshot_scheduling_metadata.sql
 
 package queries
@@ -14,7 +14,7 @@
 SET metadata = jsonb_set(
     metadata,
     '{snapshot_scheduling_metadata}',
-    to_jsonb($1::text),
+    ($1::text)::jsonb,
     true
 )
 WHERE sandbox_id = $2

diff --git a/packages/orchestrator/pkg/server/sandboxes.go b/packages/orchestrator/pkg/server/sandboxes.go
--- a/packages/orchestrator/pkg/server/sandboxes.go
+++ b/packages/orchestrator/pkg/server/sandboxes.go
@@ -782,9 +782,10 @@
 // snapshotResult holds the data produced by snapshotAndCacheSandbox that
 // callers need to start the background remote storage upload.
 type snapshotResult struct {
-	meta           metadata.Template
-	upload         *sandbox.Upload
-	completeUpload func(ctx context.Context, uploadErr error)
+	meta               metadata.Template
+	upload             *sandbox.Upload
+	completeUpload     func(ctx context.Context, uploadErr error)
+	SchedulingMetadata *orchestrator.SnapshotSchedulingMetadata
 }
 
 // snapshotAndCacheSandbox creates a snapshot of a sandbox and adds it to the
@@ -863,9 +864,10 @@
 	}
 
 	return &snapshotResult{
-		meta:           meta,
-		upload:         upload,
-		completeUpload: completeUpload,
+		meta:               meta,
+		upload:             upload,
+		completeUpload:     completeUpload,
+		SchedulingMetadata: snapshot.SchedulingMetadata,
 	}, nil
 }

You can send follow-ups to the cloud agent here.

Use slices.SortFunc and remove an unnecessary conversion in metadata generation.
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Limit one drops parent build
    • Added condition to prevent base build from overwriting parent build when chain limit is 1 and last element is the parent.

Create PR

Or push these changes by commenting:

@cursor push 527fa53cf5
Preview (527fa53cf5)
diff --git a/packages/orchestrator/pkg/sandbox/snapshot.go b/packages/orchestrator/pkg/sandbox/snapshot.go
--- a/packages/orchestrator/pkg/sandbox/snapshot.go
+++ b/packages/orchestrator/pkg/sandbox/snapshot.go
@@ -139,7 +139,7 @@
 					break
 				}
 			}
-			if !hasBase {
+			if !hasBase && chain[len(chain)-1].buildID != parentBuildID {
 				if c, ok := contributions[baseBuildID]; ok {
 					chain[len(chain)-1] = *c
 				} else {

You can send follow-ups to the cloud agent here.

Comment thread packages/orchestrator/pkg/sandbox/snapshot.go
Avoid replacing the only chain entry with the base build because base build is already stored separately.
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Nested metadata breaks JSONBStringMap scans
    • Changed update_snapshot_scheduling_metadata.sql to use to_jsonb(@metadata::text) instead of (@metadata::text)::jsonb, storing the scheduling metadata as a JSON string value that JSONBStringMap can unmarshal correctly.
  • ✅ Fixed: Upsert clears scheduling metadata key
    • Changed create_new_snapshot.sql conflict handler to use snapshots.metadata || excluded.metadata instead of excluded.metadata, preserving existing metadata keys including snapshot_scheduling_metadata during upsert.

Create PR

Or push these changes by commenting:

@cursor push c71543e849
Preview (c71543e849)
diff --git a/packages/db/queries/create_new_snapshot.sql.go b/packages/db/queries/create_new_snapshot.sql.go
--- a/packages/db/queries/create_new_snapshot.sql.go
+++ b/packages/db/queries/create_new_snapshot.sql.go
@@ -55,7 +55,7 @@
             now()
    )
     ON CONFLICT (sandbox_id) DO UPDATE SET
-        metadata = excluded.metadata,
+        metadata = snapshots.metadata || excluded.metadata,
         sandbox_started_at = excluded.sandbox_started_at,
         allow_internet_access = COALESCE(excluded.allow_internet_access, snapshots.allow_internet_access),
         origin_node_id = excluded.origin_node_id,

diff --git a/packages/db/queries/snapshots/create_new_snapshot.sql b/packages/db/queries/snapshots/create_new_snapshot.sql
--- a/packages/db/queries/snapshots/create_new_snapshot.sql
+++ b/packages/db/queries/snapshots/create_new_snapshot.sql
@@ -41,7 +41,7 @@
             now()
    )
     ON CONFLICT (sandbox_id) DO UPDATE SET
-        metadata = excluded.metadata,
+        metadata = snapshots.metadata || excluded.metadata,
         sandbox_started_at = excluded.sandbox_started_at,
         allow_internet_access = COALESCE(excluded.allow_internet_access, snapshots.allow_internet_access),
         origin_node_id = excluded.origin_node_id,

diff --git a/packages/db/queries/snapshots/update_snapshot_scheduling_metadata.sql b/packages/db/queries/snapshots/update_snapshot_scheduling_metadata.sql
--- a/packages/db/queries/snapshots/update_snapshot_scheduling_metadata.sql
+++ b/packages/db/queries/snapshots/update_snapshot_scheduling_metadata.sql
@@ -3,7 +3,7 @@
 SET metadata = jsonb_set(
     metadata,
     '{snapshot_scheduling_metadata}',
-    (@metadata::text)::jsonb,
+    to_jsonb(@metadata::text),
     true
 )
 WHERE sandbox_id = @sandbox_id;

diff --git a/packages/db/queries/update_snapshot_scheduling_metadata.sql.go b/packages/db/queries/update_snapshot_scheduling_metadata.sql.go
--- a/packages/db/queries/update_snapshot_scheduling_metadata.sql.go
+++ b/packages/db/queries/update_snapshot_scheduling_metadata.sql.go
@@ -1,6 +1,6 @@
 // Code generated by sqlc. DO NOT EDIT.
 // versions:
-//   sqlc v1.31.1
+//   sqlc v1.29.0
 // source: update_snapshot_scheduling_metadata.sql
 
 package queries
@@ -14,7 +14,7 @@
 SET metadata = jsonb_set(
     metadata,
     '{snapshot_scheduling_metadata}',
-    ($1::text)::jsonb,
+    to_jsonb($1::text),
     true
 )
 WHERE sandbox_id = $2

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit af997d5. Configure here.

Comment thread packages/api/internal/orchestrator/pause_instance.go
Store scheduling metadata as a string value in the existing metadata map and keep it across snapshot upserts until refreshed.
@ValentaTomas ValentaTomas marked this pull request as ready for review May 30, 2026 21:58
Comment thread packages/api/internal/orchestrator/pause_instance.go
Comment thread packages/orchestrator/pkg/sandbox/snapshot.go
Comment thread packages/orchestrator/pkg/sandbox/snapshot.go
Comment thread packages/orchestrator/pkg/sandbox/snapshot.go
Comment thread packages/orchestrator/pkg/sandbox/snapshot.go
Comment thread packages/orchestrator/pkg/sandbox/snapshot.go
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