Repository navigation
sandbox-store: move five api writes of sandboxes into the store - #1347
Merged
Merged
Conversation
|
Thanks for opening this pull request! A SPACE maintainer will take a look. CONTRIBUTING.md explains how we review outside contributions. |
ac-pplx
force-pushed
the
ac/lifecycle-move-api-rest
branch
from
November 2, 2026 23:25
e6d14ad to
2729fad
Compare
This was referenced Nov 3, 2026
Merged
Merged
ac-pplx
marked this pull request as ready for review
November 3, 2026 03:51
ac-pplx
marked this pull request as draft
November 3, 2026 03:52
ac-pplx
force-pushed
the
ac/lifecycle-move-api-rest
branch
from
November 3, 2026 06:00
2729fad to
5c487bd
Compare
ac-pplx
marked this pull request as ready for review
November 3, 2026 06:29
kumarpallav-pplx
requested review from
kumarpallav-pplx and
nikhilbirmiwal-pplx
November 3, 2026 06:30
nikhilbirmiwal-pplx
approved these changes
Nov 3, 2026
Five UPDATE sandboxes statements were still written inline in the api, against the store crate's rule that every write of the row belongs to pplx-space-sandbox-store. They move into three new store modules, byte-for-byte except for their binds: - intent::set_network_policy_override and intent::set_snapshots_enabled_desired, from SandboxController::set_network_policy and set_snapshots_enabled_desired (controllers/sandbox/updates.rs). - action_stamp::stamp_action_lifecycle and action_stamp::stamp_started_after_start, from maybe_update_sandbox_lifecycle and maybe_mark_started_after_start (controllers/sandbox/responses.rs). These are the lifecycle dispatcher's post-action stamps. Their guard refuses only terminal rows. Nothing else is re-checked, so a stamp overwrites any other stored lifecycle, including `deleting`, `stopping`, `suspending` and `suspended`, which another intent may have written while the substrate call was in flight. The move keeps that behaviour as it is; the module docs say so, and a later behaviour change fixes it. - artifact_marker::set_current_local_artifact, from persist_optional_current_local_artifact (controllers/substrate/mod.rs). The api functions stay where they are, with the same signatures, error mapping and logging. The FOR UPDATE row lock, the deleting-snapshot check, the transactions and the reconcile enqueues also stay at the call sites. The store functions run on the executor the caller passes and return sqlx::Error, so caller error handling is unchanged. The artifact marker takes the artifact as a (LocalArtifactId, ArtifactKind) pair, because PublishedLocalArtifact is an api type; it binds the same UUID and string values as before. The sql! locators of the moved statements now name the store modules, which changes how they appear in pg_stat_activity and query monitoring. pattern_ratchet.sandbox_writes drops to 1 for responses.rs (the post-wake update is left for another change), the updates.rs entry is removed (the create path's change below this one moved its rollback deletes), the substrate/mod.rs entry is removed, and the substrate/mod.rs file_size cap is lowered to 2071.
ac-pplx
force-pushed
the
ac/lifecycle-move-api-rest
branch
from
November 3, 2026 21:24
5c487bd to
54c3ba5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Five
UPDATE sandboxesstatements were still inline in the api, though every write of the row belongs topplx-space-sandbox-store. They move into three new store modules. The SQL text is unchanged except for binds, and each function is named for its transition, with a doc comment on the fact it writes:api::controllers::sandbox::updatesset_network_policy→sandbox_store::intent::set_network_policy_override(intent; generation check is the only guard)updatesset_snapshots_enabled_desired→intent::set_snapshots_enabled_desired(intent; id only)responsesmaybe_update_sandbox_lifecycle→action_stamp::stamp_action_lifecycle(operation step)responsesmaybe_update_sandbox_state→action_stamp::stamp_action_state(operation step)substratepersist_optional_current_local_artifact→artifact_marker::set_current_local_artifact(observed projection; id only, relies on the caller's row lock)action_stampholds the lifecycle dispatcher's post-action stamps. Their guard refuses only terminal rows (failed,quarantined,deleted,cleanup_required), so a stamp can overwrite an in-flight lifecycle (deleting,stopping,suspending,suspended). This move keeps that behaviour as it is. The module docs state it, the existing characterization table pins it as a known defect, and a later behaviour change fixes it.The api functions keep their signatures, error mapping and logging. The
FOR UPDATElock, the deleting-snapshot check, the transactions and the reconcile enqueues stay at the call sites. Store functions take the caller's executor and returnsqlx::Error. The marker takes(LocalArtifactId, ArtifactKind)instead of the api'sPublishedLocalArtifactand binds the same values.sql!locators change. The moved statements' locators now name the store modules, and that path is what appears inpg_stat_activityand query monitoring.architecture-baseline.toml:sandbox_writesgoes to 2 forupdates.rsand 1 forresponses.rs, and thesubstrate/mod.rsentry is removed. Thesubstrate/mod.rsfile_sizecap goes from 2089 to 2064.Not moved
crates/libs/placement/machine/src/postgres.rs:completion_sqland two claim files inpostgres/sql/(user_restore_claim.sql,evacuation_restore_claim.sql) writesandboxes, and the third,claim_miss_snapshot.sql, only reads it. None of them has a production caller. They are left as they are for a separate decision; this PR does not touchcrates/libs/placement.updates.rsand the post-wake update inresponses.rsbelong to other moves.Open PRs that will need to rebase
set_network_policy(moving it tocontrollers/sandbox/network_policy.rsand a storenetwork_policymodule). Its statement is nowupdates.rs→sandbox_store::intent::set_network_policy_override.maybe_update_sandbox_statewithmaybe_mark_started_after_startand changes its SQL. The statement is nowresponses.rs→sandbox_store::action_stamp::stamp_action_state.updates.rs: api: move the create path's sandboxes writes into sandbox-store #1329, api: permalaunch space-api-timeout-lock-bypass #1178, [api] rate-limit activations per client key, atomically with the cluster bucket #1131, [api] record the activating client and label sandbox pods with it #1068, [space] fence every placement writer against the provider-terminate path #559 and [space-api] preserve CommandIdle template default across ECW lifecycle endpoints #538.responses.rs: [api] record the activating client and label sandbox pods with it #1068 and [space] fence every placement writer against the provider-terminate path #559.substrate/mod.rs: [cluster-controller] record pod-create intent so the pod-less drain give-up can release a stalled recovery #1323, [api] trigger a stopped sandbox's runtime upgrade from a test hook, and smoke it on Kind #1322, api: cap each client's live sandboxes, counted from the pod reflector and a Redis pending set #1172, [api] rate-limit activations per client key, atomically with the cluster bucket #1131, [api] record the activating client and label sandbox pods with it #1068, api: refuse a cold create with a 503 when every eligible node's network slots are held #1061, [space] fence every placement writer against the provider-terminate path #559, Bound reconnect recovery on unpublished local suspends #350 and api: decide admission pool claims for the create's owner; collect refused claims promptly #168.sandbox/store/src/lib.rsandarchitecture-baseline.toml.Validation
At this head (rebased on
main):cargo clippy --locked -p pplx-space-sandbox-store -p pplx-space-api -p pplx-space-placement --all-targets --all-features -- -D warnings: clean.cargo arch --base-baseline <main baseline> --base-architecture <main ARCHITECTURE.toml>: 0 required findings.cargo nextest run --locked -p pplx-space-sandbox-store -p pplx-space-api -p pplx-space-placement --all-features, with the shared Postgres and Redis setup scripts: 2232 passed, 1 skipped (an#[ignore]local benchmark), no dependency skips. The PR edits no test files.'quarantined'from thestamp_action_lifecycleguard failspost_action_stamps_write_every_stored_lifecycle_but_the_terminal_ones. Reverted.infra/space/kind/scripts/up.sh, 7m12s), thenscripts/tests/smoke-kind-typed.sh all(51m48s):ecw.cold_create_environmentandecw.warm_pool_environmentneededSPACE_SMOKE_KIND_COLD_RECEIPTS. Both passed when rerun with it set.local.restarted_btrfs_send_is_rejected_and_retriedneeds the otel telemetry mode and was not run.Severity and risk
Severity: does not apply (a refactor with no behaviour change).
Risk: low. Each statement, its binds and its transaction are unchanged, so old and new pods issue the same SQL during a rollout. Only the
sql!locator text differs. Rollback: revert this commit.AI assistance: an AI agent (Claude Code) wrote the code change and this description.
After the wave-2 restack
This PR is one layer of a 10-PR stack, restacked onto main
c5fdf0c7(which includes #826) and then rebased mechanically onto42a99002, which changed only baseline context lines, in order #1329 → #1336 → #1338 → #1339 → #1342 → #1347 → #1333 → #1335 → #1328 → #1332. The validation earlier in this description ran at pre-restack heads; this layer's head is54c3ba53.maybe_update_sandbox_stateasmaybe_mark_started_after_start, which also drops adiskartifact pin. This layer moves [cluster-controller] re-place an evacuated stopped sandbox as stopped on another node, materialized by the shim without a VM #826's statement asaction_stamp::stamp_started_after_start(executor, id), byte-identical to main's, andmaybe_mark_started_after_startkeeps [cluster-controller] re-place an evacuated stopped sandbox as stopped on another node, materialized by the shim without a VM #826's doc and log and calls it. For every file [cluster-controller] re-place an evacuated stopped sandbox as stopped on another node, materialized by the shim without a VM #826 and this stack share, each SQL line [cluster-controller] re-place an evacuated stopped sandbox as stopped on another node, materialized by the shim without a VM #826 added was checked to be present verbatim at the top head, in place or in the store module it moved to.cargo fmt --all -- --check,cargo check --workspace --all-targets --all-featuresandcargo arch(0 required findings, with main's layers and table-owner rules), and the ratchet and caps this layer changes are recounted exactly.e90aa284before that rebase; nextest passed 4370 tests again at77fbae1e):cargo clippy --workspace --locked --all-targets --all-features -- -D warningsis clean, andSPACE_CI_REQUIRE_TEST_DEPS=1 cargo nextest run --all-featurespassed 4355 tests across the store, api, internal-api server, cluster-controller server, metrics, db-test-fixtures, common-core, common-svc and portability crates, [cluster-controller] re-place an evacuated stopped sandbox as stopped on another node, materialized by the shim without a VM #826's suites included.scripts/tests/smoke-kind-typed.sh allgave ECW 91/91 and native 42/42, including [cluster-controller] re-place an evacuated stopped sandbox as stopped on another node, materialized by the shim without a VM #826'snative.forced_drain_re_places_stopped_sandboxand its re-drain and restarted variants. The Direct substrate lane'sallgave ECW 51/51 and native 27/27. No flakes and no reruns.+/-lines and its SQL string literals match its reviewed head (range-diff plus a literal multiset compare).