Repository navigation
refactor(compute): standardize lifecycle coordination across drivers #4115
Description
User Story
As an operator, I want sandbox restart and recovery to behave consistently across drivers, so delayed cleanup cannot disrupt a newer run.
Problem Statement
Lifecycle coordination is driver-specific. Background observations or cleanup must not stop, delete, or invalidate a newer runtime. Reused resource IDs do not identify a run. Protecting runtime resources and filtering stale published state are separate guarantees: gateway gates/event filters cannot undo containment already performed inside a driver.
Impact / Why This Matters
Podman reconciliation can stop a newly restarted workload while its supervisor is still exited. Retrying is unreliable if cleanup races recur; isolated fixes leave equivalent paths unaudited. The focused fix is PR #4117. Equivalent failures in other drivers remain unproven.
Proposed Design
- Land the focused Podman fix. Extract an instance-local gate independently of engine-specific exit timestamps. Specify lock lifetime, clone sharing, identity revalidation, rollback and genuine-loss containment. Reconciliation may wait (Podman) or skip a busy sandbox and retry (Kubernetes); document that choice.
- Audit/adopt one driver at a time: lifecycle RPCs, watcher/initial sync, supervisor-loss, get/list side effects, admission, cleanup, cancellation and retry. Preserve readiness, exec, workspace retention and genuine-loss containment.
- Separately design runtime-generation ownership and completion/fencing of accepted remote mutations across cancellation, independent processes and HA. A local mutex alone cannot supply these guarantees.
Acceptance Criteria
- Shared contract documents runtime-resource protection separately from stale-state publication.
- Each driver has deterministic overlap tests, fresh ownership/run checks, and live qualification; no immunity inferred from another driver's pass.
- Tests cover reused IDs, admission, rollback, cancellation/retry and genuine dependency loss.
- Cross-process ownership and remote cancellation completion are tracked separately.
Alternatives Considered
Continue isolated fixes: small changes but inconsistent guarantees. One cross-driver refactor: combines local coordination with distributed ownership design and delays the proven Podman fix.
Implementation and Test References
- Podman: gate and guarded inspection, lifecycle/admission and overlap tests, concurrent barrier fixture, documented limits. Start with
restart_and_reconciliation_do_not_stop_the_new_workload; companion tests cover cancellation/failure/retry, delayed events and admission. Runcargo test -p openshell-driver-podman. - Kubernetes #4078: gate and scripted overlap tests.
- Portable assertions: sandbox-lifecycle scenario and CLI test entrypoint. This checks normal stop/start, Ready, exec and workspace persistence; it does not force a race. Add driver-specific startup barriers/reconciliation triggers with common assertions. Stress loops alone are insufficient.
Repeatable Live Qualification
Use the tmachine workflow: nix run .#tmachine -- setup fedora-podman-rootless, build current Linux artifacts with nix run .#build-artifacts-binaries, then nix run .#tmachine -- test fedora-podman-rootless none shell. Run a rootless Podman gateway with current sandbox/supervisor binaries and a workload with matching unprivileged UID/GID. Create and stop a Ready sandbox; collect labeled Podman events and both container states during restart.
In a disposable test build, pause three seconds after workload start, before supervisor start. Compare with only the start gate bypassed versus retained; remove instrumentation afterward. On Fedora 44 / Podman 5.8.1 / SELinux Enforcing, bypassing stopped the workload after 254 ms and returned ContainerExited; retaining reached Ready and successful exec. Kill the supervisor afterward and verify workload containment. Live gateway recovery after that forced loss was rejected by phase preconditions; driver-level retry tests do not establish gateway recovery.
For each other driver, place a test barrier at its equivalent partial-start/cleanup boundary, delay old reconciliation across restart, verify no premature destructive action and eventual Ready/exec, then inject genuine dependency loss. Audit every destructive actor and repeat on that driver's live runtime.
Activity
elezar commentedon Oct 2, 2026
Additional suspected Docker instance for the cross-driver lifecycle audit:
on_failure_policy_replaces_runtime_and_preserves_workspace(tests/sandbox_lifecycle.rs:1316). The test deliberately exits the initial workload with code 17, then expects theon-failurereplacement to reach Ready with restart count 1 and preserve the workspace.Phase: Error,Restart count: 1, no last exit code, andReady: False (ContainerExited). The lifecycle suite finished with 17 passed and 1 failed.Gateway timeline for sandbox
2fae61ab-b92a-4e2e-b103-21c705b5acd7(e2e-25213-7), UTC on 2026-10-02:This resembles the restart/reconciliation race described in #3776 and addressed for Podman by #4117, but the mechanism is not confirmed for Docker. The available job logs do not distinguish a stale published exit snapshot from actual premature workload containment. The replacement supervisor connection alone does not establish that the workload stayed healthy.
Suggested audit target: Docker lifecycle/watch/admission overlap during partial restart, including both protection against destructive reconciliation and filtering of stale state publication. A deterministic overlap reproduction with container-state and containment evidence is still needed before treating this as the same root cause or claiming a Docker fix.
aetherxeg-source commentedon Oct 2, 2026
The cross-driver contract should preserve:
"resource identity != lifecycle generation"
A driver can correctly resolve the logical sandbox and still act on the wrong incarnation of that sandbox.
I would standardize lifecycle evidence around:
"{sandbox_identity, execution_generation, operation_generation, authority}"
and require destructive actions to carry an expected execution generation.
A cross-driver conformance test could:
Critically, this should be tested independently for observation filtering and destructive remote effects, because filtering a stale observation after a driver has already stopped B is too late.
The core invariant is:
"stale generation has zero destructive authority over a newer runtime"
elezar commentedon Oct 6, 2026
Additional context from reviewing #4235 at
0cfcd1e193e8c486114c9e7d1ee75ea75a0cf2fa(fixes #4129):Docker evidence now distinguishes stale publication from containment
This updates the earlier suspected Docker instance in this issue. #4129 documents a controlled reproduction that delays an authentic old-runtime exit snapshot until after replacement readiness: the gateway regresses
Ready -> Error / ContainerExitedwhile both replacement containers remain running. That establishes stale state publication as a separate failure mechanism; it does not establish that the original CI failure had only this cause, since #4235 also removes independent test timing assumptions.#4235 extends gateway live-state revalidation from
StartingtoReadyobservations reportingContainerExited,ContainerStopped, orContainerRuntimeRestart. It waits for the local sandbox lifecycle gate and rereads driver/store state. Its deterministic coverage checks both stale and genuine terminal observations for all three reasons. Driver-side filtering alone cannot retract a snapshot already queued at the gateway. Gateway revalidation.Concrete remaining audit targets
ProcessExited,ProcessPollFailed, andComputeStoppedbypass the newReadyrevalidation predicate. The gateway classifiesReady=False / ProcessExitedas terminal, so a delayed old-runtime snapshot can still fail a ready replacement at the state-application boundary. This is a source-level gap, not a live-confirmed reproduction. Condition predicate, VM monitor.Suspended=True / PodTerminateddo not enter the newReadyrevalidation path. Existing observed-generation filtering and bootstrap completion coordination are useful, but do not prove immunity to a converted old snapshot already queued downstream. Force that delivery boundary separately from controller/driver reconciliation. Bootstrap completion coordination.Suggested deterministic coverage
For each driver, hold an observation or cleanup action from runtime A, replace A with B under the same sandbox identity, then release A's action after B reaches
Ready. Independently verify:Ready, exec succeeds, and workspace markers survive delayed old snapshots/deletion observations.The contract should cover every observation/cleanup path rather than infer lifecycle semantics solely from container-specific reason strings. Runtime-generation ownership and cross-process/HA/cancellation fencing remain separate work from an instance-local gate.
No blocker identified in #4235 for its demonstrated bug, and it does not block this follow-up work: it changes no driver API or general lifecycle architecture. Source review and existing CI results support the observations above; this review did not independently repeat live qualification in other drivers.
aetherxeg-source commentedon Oct 7, 2026
This decomposition is useful. I think it makes one remaining contract boundary especially important:
stale-publication fencing != stale-mutation fencing
The gateway revalidation in #4235 can reject or repair a stale terminal observation before it becomes authoritative state, but it cannot retroactively undo a driver-side cleanup or resource mutation that was already committed by runtime A after runtime B became authoritative.
For the VM path in particular, I would make each post-observation side effect independently generation-bound:
"observed runtime generation"
"-> registry mutation"
"-> exit-marker write"
"-> extension cleanup"
"-> GPU/resource release"
A deterministic adversarial test can pause runtime A after exit detection but immediately before each one of those effect boundaries, publish and ready B, then release A.
The required result should be:
"A may complete bookkeeping about A, but A has zero authority to mutate B-owned state or resources."
That also gives a cleaner cross-driver conformance rule than enumerating terminal reason strings: every delayed observation and every delayed destructive effect must prove ownership of the runtime generation it is about to modify.
I would keep publication fencing and effect fencing as separate test families, because passing the former does not imply the latter.
github-actions commentedon Oct 22, 2026
This issue has had no activity for 14 days and is now marked stale. It may be closed in 14 days if there is no further activity. Comment or remove the state:stale label to keep it open.