Live resize: nothing observable after `Modify` returns `converging` — request a re-readable status or a wait/completion signal

Author: bgeilsCreated Sep 14, 2026Updated Sep 22, 2026
LabelsSDKFilesystemsCLI

Summary

Sandbox.Modify with a live CPU or memory target returns a plan whose resize_status is a single readback taken at apply time. When a resource comes back converging, nothing afterwards tells the caller when the guest has finished: inspect / GetSandbox already reports the requested target as the effective value, a dry-run or idempotent re-apply returns resize_status: [], and there is no event or wait helper. The only working signal today is to exec into the guest and poll nproc and /proc/meminfo.

Filed at Steve's request (shared channel, 2026-09-14) as the completion-event item, separate from the premature applied on memory shrink and from #1512 (memory.limit gauge).

Environment

  • Go SDK v0.6.16 and bd69a8f598d6 (releases/v0.7.0 preview); runtime 0.6.18
  • Linux x86-64 KVM host (Intel 8375C); Ubuntu 24.04 guest booted at 4 vCPU / 8 GiB with max_cpus 16, max_memory 32 GiB

Observed

  1. Grow 4→8 vCPU / 8→16 GiB via Modify(ModifyOptions{CPUs: 8, MemoryMiB: 16384, Policy: ModificationPolicyNoRestart}). The call returned in 1.4 ms with applied: true. The plan's resize_status entries carried pre-plug actual values (4 and 8 GiB) while the guest converged asynchronously — about 1.1 s later on an idle node, 8–20 s on our production nodes.
  2. Nothing re-readable afterwards. A dry-run re-plan and an idempotent re-apply of the same target both return resize_status: []. GetSandbox / inspect reports the requested values, because apply persists the target into the active config (persist_active_config in sdk/rust/lib/sandbox/modify.rs, commented "so inspect does not report the already-live change as pending").
  3. From the code (sdk/rust/lib/sandbox/modify.rs): resize_status is computed once from the control_cpu_target / control_memory_target readback; the dry-run plan is built with resize_status: Vec::new(); the SDK has no wait or event surface for it.
  4. Related, already acknowledged: the memory classification is current_mib >= target_mib, so any shrink reports applied immediately even though the guest has not unplugged yet.

Why it matters

An orchestrator grows a sandbox at claim time and must not advertise or account the new capacity before the guest actually has it. Guest-side polling needs an exec session per sandbox, cannot distinguish "not yet" from guest-refused, and does not scale to hundreds of concurrent resizes. We currently poll nproc / MemTotal eight times over 16 s and rewrite the persisted actual values ourselves.

Proposal (any one of these would do)

  1. Re-readable state. Keep the per-resource ResourceResizeStatus on the sandbox record until each resource reaches a terminal state (applied, guest-refused, failed), refreshing actual from the guest driver, and expose it through GetSandbox / inspect and msb ps --format json.
  2. A wait helper, mirroring the RequestStop / WaitUntilStopped split: Sandbox.WaitUntilResized(ctx) (or Modify(..., Wait: true) with a timeout) returning the final resize_status.
  3. Optionally, a completion event on the sandbox event stream for callers that already subscribe.

For shrink, applied should additionally require the guest to have released the memory (the acknowledged follow-up).

Source: superradcompany/microsandbox