Skip to content

objectstorage: size small snapshot upload buffers to actual data - #2945

Merged
Haven Xia (HavenXia) merged 1 commit into
agent-substrate:mainfrom
chw120:fix-small-upload-memory
Oct 20, 2026
Merged

Haven Xia (HavenXia) merged 1 commit into
agent-substrate:mainfrom
chw120:fix-small-upload-memory

Conversation

@chw120

Copy link
Copy Markdown
Collaborator

Summary

Follow-up to #2848 (and #2847). While #2848 fixed upload memory accounting for multi-part ranged sparse files, small single-request files (< 64 MiB) still allocated and reserved worst-case 64 MiB buffers at three layers (64 MiB GCS lookahead + 64 MiB GCS chunk buffer + 64 MiB zstd worker = 192 MiB per file, and 128 MiB for manifest.json).

Under the default --upload-memory-budget=512Mi, a small gVisor snapshot (~7 files + manifest.json, ~17 MB total) still required > 1.2 GiB of budget reservations, forcing concurrent SuspendActor uploads to queue behind the semaphore (2–3 files at a time).

Changes

  • Proportional GCS single-request buffers: When the upload stream has a known size bound below 64 MiB, size both the PutObject lookahead buffer and the single-request Writer.ChunkSize to the actual object size (rounded up to 256 KiB multiples, 512 KiB minimum total instead of 128 MiB).
  • Right-sized zstd compression chunks: For sparse files with < 8 MiB of populated data, scale the per-frame compression chunk and zstd window down to a power of two in [1 MiB, 8 MiB], reducing per-worker memory from 64 MiB down to as low as 8 MiB.
  • Budget accounting: Update Uploader planning to reserve these proportional buffer sizes (8.5 MiB minimum for small sparse files instead of 192 MiB), allowing all files in a snapshot burst to upload concurrently within the default 512Mi budget. Large files (>= 64 MiB) and ranged sparse uploads (parts >= 2) are unchanged.

Test Plan

1. GKE Cluster Benchmark (glutton_baseline_10_users, 10 gVisor workers, default 512Mi budget)

Metric (grpc_SuspendActor) Oct 15 Baseline (pre-#2708) main (90de075f, with #2848) This PR Change vs main
p50 400 ms 850 ms 390 ms -54.1%
p75 — 1,000 ms 420 ms -58.0%
p90 460 ms 1,400 ms 450 ms -67.9%
p95 — 1,600 ms 520 ms -67.5%
p99 610 ms 1,700 ms 640 ms -62.4%
Average — 905.4 ms 414.3 ms -54.2%
Throughput — 4.39 req/s 5.73 req/s +30.5%

2. Microbenchmarks (pkg/objectstorage/upload_bench_test.go)

Benchmark (512 MiB budget, 50 ms RTT) main (#2848) This PR Speedup / Memory
BenchmarkConcurrentSnapshotUploads/gcs_shared_512MiB (80 small files) 2,085 ms / 5,375 MiB 73.6 ms / 83.2 MiB 28.4x faster, -98.5% B/op
BenchmarkLargeSparseUpload/4_actors_gvisor_7files_each (4× [256 MiB ranged + 6× 64 KiB]) 905.5 ms / 1,613 MiB 358.4 ms / 138 MiB 2.5x faster, -91.4% B/op
BenchmarkLargeSparseUpload/1_actor_256MiB_ranged (large file only) 115.6 ms 114.6 ms Unchanged

Part of #2553

Comment thread pkg/objectstorage/gcs.go Outdated
Comment thread pkg/objectstorage/gcs.go
Comment thread pkg/objectstorage/sparsezstd.go
On GCS, streamed snapshot files smaller than uploadCompositeMin (64 MiB)
allocated and reserved 64 MiB for PutObject's lookahead buffer, 64 MiB for
putSingle's Writer.ChunkSize, and 64 MiB for parZstd's 8 MiB chunk buffers.
Each small file reserved 192 MiB against the 512 MiB default upload budget,
serializing a 7-file actor snapshot into two-file waves.

Wrap bounded upload streams in boundedReader so PutObject and putSingle size
their single-request buffers to the next 256 KiB multiple above the stream
bound. Scale parZstd's frame chunk size down to a power of two in [1 MiB, 8 MiB]
for sparse files with less than 8 MiB of populated data, and update
planStream and streamBufferMemory to reserve the corresponding memory.
@HavenXia
Haven Xia (HavenXia) added this pull request to the merge queue Oct 20, 2026
Merged via the queue into agent-substrate:main with commit d77cfeb Oct 20, 2026
15 checks passed
@chw120
Chenyi Wang (chw120) deleted the fix-small-upload-memory branch October 20, 2026 16:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants