Live resize: nothing observable after `Modify` returns `converging` — request a re-readable status or a wait/completion signal
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.16andbd69a8f598d6(releases/v0.7.0preview); 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
- Grow 4→8 vCPU / 8→16 GiB via
Modify(ModifyOptions{CPUs: 8, MemoryMiB: 16384, Policy: ModificationPolicyNoRestart}). The call returned in 1.4 ms withapplied: true. The plan'sresize_statusentries carried pre-plugactualvalues (4and8 GiB) while the guest converged asynchronously — about 1.1 s later on an idle node, 8–20 s on our production nodes. - 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_configinsdk/rust/lib/sandbox/modify.rs, commented "so inspect does not report the already-live change as pending"). - From the code (
sdk/rust/lib/sandbox/modify.rs):resize_statusis computed once from thecontrol_cpu_target/control_memory_targetreadback; the dry-run plan is built withresize_status: Vec::new(); the SDK has no wait or event surface for it. - Related, already acknowledged: the memory classification is
current_mib >= target_mib, so any shrink reportsappliedimmediately 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)
- Re-readable state. Keep the per-resource
ResourceResizeStatuson the sandbox record until each resource reaches a terminal state (applied,guest-refused,failed), refreshingactualfrom the guest driver, and expose it throughGetSandbox/ inspect andmsb ps --format json. - A wait helper, mirroring the
RequestStop/WaitUntilStoppedsplit:Sandbox.WaitUntilResized(ctx)(orModify(..., Wait: true)with a timeout) returning the finalresize_status. - 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