Repository navigation
bug(vm): gateway restart can retain stale supervisor ownership and fail sandbox recovery #3546
Description
Activity
🏗️ build-plan
Implementation Plan
Issue type:
fix
Complexity: Medium
Confidence: High — reproduced with delayed ownership cleanupSummary
Track supervisor connection initialization and session cleanup independently of the live-session registry. Graceful shutdown will close admission, complete compute cleanup, signal remaining session loops, and wait for a bounded ownership-cleanup drain before exiting.
Scope
crates/openshell-server/src/supervisor_session.rs: lifecycle tracking before owner publication, cancellation-safe setup ownership, explicit shutdown signal, bounded drain, and deterministic regression tests.crates/openshell-server/src/lib.rs: shutdown ordering and error handling that still drains sessions if compute cleanup fails.architecture/gateway.md: document the shutdown/ownership invariant.
Implementation Steps
- Add tracking that covers pending handshakes, superseded sessions, and sessions removed from the in-memory registry until their owner release finishes. Use existing Tokio synchronization primitives where practical.
- Keep connection setup owned by a task so request cancellation cannot abandon cleanup after owner publication. Close admission before stopping compute; signal session exit and drain afterward.
- Retain conditional owner deletion and report bounded shutdown failures without changing ownership TTLs or supervisor startup timeouts.
- Add regression coverage and validate the existing VM restart E2E, including the controlled two-second cleanup delay in a disposable checkout.
Test Plan
- Unit/integration: delayed owner deletion after registry removal; pending admission and rejection after shutdown; old-session cleanup preserving replacement ownership; bounded drain timeout; shutdown signal reaching already-active and late-starting session tasks.
- E2E: focused VM gateway restart, with and without diagnostic cleanup delay. ARM64 local validation is available; x86-64 validation comes from CI.
- Run repository tests, pre-commit, and local CI checks before publishing a PR.
Risks & Open Questions
- Existing HTTP connections can submit RPCs after listener shutdown, so admission and lifetime tracking must cover that ordering.
- Do not stop Kubernetes workloads when draining control sessions.
- An empty registry is not proof that database cleanup completed.
- Persistence failure must remain visible and shutdown bounded; preserve session/replica/resource-version protections.
Documentation Impact
Update the gateway architecture overview. No public configuration, driver defaults, process identity,
/procaccess, or LSM behavior changes are planned.
Revision 1 — initial plan, reviewed against current main
🏗️ build-from-issue-agent
Implementation Complete
PR: #3547
The gateway now closes supervisor-session admission, completes compute shutdown, signals remaining control sessions, and waits up to ten seconds for tracked cleanup. Tracking begins before ownership publication and continues through owner release, including initialization and sessions already removed from the live registry. Existing conditional owner-deletion protections remain intact.
Validation passed: five new deterministic shutdown regression tests,
mise run pre-commit, andmise run ci(including 1,754 passing server tests with 8 ignored). The existing VM restart test and both CLI conformance scenarios passed on Linux ARM64, both normally and with the same diagnostic two-second cleanup delay that previously reproduced the failure. That instrumentation is excluded from the PR. Linux x86-64 validation remains for CI.Updated the gateway architecture, public lifecycle documentation, and cluster diagnostic skill. No issue workflow labels were changed. The PR links this issue for closure when merged.
- added a commit that references this issue
on Sep 22, 2026
User Story
As an operator running VM-backed sandboxes, I want a graceful gateway restart to restore running sandboxes and preserve explicitly stopped sandboxes, so that routine restarts do not leave workloads in
Error.Problem Statement
The gateway can exit before an asynchronous supervisor-session cleanup task deletes its persisted ownership record. After restart, the new supervisor cannot replace the still-fresh record belonging to the previous instance.
ConnectSupervisorreturnsUnavailable; the supervisor's 10-second acceptance timeout expires before the 45-second ownership TTL, and the sandbox entersError.Observed in
vm_gateway_restart_preserves_running_and_stopped_intenton both attempts of the VM CI job for #3386:The race mechanism was subsequently reproduced locally on main at
50230616d51f352954450c527f9dd64218be0b80using a controlled delay in ownership cleanup. This establishes the failure mechanism, but does not establish the precise scheduling or database delay in CI.Impact / Why This Matters
A graceful restart can leave a previously healthy, running-intent sandbox unavailable instead of restoring it. The VM restart E2E failed twice in CI, each time exhausting the readiness wait. Retrying CI did not resolve this occurrence, and repeated manual retries are not a reliable recovery strategy for workloads. No reliable operational workaround was validated in this investigation.
Acceptance Criteria
Reproduction Steps
Existing E2E, without instrumentation
On a machine with the VM driver's documented prerequisites (Linux KVM or supported macOS virtualization, repository build dependencies, and container/image tooling), check out
50230616d51f352954450c527f9dd64218be0b80in a disposable checkout and run:The test creates a running VM sandbox, writes and flushes an overlay marker, creates and explicitly stops another sandbox, then stops and restarts the gateway. It expects the first sandbox to execute
cat /sandbox/vm-gateway-start-stateand returnbefore-restartwhile the second remains stopped.This failure is timing-sensitive. Three uninstrumented local runs with the failing PR's ARM64 binaries passed. A control using the gateway built from updated main also passed in 23.25 seconds.
Controlled reproduction used in the investigation
In the disposable checkout, edit
crates/openshell-server/src/supervisor_session.rs. Inside the detached task inhandle_connect_supervisor, immediately beforeowner_index.release_if_current(&owner_guard).await, insert:The following reproduces the tested Linux ARM64 binary combination: build the gateway from main and reuse the other executables from the failing CI run. Artifact downloads require GitHub access and are subject to retention; source builds via
mise run e2e:vmare the alternative once artifacts expire.For the control, run the same command without
DIAGNOSTIC_VM_OWNER_RELEASE_DELAY_MS. Remove the diagnostic hook and rebuild after investigation.Observed: the delay-start message appeared, but the delay-completed message did not: the old gateway exited while cleanup was pending. A read-only query of the per-run SQLite
objectstable, filtered toobject_type = 'supervisor_session_owner', confirmed that the previous session and replica still owned the sandbox after restart. New supervisor connections were rejected, and the full test failed attests/vm_gateway_start.rs:161after 260.04 seconds, exit code 101.Environment
c1bb9fdc0d9bedb30d2096880dfeabeb4b8f8a69.6.17.0-1018-nvidia, KVM available.50230616d51f352954450c527f9dd64218be0b80, after refactor(runtime): retire Community image dependencies #3386 merged.nvcr.io/nvidia/base/ubuntu:24.04.ghcr.io/astral-sh/uv:0.12.17-python3.12-trixie-slim@sha256:9a59bb7206905ccaae4f7dab222fbac47c125a21e5fc16f43f427cd6c940ade3.v0.0.116; this report concerns the tested commits, and that release was not tested.Logs
Condensed local diagnostic timeline; identifiers omitted:
Investigation Notes
tokio::spawntask. It removes the in-memory session before awaiting persisted owner deletion. Merely waiting for the registry to become empty would not close this gap.The fix direction is a bounded shutdown drain that tracks cleanup completion through the conditional database deletion, preserving the existing replacement-session protections. A shutdown sleep or longer startup timeout would mask the missing synchronization.
The workload image was identical between the passing control and failing diagnostic run. Image-dependent timing may expose the race, but a direct image incompatibility was not demonstrated. No production fix was applied during this investigation.