Repository navigation
sbx-mgr: remove dead and test-only state writers - #332
Conversation
These writers act on sbx-mgr's node-local SQLite `sandboxes` table (the `state` column, `SandboxState`), not the control-plane Postgres `public.sandboxes` table (`lifecycle`). Writers with no production caller now compile only in tests. Each one is still used by tests, either as a fixture or to reach the shared writer behind it, so they move under `#[cfg(test)]` rather than being deleted: - `mark_quarantined` - `mark_starting_quarantined` and `mark_restoring_quarantined` - `rollback_starting` and `rollback_restoring` - `mark_stopping_superseding_activation` and `mark_deleting_superseding_activation`, together with their private helper `update_state_superseding_activation` - `clear_cleanup_intent` Two branches that no input can reach are removed: - `request_cleanup_episode` kept the restore columns when the episode carried a quarantine-recovery fence. Such an episode is only ever inserted already requested (`adopt_quarantine_cleanup_episode`). No statement returns an episode to `armed`, and this function returns before the write for a requested episode. The arm could therefore only keep NULL columns or fail the schema's restore-column CHECK. The columns are now always cleared. - Cleanup admission's fallback to `update_state` for a target other than `stopping` or `deleting`. Every caller of `admit_lifecycle_cleanup_*` passes one of those two states. Production behavior is unchanged: no reachable path writes different rows, returns different results or logs differently. Workspace callers outside the crate use only `run` and constants, so no external crate loses an item.
Review — 2 findingsLow
I traced the two unreachability proofs myself and both hold. Verified against |
the-gigi-pplx
left a comment
There was a problem hiding this comment.
Approved. 2 non-blocking findings in the review comment above.
What this does
This removes production-dead state writers and two unreachable branches from sbx-mgr's node-local SQLite
sandboxestable (state/SandboxState). It does not touch the control-plane Postgrespublic.sandboxestable (lifecycle).The sbx-mgr state-write inventory (
reports/state-write-helper-plan.mdon branchac/state-write-helper-plan, finding 8) lists eight dead or test-only writers and two unreachable branches. Each one was re-checked against currentmain(2d9e1d3). No reachable production path changes.Self-assessed severity: none (cleanup). The change is behavior-preserving, removes about 30 lines of dead or unreachable code and makes nine items test-only.
Writers: no production caller, now
#[cfg(test)]Every writer below is still used by tests, either as a fixture (seeding a quarantined, stopping or deleting row) or as a test entry point into a shared writer.
mark_starting_quarantinedandmark_restoring_quarantinedreachmark_quarantined_from_state.mark_quarantinedcallsmark_quarantined_innerdirectly with no expected state.rollback_startingandrollback_restoringreachrollback_activation_state. The superseding writers reachupdate_state_superseding_activation_in_transaction. Deleting them would mean rewriting those fixtures in test files that open PRs edit, and widening private helpers' visibility. So they move under#[cfg(test)]and their bodies are unchanged.mainmark_quarantineddb/quarantine.rsmark_starting_quarantineddb/state_transitions.rsmark_restoring_quarantineddb/state_transitions.rsrollback_startingdb/state_transitions.rsrollback_restoringdb/state_transitions.rsmark_stopping_superseding_activationdb/state_transitions.rsmark_deleting_superseding_activationdb/state_transitions.rsclear_cleanup_intent(side-column writer)db/cleanup_episodes.rsupdate_state_superseding_activation(private helper of the two superseding writers; mechanical fallout)db/state_transitions.rsCall-site inventory, before and after. I grepped the whole repository for each name: every hit outside its definition is in a
tests.rsortests/module. The only crate that depends onpplx-space-sbx-mgriscrates/apps/sbx/mgr/svc, and it importsrun,SERVICE_NAME,SOCKET_PATHandSbxMgrRunConfigonly.I also ran a throwaway compiler probe: make
dbcrate-private (pub(crate) mod db) andcargo check -p pplx-space-sbx-mgr --lib(non-test). Onmain, rustc reports all nine items above as "never used". With this change those nine warnings disappear because the items no longer exist outside tests, and no new item becomes unused. Production code no longer reaches any of them.Unreachable branch 1:
request_cleanup_episodekeeping the restore columns (inventory F-d)request_cleanup_episodekeptrestore_origin,restore_phaseandrestore_storage_uriwhen the episode carried a quarantine-recovery fence (episode.quarantine_recovery.is_some()). Now it always clears them.Proof the branch is unreachable. The UPDATE runs only for an episode whose
requested_atis NULL, which meansphase = 'armed'(schemaCHECK). The function returns earlier for a requested episode. For the branch to run, an armed episode would need recovery fence columns:recovery_local_artifact_idandrecovery_execution_generationare written by exactly one production statement. That is the INSERT inadopt_quarantine_cleanup_episode(db/quarantine_recovery.rs), and it insertsphase = 'requested'withrequested_atset.insert_cleanup_episode, the only other INSERT, never sets fence columns. No UPDATE writes them.armed. Every UPDATE ofsandbox_cleanup_episodes(charge_prepared_clone_disposal_attempt,rearm_prepared_clone_disposal,bind_prepared_clone_root,request_cleanup_episode,record_cleanup_episode_failure, the twooperation_activation_attempt_idwriters) either setsphase = 'requested'or leavesphasealone. None setsphase = 'armed'orrequested_at = NULL. Nothing outsidecrates/libs/sbx/mgr/server/srcwrites the table, and the baseline is the only migration.CHECKties non-NULL restore columns tostate = 'restoring'. Keeping them while writingcleanup_requiredeither keeps NULLs, which is the same as clearing them, or fails the CHECK.Probes (throwaway, not committed):
I made the branch panic on
main(assert!(!preserve_restore_operation)) and ran the full sbx-mgr suite: 1568/1568 passed, so no test reaches it.A throwaway test drove the only producer (
adopt_quarantine_cleanup_episode) and then calledrequest_cleanup_episode. It returnedAlreadyRequestedand the row was unchanged, onmainand with this change.The same test then forced two rows with raw SQL, which no production statement can do:
phase = 'armed',requested_at = NULL;state = 'restoring',restore_origin = 'local',restore_phase = 'activation'.That made the
mainpanic probe fire, confirming the probe was live. Without the panic,mainreturned the restore-columnCHECK constraint failederror. With this change it returnsRequestedwith the restore columns cleared.Forcing only the episode back to
armed, with the row left incleanup_requiredand NULL restore columns, gives the same result on both: one row changed, NULL columns. The arm can matter only when restore columns are set, so any behavioral difference is limited to input no production path can produce.Unreachable branch 2: cleanup admission's non-stop/delete
elseadmit_lifecycle_cleanup_fencedcalledupdate_statewhentarget_statewas neitherStoppingnorDeleting. Now it always callsupdate_state_superseding_activation_in_transaction, whose owndebug_assert!already requires one of those two states.Proof the branch is unreachable.
target_statecomes only fromCleanupAdmissionSpec. All four constructors inservice/cleanup.rsproduceStoppingorDeleting:admit_auto_lifecycle_delete→Deleting.admit_selected_cleanup→StoppingorDeleting.admit_lifecycle_cleanup_from_observed_statetakesclaim.target_state. Its only production caller (grpc_spaced_process_snapshots.rs) passesStopping, and so does its direct test caller (service/tests.rs~10880).admit_lifecycle_cleanup_for_observed_executiontakes a parameter. Its only production caller (grpc_lifecycle/kill.rs) passesStopping. Its direct test callers (service/tests.rs~10636, 10676, 10697, 10807 and 10921) all passStopping. The test-onlyadmit_lifecycle_cleanuphelper (service/tests.rs~57) also calls it, and the helper's nine callers passStoppingorDeleting.Probe. I made the
elsebranchunreachable!()onmainand ran the full sbx-mgr suite: 1568/1568 passed.With whitespace ignored (
git diff -w), this hunk is pure deletion: theif matches!(…) {header and theelse { … }body.Considered and left alone
Other test-only items the compiler probe found. These are outside this inventory and left for a follow-up:
begin_activation,complete_cleanup_episode,quarantine_cleanup_episode,restore_adopt_orphan_or_insert_sandbox(indb/sandboxes.rs, which sbx-mgr: keep quarantined rows through inventory and bound recovery that can never succeed #270 edits),claim_teardown_suspend_operation,delete_sandbox. Also the readerscount_pool_sandboxes,current_quarantine_reason_wire_value,list_sandbox_id_statesandlist_by_state.Production-unreachable arms of
mark_quarantined_inner(db/quarantine.rs~805–1001). Withmark_quarantinedtest-only, every production path passes a literal, non-quarantined, non-failed expected state:mark_quarantined_best_effort;service/recovery.rs;mark_suspending_quarantined.So
starts_new_episodeis always true in production. The following arms are reachable only from tests:expected_state: Nonepath;UnknownCurrentState;Transitionerror for afailedrow;!starts_new_episode, ~864–1001).These tests pin them:
quarantine_recovery_reason_change_resets_episode_retry_state;quarantine_recovery_source_change_resets_episode_retry_state;quarantine_recovery_reason_change_preserves_a_live_claim;mark_quarantined_reports_unknown_current_state;mark_quarantined_persists_current_state_and_history;malformed_expiry_cannot_clear_ownership_when_quarantine_is_reclassified(db/timestamps/tests.rs~238).A follow-up must decide whether to delete the reconfirmation branch or name the production path meant to reconfirm. It is left here because sbx-mgr: keep quarantined rows through inventory and bound recovery that can never succeed #270 edits
db/tests.rs, where most of those tests live.rollback_activation_state(db/state_transitions.rs~1216–1234) has one production caller,restore_resident_resume_state, which passesexpected = Resumingafter checking forStartedorPauseditself. So itsrestore_* = CASE WHEN ?4 = 'restoring'arms are dead in the same way as F-d, and itsQuarantined | Failedguard duplicates the caller's check. Follow-up: retargetactivation_entry_rollbacks_and_quarantines_do_not_overwrite_newer_stateandgeneric_lifecycle_writers_reject_failed_targetsto the_episodewriters, then replace the CASE with= NULLor delete the wrappers.Typing cleanup admission's target state. With the
elsegone, the rule that the target isStoppingorDeletingis enforced only by thedebug_assert!inupdate_state_superseding_activation_in_transaction(~310). A two-variant enum forCleanupAdmissionSpec.target_state,ObservedCleanupClaim.target_stateand theadmit_lifecycle_cleanup_for_observed_executionparameter would make the type system enforce it.grpc_lifecycle/kill.rsandgrpc_spaced_process_snapshots.rs(which sbx-mgr: inject the node-manager client transport and template-manager resolver #296 edits), and six direct call sites plus the test helper inservice/tests.rs(where sbx-mgr: build every test service from explicit test options #322 has hunks around ~10754 and ~10875). Changing the helper's signature would also touch its nine callers in three test files.Deferred for sbx-mgr: keep quarantined rows through inventory and bound recovery that can never succeed #270. None of the inventory's ten items is in sbx-mgr: keep quarantined rows through inventory and bound recovery that can never succeed #270's hunks, so nothing is deferred.
No item turned out reachable.
Host safety
This adds no tests and changes no test bodies. The throwaway probes ran in a scratch copy, inside the CI image, as an unprivileged uid with
--cap-drop=ALLand no host network. They used task-owned Postgres and Redis containers and touched no fixed host path, socket, firewall, netns or foreign process. The one privileged sbx-mgr test (transfer_directory::tests::distinct_service_uids_can_exchange_downloads) ran as root in a--privilegedcontainer, as CI'stests-privilegedstep does.Overlap with open PRs
ac/fix-sbxmgr-inventory-keeps-quarantined-rows, headcc062ce797): no shared files. Its diff applies cleanly on this branch.ac/sbxmgr-seam-node-manager-transport, rebased, head5547397a26): both editservice/cleanup.rs, in disjoint hunks (sbx-mgr: inject the node-manager client transport and template-manager resolver #296: lines 563 and 2204–2788; this PR: 1226–1300). Its diff applies cleanly on this branch.ac/sbxmgr-explicit-test-service, stacked on sbx-mgr: inject the node-manager client transport and template-manager resolver #296, head2b8049a7d0): its diff shows the same fiveservice/cleanup.rshunks as sbx-mgr: inject the node-manager client transport and template-manager resolver #296, carried from the stack, and is otherwise disjoint from this PR. It applies cleanly on this branch. sbx-mgr: inject the template-manager resolver #299 has merged into sbx-mgr: inject the node-manager client transport and template-manager resolver #296's branch.ac/sbxmgr-deferred-tests-on-seams, stacked on sbx-mgr: build every test service from explicit test options #322, headf93fc01e3b): touches none of this PR's four files.Validation
All Rust commands ran in the repository's CI image (
ci/Dockerfile.ci, toolchain 1.98.0) against this branch's tree, as an unprivileged uid with--cap-drop=ALL. Postgres and Redis came fromci/scripts/ci-test-services.sh, restarted fresh for each run, withSPACE_CI_REQUIRE_TEST_DEPS=1.cargo fmt --all -- --checkcargo clippy --locked --workspace --all-targets --all-features -- -D warningscargo clippy --locked -p pplx-space-sbx-mgr --all-targets --all-features -- -D warningsci/scripts/run-rust-checks.sh fmt(env-contract check, Rust error policy, fmt)scripts/source_guards/env_test_hook_boundary.py,observability_vendor_boundary.pyscripts/check_no_inline_rust_test_modules.py,scripts/source_guards/thin_service_mains.pycrates/libs/observabilityandcrates/apps/vol/mgr/svc/src/main.rs, which this change does not touch, andthin_service_mains.pyprints identical output onmain2d9e1d3..HEADcargo nextest run --locked -p pplx-space-sbx-mgr --retries 0 -E 'not group(privileged)', three consecutive runs (after one earlier identical green run)SPACE-TEST-DEP-SKIPrecorded. sbx-mgr's only ignored test is the privileged one belowcargo nextest run --locked -p pplx-space-sbx-mgr --retries 0 --run-ignored all -E 'group(privileged)', as root in a--privilegedcontainer (CI'stests-privilegedshape), three runstransfer_directory::tests::distinct_service_uids_can_exchange_downloads)main: both removed branches made to panic, then the full sbx-mgr suitespace-localcluster (none existed beforehand), bring-up throughscripts/tests/smoke-kind-e2e.shwith Tilt andSPACE_KIND_TELEMETRY_MODE=otelcrates/andCargo.lockis byte-identical to this branch. All four sandbox-manager pods (two workers, two builders) runspace-sbxmgr-svcsha256bbae248b…, identical to the artifact build. The binary lacks the removed SQL literal (CASE WHEN ?6 THEN restore_origin) and contains an unchanged control literalokand 4 not applicable, nothing pending; still the same pod binary hashes after the runSKIP_BOOTSTRAP=1 scripts/tests/smoke-kind-typed.sh allwithSPACE_SMOKE_KIND_ADMIN_TOKEN_FILEandSPACE_SMOKE_KIND_COLD_RECEIPTSset as CI doesnative_contract.command_replay_credentialsis declared ignored in the scenario inventory and was withheld.ecw.cold_create_environmentandlocal.encrypted_resuspend_preserves_cowran and passedNo reruns were needed, so no failure classification applies.
AI disclosure: an AI coding agent (Claude) wrote this change, ran the probes and validation, and drafted this description.
🤖 Generated with Claude Code